web-dev-qa-db-fra.com

C / C ++ int [] vs int * (pointeurs vs notation de tableau). Quelle est la différence?

Je sais que les tableaux en C ne sont que des pointeurs vers des données stockées séquentiellement. Mais quelles différences impliquent la différence de notation [] et *. Je veux dire dans TOUS les contextes d'utilisation possibles. Par exemple:

char c[] = "test";

si vous fournissez cette instruction dans un corps de fonction, elle allouera la chaîne sur une pile tout en

char* c = "test";

pointera vers un segment de données (en lecture seule).

Pouvez-vous répertorier toutes les différences entre ces deux notations dans TOUS les contextes d'utilisation pour former une vue générale claire.

36
Alexander Reshytko

Selon la norme C99:

Un type de tableau décrit un ensemble d'objets non vide alloué de manière contiguë avec un type d'objet membre particulier, appelé type d'élément.

36) Les types de tableaux sont caractérisés par leur type d'élément et par le nombre d'éléments dans le tableau. Un type de tableau serait dérivé de son type d'élément, et si son type d'élément est T, le type de tableau est parfois appelé tableau de T. La construction d'un type de tableau à partir d'un type d'élément est appelée dérivation du type de tableau.

Un type de pointeur peut être dérivé d'un type de fonction, d'un type d'objet ou d'un type incomplet, appelé type référencé. Un type de pointeur décrit un objet dont la valeur fournit une référence à une entité du type référencé. Un type de pointeur dérivé du type référencé T est parfois appelé pointeur vers T. La construction d'un type de pointeur à partir d'un type référencé est appelée dérivation du type de pointeur.

Selon les déclarations standard…

char s[] = "abc", t[3] = "abc";
char s[] = { 'a', 'b', 'c', '\0' }, t[] = { 'a', 'b', 'c' };

…sont identiques. Le contenu des tableaux est modifiable. Par contre, la déclaration…

const char *p = "abc";

… Définit p avec le type pointeur sur la constante char et l'initialise pour pointer vers un objet de type tableau constant de char ( en C++ ) de longueur 4 dont les éléments sont initialisés avec un littéral de chaîne de caractères. Si une tentative est faite d'utiliser p pour modifier le contenu du tableau, le comportement n'est pas défini.

Selon 6.3.2.1 Indice de tableau le déréférencement et l'indice de tableau sont identiques:

La définition de l'opérateur d'indice [] est-ce E1[E2] est identique à (*((E1)+(E2))).

Les différences entre les tableaux et les pointeurs sont les suivantes:

  • le pointeur n'a aucune information sur la taille de la mémoire derrière (il n'y a pas de moyen portable pour l'obtenir)
  • un tableau de type incomplet ne peut pas être construit
  • un type de pointeur peut être dérivé d'un type incomplet
  • un pointeur peut définir une structure récursive (celle-ci est la conséquence des deux précédentes)

Ces liens peuvent être utiles au sujet:

25
Sergey K.
char c[] = "test";

Cela créera un tableau contenant le test de chaîne afin que vous puissiez modifier/changer n'importe quel caractère, par exemple

c[2] = 'p';

mais,

char * c = "test"

C'est un littéral de chaîne - c'est un caractère constant.
Donc, toute modification de ce littéral de chaîne nous donne une erreur de segmentation. Donc

c[2] = 'p';

est illégal maintenant et nous donne la faute.

9
neel

char [] Dénote le type "tableau de borne inconnue de char", tandis que char * Dénote le type "pointeur vers char". Comme vous l'avez observé, lorsqu'une définition d'une variable de type "tableau de borne inconnue de char" est initialisée avec un littéral de chaîne, le type est converti en "tableau [N] de char" où N est la taille appropriée. La même chose s'applique en général à l'initialisation à partir d'un agrégat de tableau:

int arr[] = { 0, 1, 2 };

arr est converti en type "tableau [3] d'int".

Dans une définition de type définie par l'utilisateur (struct, class ou union), les types liés à un tableau de inconnus sont interdits en C++, bien que dans certaines versions de C ils sont autorisés en tant que membre last d'une structure, où ils peuvent être utilisés pour accéder à la mémoire allouée après la fin de la structure; cette utilisation est appelée "tableaux flexibles".

La construction de type récursif est une autre différence; on peut construire des pointeurs et des tableaux de char * (par exemple char **, char (*)[10]) mais ceci est illégal pour les tableaux de borne inconnue; on ne peut pas écrire char []* ou char [][10] (bien que char (*)[] et char [10][] soient très bien).

Enfin, la cv-qualification fonctionne différemment; étant donné typedef char *ptr_to_char et typedef char array_of_unknown_bound_of_char[], la qualification du cv de la version du pointeur se comportera comme prévu, tandis que la qualification du cv de la version du tableau migrera la qualification cv vers le type d'élément: autrement dit, const array_of_unknown_bound_of_char Équivaut à const char [] Et non à la char (const) [] fictive. Cela signifie que dans une définition de fonction, où la désintégration tableau vers pointeur opère sur les arguments avant de construire le prototype,

void foo (int const a[]) {
    a = 0;
}

est légal; il n'y a aucun moyen de rendre le paramètre lié au tableau de inconnu non modifiable.

4
ecatmur

Le tout devient clair si vous savez que déclarer une variable pointeur ne crée pas le type de variable, il pointe vers. Il crée une variable pointeur.

Donc, dans la pratique, si vous avez besoin d'une chaîne, vous devez spécifier un tableau de caractères et un pointeur peut être utilisé plus tard.

1
Shash

En fait, les tableaux sont équivalents à pointeurs constants.

De plus, char c [] alloue de la mémoire au tableau, dont l'adresse de base est c elle-même. Aucune mémoire distincte n'est allouée pour stocker cette adresse.

L'écriture char * c alloue de la mémoire à la chaîne dont l'adresse de base est stockée dans c. De plus, un emplacement de mémoire séparé est utilisé pour stocker c.

0
Cygnus