Como é que o endereço de uma matriz é igual ao seu valor em C?

189

No bit a seguir, os valores e os endereços dos ponteiros diferem conforme o esperado.

Mas valores e endereços de matriz não!

Como isso pode ser?

Resultado

my_array = 0022FF00
&my_array = 0022FF00
pointer_to_array = 0022FF00
&pointer_to_array = 0022FEFC
#include <stdio.h>

int main()
{
  char my_array[100] = "some cool string";
  printf("my_array = %p\n", my_array);
  printf("&my_array = %p\n", &my_array);

  char *pointer_to_array = my_array;
  printf("pointer_to_array = %p\n", pointer_to_array);
  printf("&pointer_to_array = %p\n", &pointer_to_array);

  printf("Press ENTER to continue...\n");
  getchar();
  return 0;
}
Alexandre
fonte
Na FAQ do comp.lang.c: - [O que significa a `` equivalência de ponteiros e matrizes '' em C? ] ( c-faq.com/aryptr/aryptrequiv.html ) - [Como as referências de matriz decaem em ponteiros, se arr é uma matriz, qual é a diferença entre arr e & arr? ] ( c-faq.com/aryptr/aryvsadr.html ) Ou leia a seção inteira Arrays and Pointers .
Jamesdlin
3
Eu adicionei uma resposta no diagrama a essa pergunta há dois anos. O que sizeof(&array)retorna?
Grijesh Chauhan

Respostas:

212

O nome de uma matriz geralmente avaliada como o endereço do primeiro elemento da matriz, de modo que arraye &arraytem o mesmo valor (mas tipos diferentes, de modo que array+1e &array+1vai não ser igual, se a matriz é mais do que um elemento de comprimento).

Existem duas exceções: quando o nome da matriz é um operando sizeofou unário &(endereço de), o nome se refere ao próprio objeto da matriz. Assim, sizeof arrayfornece o tamanho em bytes de toda a matriz, não o tamanho de um ponteiro.

Para uma disposição como definida T array[size], que terá tipo T *. Quando / se você incrementa, você chega ao próximo elemento na matriz.

&arrayavalia para o mesmo endereço, mas, dada a mesma definição, cria um ponteiro do tipo T(*)[size]- ou seja, é um ponteiro para uma matriz, não para um único elemento. Se você incrementar esse ponteiro, ele adicionará o tamanho de toda a matriz, não o tamanho de um único elemento. Por exemplo, com código como este:

char array[16];
printf("%p\t%p", (void*)&array, (void*)(&array+1));

Podemos esperar que o segundo ponteiro seja 16 maior que o primeiro (porque é uma matriz de 16 caracteres). Como% p normalmente converte ponteiros em hexadecimal, pode parecer algo como:

0x12341000    0x12341010
Jerry Coffin
fonte
3
@ Alexandre: &arrayé um ponteiro para o primeiro elemento da matriz, onde, como arrayse refere a toda a matriz. A diferença fundamental também pode ser observada comparando-se sizeof(array)com sizeof(&array). Observe, no entanto, que se você passa arraycomo argumento para uma função, apenas &arrayé de fato passado. Você não pode passar uma matriz por valor, a menos que seja encapsulada dentro de a struct.
Clifford
14
@Clifford: Se você passar o array para uma função, ele decai para um ponteiro para o primeiro elemento ser efetivamente &array[0]passado, não o &arrayque seria um ponteiro para o array. Pode ser uma escolha difícil, mas acho importante deixar claro; compiladores irá avisar se a função tem um protótipo que corresponde ao tipo do ponteiro passado.
CB Bailey
2
@Jerry Coffin Por exemplo int * p = & a, se eu quiser o endereço de memória do ponteiro int p, posso fazer & p. Como o & array converte no endereço de todo o array (que começa no endereço do primeiro elemento). Então, como encontro o endereço de memória do ponteiro da matriz (que armazena o endereço do primeiro elemento na matriz)? Deve estar em algum lugar na memória, certo?
John Lee
2
@ JohnLee: Não, não precisa haver um ponteiro para a matriz em nenhum lugar da memória. Se você criar um ponteiro, você pode, em seguida, tomar o seu endereço: int *p = array; int **pp = &p;.
Jerry Coffin
3
@Clifford o primeiro comentário está errado, por que ainda mantê-lo? Acho que isso pode levar a mal-entendidos para quem não lê a resposta a seguir (@Charles).
Rick
30

Isso ocorre porque o nome da matriz ( my_array) é diferente de um ponteiro para a matriz. É um alias para o endereço de uma matriz e seu endereço é definido como o endereço da própria matriz.

O ponteiro é uma variável C normal na pilha, no entanto. Assim, você pode pegar o endereço e obter um valor diferente do endereço que ele contém.

Eu escrevi sobre este tópico aqui - por favor, dê uma olhada.

Eli Bendersky
fonte
& My_array não deveria ser uma operação inválida, pois o valor de my_array não está na pilha, apenas my_array [0 ... length] está? Em seguida, ele faria tudo faz sentido ...
Alexandre
@ Alexandre: Não sei por que isso é permitido, na verdade.
Eli Bendersky
Você pode usar o endereço de qualquer variável (se não estiver marcada register), independentemente da duração do armazenamento: estática, dinâmica ou automática.
CB Bailey
my_arrayem si está na pilha, porque my_array é toda a matriz.
caf
3
my_array, quando não é o assunto dos operadores &ou sizeof, é avaliado como um ponteiro para seu primeiro elemento (ou seja. &my_array[0]) - mas my_arrayele próprio não é esse ponteiro ( my_arrayainda é o array). Esse ponteiro é apenas um valor efêmero (por exemplo int a;, dado , é exatamente como a + 1) - conceitualmente, pelo menos, é "calculado conforme necessário". O verdadeiro "valor" de my_arrayé o conteúdo de toda a matriz - é apenas que fixar esse valor em C é como tentar captar neblina em uma jarra.
caf
27

Em C, quando você usa o nome de uma matriz em uma expressão (incluindo a passagem para uma função), a menos que seja o operando do operador address-of ( &) ou do sizeofoperador, ele decai para um ponteiro para o primeiro elemento.

Ou seja, na maioria dos contextos arrayé equivalente &array[0]em tipo e valor.

No seu exemplo, my_arraytem um tipo char[100]que decai para a char*quando você o passa para printf.

&my_arraytem tipo char (*)[100](ponteiro para uma matriz de 100 char). Como é o operando &, esse é um dos casos que my_arraynão decai imediatamente para um ponteiro para seu primeiro elemento.

O ponteiro para a matriz tem o mesmo valor de endereço que um ponteiro para o primeiro elemento da matriz como um objeto de matriz é apenas uma sequência contígua de seus elementos, mas um ponteiro para uma matriz tem um tipo diferente de ponteiro para um elemento de essa matriz. Isso é importante quando você faz aritmética de ponteiro nos dois tipos de ponteiro.

pointer_to_arraytem type char *- inicializado para apontar para o primeiro elemento da matriz, como é o que my_arraydecai na expressão do inicializador - e &pointer_to_array possui type char **(ponteiro para um ponteiro para a char).

Destes: my_array(após decaimento para char*), &my_arraye pointer_to_arraytodos apontam diretamente para a matriz ou o primeiro elemento da matriz e, portanto, têm o mesmo valor de endereço.

CB Bailey
fonte
3

A razão pela qual my_arraye o &my_arrayresultado no mesmo endereço pode ser facilmente entendida quando você olha para o layout de memória de uma matriz.

Digamos que você tenha uma matriz de 10 caracteres (em vez dos 100 no seu código).

char my_array[10];

Memória para se my_arrayparece com:

+---+---+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+---+
^
|
Address of my_array.

Em C / C ++, uma matriz decai para o ponteiro para o primeiro elemento em uma expressão como

printf("my_array = %p\n", my_array);

Se você examinar onde está o primeiro elemento da matriz, verá que seu endereço é o mesmo que o endereço da matriz:

my_array[0]
|
v
+---+---+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+---+
^
|
Address of my_array[0].
R Sahu
fonte
3

Na linguagem de programação B, que foi a predecessora imediata de C, ponteiros e números inteiros eram livremente intercambiáveis. O sistema se comportaria como se toda a memória fosse uma matriz gigante. Cada nome de variável tinha um endereço global ou relativo à pilha associado a ele, para cada nome de variável as únicas coisas que o compilador tinha que acompanhar era se era uma variável global ou local e seu endereço relativo ao primeiro local ou global. variável.

Dada uma declaração global como i;[não havia necessidade de especificar um tipo, uma vez que tudo foi um inteiro / ponteiro] seria processado pelo compilador como: address_of_i = next_global++; memory[address_of_i] = 0;e uma declaração como i++seria processado como: memory[address_of_i] = memory[address_of_i]+1;.

Uma declaração como arr[10];seria processada como address_of_arr = next_global; memory[next_global] = next_global; next_global += 10;. Observe que, assim que a declaração foi processada, o compilador pode esquecer imediatamente de arrser uma matriz . Uma declaração como arr[i]=6;seria processada como memory[memory[address_of_a] + memory[address_of_i]] = 6;. O compilador não se importaria se arrrepresentasse uma matriz e ium número inteiro ou vice-versa. Na verdade, não se importaria se eles fossem matrizes ou dois números inteiros; seria perfeitamente feliz em gerar o código conforme descrito, sem considerar se o comportamento resultante provavelmente seria útil.

Um dos objetivos da linguagem de programação C era ser amplamente compatível com B. Em B, o nome de uma matriz [chamada de "vetor" na terminologia de B] identificava uma variável segurando um ponteiro que foi inicialmente designado para apontar para para o primeiro elemento de uma alocação do tamanho especificado, portanto, se esse nome aparecesse na lista de argumentos de uma função, a função receberia um ponteiro para o vetor. Embora C tenha adicionado tipos de matrizes "reais", cujo nome estava rigidamente associado ao endereço da alocação, em vez de uma variável de ponteiro que apontaria inicialmente para a alocação, tendo as matrizes decompostas em códigos feitos por ponteiros que declararam que uma matriz do tipo C se comporta de maneira idêntica para o código B que declarou um vetor e nunca modificou a variável que contém seu endereço.

supercat
fonte
1

Na verdade, &myarraye myarrayambos são o endereço base.

Se você quiser ver a diferença em vez de usar

printf("my_array = %p\n", my_array);
printf("my_array = %p\n", &my_array);

usar

printf("my_array = %s\n", my_array);
printf("my_array = %p\n", my_array);
Ravi Bisla
fonte