Matrizes consecutivas
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
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 umint[])?
Sim.
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;
}