web-dev-qa-db-fra.com

Comment les tableaux C sont-ils représentés en mémoire?

Je crois comprendre la manière dont les variables et les pointeurs normaux sont représentés en mémoire si vous utilisez C. 

Par exemple, il est facile de comprendre qu'un pointeur Ptr aura une adresse et que sa valeur sera une adresse différente, qui correspond à l'espace en mémoire sur lequel il pointe. Le code suivant:

int main(){
    int x = 10;
    int *Ptr;
    Ptr = &x;
return 0;
}

Aurait la représentation suivante en mémoire:

+---------------------+-------------+---------+
| Variable Name       | Address     | Value   | 
+---------------------+-------------+---------+
| x                   | 3342        | 10      |
+---------------------+-------------+---------+
| Ptr                 | 5466        | 3342    |
+---------------------+-------------+---------+

Cependant, j'ai du mal à comprendre comment les tableaux sont représentés en mémoire. Par exemple le code:

int main(){
    int x[5];
        x[0]=12;
        x[1]=13;
        x[2]=14;

    printf("%p\n",(void*)x);
    printf("%p\n",(void*)&x);

return 0;
}

sort deux fois la même adresse (par souci de simplicité 10568). Ce qui signifie que x == & x. Pourtant * x (ou x [0] en notation tableau) est égal à 12, * (x + 1) (ou x [1] en notation tableau) est égal à 13 et ainsi de suite. Comment cela peut-il être représenté? Une façon pourrait être ceci:

+---------------------+-------------+----------+----------------------+
| Variable Name       | Address     | Value    | Value IF array       |
+---------------------+-------------+----------+----------------------+
| x                   | 10568       | 10568    | 12                   |
+---------------------+-------------+----------+----------------------+
|                     | 10572       |          | 13                   | 
+---------------------+-------------+----------+----------------------+
|                     | 10576       |          | 14                   | 
+---------------------+-------------+----------+----------------------+
|                     | 10580       |          | trash                | 
+---------------------+-------------+----------+----------------------+
|                     | 10584       |          | trash                | 
+---------------------+-------------+----------+----------------------+

Est-ce proche de ce qui se passe ou complètement éteint?

25
Daniel Scocco

Un tableau est un bloc d'objets contigus sans espaces entre eux. Cela signifie que x dans votre deuxième exemple est représenté en mémoire par:

+---------------------+-------------+---------+
| Variable Name       | Address     | Value   | 
+---------------------+-------------+---------+
| x                   | 10568       | 12      |
|                     |             +---------+
|                     |             | 13      |
|                     |             +---------+
|                     |             | 14      |
|                     |             +---------+
|                     |             | ??      |
|                     |             +---------+
|                     |             | ??      |
+---------------------+-------------+---------+

C'est-à-dire que x est cinq ints grande et a une seule adresse.

La partie étrange sur les tableaux ne réside pas dans la façon dont ils sont stockés, mais dans la façon dont ils sont évalués dans les expressions. Si vous utilisez un nom de tableau quelque part qui ne fait pas l'objet des opérateurs unaires & ou sizeof, il correspond à l'adresse de son premier membre.

Autrement dit, si vous écrivez simplement x, vous obtiendrez une valeur 10568 de type int *.

Si, au contraire, vous écrivez &x, alors la règle spéciale ne s'applique pas - l'opérateur & fonctionne donc normalement, ce qui signifie qu'il récupère l'adresse du tableau. Dans l'exemple, il s'agira d'une valeur 10568 de type int (*)[5].

La raison pour laquelle x == &x est que l'adresse du premier membre d'un tableau est nécessairement égale à l'adresse du tableau lui-même, puisqu'un tableau commence par son premier membre.

32
caf

Votre diagramme est correct. La bizarrerie autour de &x n’a rien à voir avec la façon dont les tableaux sont représentés en mémoire. Cela a à voir avec array-> pointeur decay. x par lui-même en valeur le contexte se décompose en un pointeur sur son premier élément; c'est-à-dire qu'il équivaut à &x[0]. &x est un pointeur sur un tableau, et le fait que les deux soient numériquement égaux signifie simplement que l'adresse d'un tableau est numériquement égale à l'adresse de son premier élément.

22
Raymond Chen

Oui, vous l'avez Un tableau C recherche la valeur indexée x[y] en calculant x + (y * sizeof(type)). x est l'adresse de départ du tableau. y * sizeof(type) est un décalage par rapport à cela. x[0] produit la même adresse que x.

Les tableaux multidimensionnels sont similaires, donc int x[y][z] va utiliser la mémoire sizeof(int) * y * z.

Pour cette raison, vous pouvez faire quelques astuces stupides avec le pointeur C. Cela signifie également que l'obtention de la taille d'un tableau est (presque) impossible.

2
Schwern

La section Tableaux et pointeurs du C FAQ contient des informations utiles.

0
Sinan Ünür

Daniel,

ce n'est pas difficile. Vous avez l'idée de base et il n'y a pas beaucoup de différence dans la représentation en mémoire des tableaux. si vous déclarez un tableau, dites

     void main(){
         int arr[5]={0,1,2,3,4};


     }

vous avez initialisé (défini) le tableau. Ainsi, les cinq éléments seront stockés dans cinq emplacements adjacents en mémoire. vous pouvez l'observer en référençant l'adresse mémoire de chaque élément . Contrairement aux autres types de données primitifs en C, un identificateur de tableau (ici, arr ) représente lui-même son pointeur. L'idée semble vague si vous êtes débutant, mais vous vous sentirez à l'aise plus tard. 

      printf("%d",arr);

cette ligne vous montrera l'adresse mémoire du premier élément, arr [0]. Ceci est similaire au référencement de l'adresse du premier élément.

      printf("%d",&arr[0]);

maintenant, vous pouvez voir les emplacements de mémoire de tous les éléments. Le code suivant fera le travail.

    int i;
    for(i=0;i<5;i++){
       printf("location of %d is %d\n",arr[i],&arr[i]);
    } 

vous verrez chaque incrément d'adresse incrémenter de quatre (si vos entiers sont longs de 32 bits) . Ainsi, vous pouvez facilement comprendre comment les tableaux sont stockés dans la mémoire.

vous pouvez également essayer la même chose en utilisant une méthode différente.

    int i;
    for(i=0;i<5;i++){
       printf("location of %d is %d\n",*(a+i),a+i);
    }

vous obtiendrez le même ensemble de réponses dans les deux cas et tenterez d'obtenir l'équivalence. 

essayez la même expérience en utilisant différents types de données (types char, float et struct). Vous verrez comment les espaces entre les éléments adjacents varient en fonction de la taille d'un seul élément.

0
Tharindu Rusira

Un tableau C est juste un bloc de mémoire qui a des valeurs séquentielles de la même taille. Lorsque vous appelez malloc (), il vous accorde simplement un bloc de mémoire. foo[5] est identique à *(foo + 5).

Exemple - foo.c:

#include <stdio.h>

int main(void)
{
    int foo[5];
    printf("&foo[0]: %tx\n", &foo[0]);
    printf("foo: %tx\n\n", foo);
    printf("&foo[3]: %tx\n", &foo[3]);
    printf("foo: %tx\n", foo + 3);
}

Sortie:

$ ./foo
&foo[0]: 5fbff5a4
foo: 5fbff5a4

&foo[3]: 5fbff5b0
foo: 5fbff5b0
0
ObscureRobot

Un tableau en C est un bloc séquentiel de mémoire, chaque bloc membre ayant la même taille. C'est pourquoi les pointeurs fonctionnent, vous recherchez un décalage basé sur l'adresse du premier membre.

0
alex