Dans le bit de code suivant, les valeurs de pointeur et les adresses de pointeur diffèrent comme prévu.
Mais les valeurs et adresses de tableau ne le font pas!
Comment se peut-il?
Production
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;
}
sizeof(&array)
revient?Réponses:
Le nom d'un tableau s'évalue généralement à l'adresse du premier élément du tableau, donc
array
et&array
a la même valeur (mais des types différents, doncarray+1
et ne&array+1
sera pas égal si le tableau a plus d'un élément de long).Il existe deux exceptions à cela: lorsque le nom du tableau est un opérande de
sizeof
ou unaire&
(adresse de), le nom fait référence à l'objet tableau lui-même. Ainsi,sizeof array
vous donne la taille en octets du tableau entier, pas la taille d'un pointeur.Pour un tableau défini comme
T array[size]
, il aura le typeT *
. Quand / si vous l'incrémentez, vous accédez à l'élément suivant du tableau.&array
évalue à la même adresse, mais étant donné la même définition, il crée un pointeur du typeT(*)[size]
- c'est-à-dire qu'il s'agit d'un pointeur vers un tableau, pas vers un seul élément. Si vous incrémentez ce pointeur, il ajoutera la taille du tableau entier, et non la taille d'un seul élément. Par exemple, avec un code comme celui-ci:Nous pouvons nous attendre à ce que le deuxième pointeur soit 16 plus grand que le premier (car c'est un tableau de 16 caractères). Puisque% p convertit généralement les pointeurs en hexadécimal, cela pourrait ressembler à quelque chose comme:
la source
&array
est un pointeur vers le premier élément du tableau, où as searray
réfère au tableau entier. La différence fondamentale peut également être observée en comparantsizeof(array)
, àsizeof(&array)
. Notez cependant que si vous passezarray
en argument à une fonction, seul&array
est en fait passé. Vous ne pouvez pas transmettre un tableau par valeur à moins qu'il ne soit encapsulé avec unstruct
.&array[0]
passé, pas&array
ce qui serait un pointeur vers le tableau. C'est peut-être un petit choix, mais je pense qu'il est important de clarifier; les compilateurs avertiront si la fonction a un prototype qui correspond au type du pointeur transmis.int *p = array; int **pp = &p;
.C'est parce que le nom du tableau (
my_array
) est différent d'un pointeur vers un tableau. C'est un alias de l'adresse d'un tableau et son adresse est définie comme l'adresse du tableau lui-même.Le pointeur est une variable C normale sur la pile, cependant. Ainsi, vous pouvez prendre son adresse et obtenir une valeur différente de l'adresse qu'elle contient.
J'ai écrit sur ce sujet ici - s'il vous plaît jeter un oeil.
la source
register
) quelle que soit sa durée de stockage: statique, dynamique ou automatique.my_array
lui-même est sur la pile, carmy_array
c'est le tableau entier.my_array
, lorsqu'il n'est pas le sujet des opérateurs&
ousizeof
, est évalué comme un pointeur vers son premier élément (c'est-à-dire&my_array[0]
) - maismy_array
lui-même n'est pas ce pointeur (my_array
est toujours le tableau). Ce pointeur est juste une rvaleur éphémère (par exemple donnéeint a;
, c'est juste commea + 1
) - conceptuellement au moins, il est "calculé selon les besoins". La vraie "valeur" demy_array
est le contenu de l'ensemble du tableau - c'est juste qu'épingler cette valeur en C est comme essayer d'attraper du brouillard dans un bocal.En C, lorsque vous utilisez le nom d'un tableau dans une expression (y compris en le passant à une fonction), à moins qu'il ne s'agisse de l'opérande de l'
&
opérateur address-of ( ) ou de l'sizeof
opérateur, il se décompose en un pointeur vers son premier élément.Autrement dit, dans la plupart des contextes,
array
équivaut à la&array[0]
fois au type et à la valeur.Dans votre exemple,
my_array
a un typechar[100]
qui se désintègre en achar*
lorsque vous le passez à printf.&my_array
a typechar (*)[100]
(pointeur vers un tableau de 100char
). Comme il s'agit de l'opérande to&
, c'est l'un des cas quimy_array
ne se désintègre pas immédiatement en un pointeur vers son premier élément.Le pointeur vers le tableau a la même valeur d'adresse qu'un pointeur vers le premier élément du tableau, car un objet tableau est juste une séquence contiguë de ses éléments, mais un pointeur vers un tableau a un type différent vers un pointeur vers un élément de ce tableau. Ceci est important lorsque vous effectuez une arithmétique de pointeur sur les deux types de pointeur.
pointer_to_array
a typechar *
- initialisé pour pointer sur le premier élément du tableau car c'est ce quimy_array
se désintègre dans l'expression d'initialisation - et&pointer_to_array
a typechar **
(pointeur vers un pointeur vers achar
).Parmi ceux-ci:
my_array
(après décroissance verschar*
),&my_array
etpointer_to_array
tous pointent directement sur le tableau ou le premier élément du tableau et ont donc la même valeur d'adresse.la source
La raison pour laquelle
my_array
et le&my_array
résultat dans la même adresse peuvent être facilement compris lorsque vous regardez la disposition de la mémoire d'un tableau.Disons que vous avez un tableau de 10 caractères (au lieu des 100 dans votre code).
La mémoire pour
my_array
ressemble à quelque chose comme:En C / C ++, un tableau se décompose en pointeur vers le premier élément d'une expression telle que
Si vous examinez où se trouve le premier élément du tableau, vous verrez que son adresse est la même que l'adresse du tableau:
la source
Dans le langage de programmation B, qui était le prédécesseur immédiat de C, les pointeurs et les entiers étaient librement interchangeables. Le système se comporterait comme si toute la mémoire était un tableau géant. Chaque nom de variable était associé à une adresse globale ou relative à la pile, pour chaque nom de variable, les seules choses dont le compilateur devait garder une trace était de savoir s'il s'agissait d'une variable globale ou locale, et son adresse par rapport à la première variable globale ou locale. variable.
Étant donné une déclaration mondiale comme
i;
[il n'y avait pas besoin de spécifier un type, puisque tout était un entier / pointeur] serait traité par le compilateur:address_of_i = next_global++; memory[address_of_i] = 0;
et une déclaration commei++
serait traitée comme:memory[address_of_i] = memory[address_of_i]+1;
.Une déclaration comme
arr[10];
serait traitée commeaddress_of_arr = next_global; memory[next_global] = next_global; next_global += 10;
. Notez que dès que cette déclaration était traitée, le compilateur pouvait immédiatement oublier d'arr
être un tableau . Une instruction commearr[i]=6;
serait traitée commememory[memory[address_of_a] + memory[address_of_i]] = 6;
. Le compilateur ne se soucierait pas de savoir s'ilarr
représentait un tableau eti
un entier, ou vice versa. En effet, cela ne se soucierait pas de savoir s'il s'agissait des deux tableaux ou des deux entiers; il générerait parfaitement le code tel que décrit, sans se soucier de savoir si le comportement résultant serait probablement utile.L'un des objectifs du langage de programmation C était d'être largement compatible avec B. En B, le nom d'un tableau [appelé "vecteur" dans la terminologie de B] identifiait une variable contenant un pointeur qui était initialement assigné pour pointer vers au premier élément d'une allocation de la taille donnée, donc si ce nom apparaissait dans la liste d'arguments d'une fonction, la fonction recevrait un pointeur vers le vecteur. Même si C a ajouté des types de tableaux «réels», dont le nom était rigidement associé à l'adresse de l'allocation plutôt qu'à une variable de pointeur qui pointerait initialement vers l'allocation, la décomposition des tableaux en pointeurs a fait que le code déclarant un tableau de type C se comporte de la même manière en code B qui a déclaré un vecteur et n'a jamais modifié la variable contenant son adresse.
la source
En fait
&myarray
, lesmyarray
deux sont l'adresse de base.Si vous voulez voir la différence au lieu d'utiliser
utilisation
la source