Matrizes consecutivas

Aug 26 2020

Estou curioso sobre uma frase no padrão C18:

Dois ponteiros comparam iguais se e somente se ambos são ponteiros nulos, ambos são ponteiros para o mesmo objeto (incluindo um ponteiro para um objeto e um subobjeto em seu início) ou função, ambos são ponteiros para um após o último elemento da mesma matriz objeto, ou um é um ponteiro para um além do final de um objeto de matriz e o outro é um ponteiro para o início de um objeto de matriz diferente que segue imediatamente o primeiro objeto de matriz no espaço de endereço. § 6.5.9 6

Por que o objeto que segue o array tem que ser necessariamente outro array? Não poderia ser simplesmente um objeto do mesmo tipo que o tipo base da matriz (como um intimediatamente após um int[])?

Não é à toa que tentei este código:

#include <stdio.h>

struct test { int arr[10]; int i; };

int main() {
    struct test t;
    int *p, *q;
    p = t.arr + 10;
    q = &t.i;
    if (p == q)
        printf("Equal pointers.");
    return 0;
}

E produz ponteiros iguais. Esse comportamento não é garantido, apenas uma coincidência definida pela implementação?

Respostas

24 chux-ReinstateMonica Aug 26 2020 at 17:41

OP: Por que o objeto que segue o array tem que ser necessariamente outro array?

Isso não. "... início de um array diferente ..." é uma simplificação. A próxima especificação é:

Para os propósitos desses operadores, um ponteiro para um objeto que não é um elemento de uma matriz se comporta da mesma forma que um ponteiro para o primeiro elemento de uma matriz de comprimento um com o tipo do objeto como seu tipo de elemento. C17dr § 6.5.9 7


OP: Não poderia ser simplesmente um objeto cujo tipo era o mesmo que o tipo base da matriz (como um intimediatamente após um int[])?

Sim.

8 th33lf Aug 26 2020 at 17:26

Em primeiro lugar, especificar array aqui não exclui/proibe um único objeto. Um único objeto na memória é indistinguível de um array de tamanho 1.

( Editar : Leia esta resposta para uma citação do padrão que afirma explicitamente isso ao se referir a ponteiros)

Em segundo lugar, a norma também tenta esclarecer a afirmação que você citou, com a seguinte nota de rodapé indicando cenários onde a regra se aplica:

Dois objetos podem ser adjacentes na memória porque são elementos adjacentes de uma matriz maior ou membros adjacentes de uma estrutura sem preenchimento entre eles, ou porque a implementação escolheu colocá-los assim, mesmo que não sejam relacionados.

Juntando tudo, o que o padrão tenta dizer aqui é que, em geral, dois ponteiros para objetos diferentes não devem ser comparados iguais. No entanto, uma vez que é legal apontar um além de um objeto array na memória, se houver um objeto diferente (array) naquele local, ainda é legal para tal ponteiro comparar igual a um ponteiro para o objeto adjacente. Agora, pode ou não haver um objeto válido neste local, por causa das opções de alinhamento e preenchimento, mas se houver um, é aceitável que esses ponteiros sejam comparados iguais.

No seu exemplo, se eu mudasse a matriz para um char, os ponteiros provavelmente seriam comparados desiguais porque o compilador escolheria alinhar o int para 4 bytes (na maioria das plataformas de 32 ou 64 bits), introduzindo assim o preenchimento. Esse comportamento ainda é legal de acordo com o padrão.

#include <stdio.h>

struct test { char arr[10]; int i; };

int main() {
    struct test t;
    int *p, *q;
    p = (int*)(t.arr + 10);
    q = &t.i;
    if(p == q)
      printf("Equal pointers.");
    else
      printf("Unequal pointers.");
    return 0;
}