157 votes

C les pointeurs : pointant vers un tableau de taille fixe

Cette question va au C gourous:

En C, il est possible de déclarer un pointeur comme suit:

char (* p)[10];

.. ce qui est dit en substance que ce pointeur pointe vers un tableau de 10 caractères. La chose intéressante à propos de déclarer un pointeur de ce type est que vous obtiendrez une erreur de compilation si vous essayez d'affecter un pointeur d'un tableau de taille différente pour p. Il vous donnera également une erreur de compilation si vous essayez d'affecter la valeur d'un simple pointeur de char à p. J'ai essayé cela avec gcc et il semble fonctionner avec la norme ANSI, C89 et C99.

Il me semble que de déclarer un pointeur comme cela pourrait être très utile - en particulier, lors du passage d'un pointeur à une fonction. Habituellement, les gens écrivent le prototype d'une telle fonction comme ceci:

void foo(char * p, int plen);

Si vous vous attendiez à un tampon d'une taille spécifique, il vous suffit de tester la valeur de plen. Toutefois, vous ne peut pas être garanti que la personne qui passe p vous allez vraiment vous donner plen valide emplacements de mémoire dans la mémoire tampon. Vous devez faire confiance que la personne qui a appelé cette fonction est en train de faire la bonne chose. Sur l'autre main:

void foo(char (*p)[10]);

..serait de force à l'appelant de vous donner un buffer de la taille spécifiée.

Cela semble très utile, mais je n'ai jamais vu un pointeur déclarée comme telle dans le code que j'ai jamais couru à travers.

Ma question est: Est-il une raison pourquoi les gens ne pas déclarer des pointeurs comme ça? Ne suis-je pas voir certains évident piège?

Merci d'avance

205voto

AndreyT Points 139512

Ce que vous dites dans votre post n'est absolument correct. Tout développeur C vient exactement la même découverte et exactement à la même conclusion quand (si) ils atteignent un certain niveau de compétence, avec C la langue.

Lorsque les spécificités de votre secteur d'activité appel à un certain nombre de taille fixe (taille de la matrice est une constante de compilation), la seule bonne façon de passer un tableau à une fonction en utilisant un pointeur de tableau de paramètre

void foo(char (*p)[10]);

(en langage C++ cela se fait aussi avec des références

void foo(char (&p)[10]);

).

Cela permettra de langue au niveau de la vérification de type, qui sera assurez-vous que le tableau de exactement de taille correcte est fournie en argument. En fait, dans de nombreux cas, les gens d'utiliser cette technique de manière implicite, sans même s'en rendre compte, se cachant le type du tableau derrière un typedef nom

typedef int Vector3d[3];

void transform(Vector3d *vector);
/* equivalent to `void transform(int (*vector)[3])` */
...
Vector3d vec;
...
transform(&vec);

Notez que le code ci-dessus est invariante par rapport à l' Vector3d type d'un tableau ou d'un struct. Vous pouvez changer la définition de l' Vector3d à tout moment à partir d'un tableau à un struct et à l'arrière, et vous n'aurez pas à changer le reste du code (il ya des exceptions à cela, mais dans le contexte de cette discussion, c'est vrai).

Cependant, vous ne verrez pas cette méthode de la matrice de passage utilisé explicitement trop souvent, tout simplement parce que trop de nombreuses personnes se confondre par un plutôt alambiqué et de syntaxe ne sont tout simplement pas assez confortable avec de telles caractéristiques du langage C pour les utiliser correctement. Pour cette raison, en moyenne, la vraie vie, le passage d'un tableau comme un pointeur vers son premier élément est le plus répandu. Il a juste l'air plus "simple".

Mais en réalité, à l'aide du pointeur vers le premier élément de la matrice de passage est un créneau très technique, une astuce, qui sert un but très précis: son seul but est de faciliter le passage de tableaux de taille différente (c'est à dire au moment de l'exécution de la taille). Si vous avez vraiment besoin pour être en mesure de traiter les tableaux de la durée d'exécution de la taille, puis à la bonne façon de passer un tableau est un pointeur vers son premier élément avec le béton de la taille fournie par un paramètre supplémentaire

void foo(char p[], unsigned plen);

En fait, dans de nombreux cas, il est très utile d'être en mesure de traiter les tableaux de la durée d'exécution de la taille, ce qui contribue également à la popularité de la méthode. De nombreux développeurs C tout simplement de ne jamais rencontrer (ou jamais reconnaître) la nécessité de traiter un tableau de taille fixe, donc en restant insensible à la bonne taille fixe de la technique.

Néanmoins, si la taille de la matrice est fixe, passant comme un pointeur vers un élément

void foo(char p[])

est une technique principale erreur de niveau, ce qui n'est malheureusement assez répandue de nos jours. Un pointeur vers tableau technique doit être utilisée à la place dans de tels cas.

Une autre raison qui pourrait empêcher l'adoption du tableau de taille fixe la technique de passage, c'est la domination de l'approche naïve, à la saisie de tableaux alloués dynamiquement. Par exemple, si le programme d'appels pour les matrices de type char[10] (comme dans votre exemple), une moyenne développeur malloc de ces tableaux

char *p = malloc(10 * sizeof *p);

Ce tableau ne peut pas être transmis à une fonction déclarée comme

void foo(char (*p)[10]);

qui confond le développeur moyen, et leur fait abandonner la taille fixe déclaration de paramètre sans lui donner une autre pensée. En réalité, si, à la racine du problème réside dans le naïf malloc approche. L' malloc technique ci-dessus est, à nouveau, réservés pour les tableaux de la durée d'exécution de taille. Si le type du tableau a au moment de la compilation de la taille, de la bonne façon d' malloc il se présenterait comme suit

char (*p)[10] = malloc(sizeof *p);

Bien entendu, cela peut être facilement transmis à la ci-dessus déclarée foo

foo(p);

et le compilateur d'effectuer le bon type de vérification. Mais encore une fois, c'est trop confus pour un non préparés développeur C, qui est pourquoi vous ne voyez pas trop souvent dans le "typique" de la moyenne quotidienne de code.

15voto

figurassa Points 462

Je voudrais ajouter à AndreyT de réponse (dans le cas où quelqu'un tombe sur cette page à la recherche pour plus d'infos sur ce sujet):

Comme je commence à jouer plus avec ces déclarations, je me rends compte qu'il y a handicap majeur associé avec eux dans C (apparemment pas en C++). Il est assez courant d'avoir une situation où vous souhaitez donner un appelant d'un const pointeur vers une mémoire tampon que vous avez écrit. Malheureusement, ce n'est pas possible lors de la déclaration d'un pointeur de ce genre dans C. En d'autres termes, la norme C (6.7.3 - Paragraphe 8) est en contradiction avec quelque chose comme ceci:


   int array[9];

   const int (* p2)[9] = &array;  /* Not legal unless array is const as well */

Cette contrainte ne semble pas être présent en C++, ce type de déclarations beaucoup plus utile. Mais dans le cas de C, il est nécessaire de revenir à un régulière déclaration de pointeur à chaque fois que vous voulez un const pointeur vers le tampon de taille fixe (sauf si le tampon lui-même a été déclarée const pour commencer). Vous pouvez trouver plus d'info dans ce thread messagerie: texte du lien

C'est une contrainte sévère à mon avis et peut-être l'une des principales raisons pour lesquelles les gens n'ont pas l'habitude de déclarer des pointeurs comme ça en C. Les autres étant le fait que la plupart des gens ne savent même pas que vous pouvez déclarer un pointeur de ce type de AndreyT a souligné.

3voto

Keith Randall Points 17518

La raison évidente est que ce code ne compile pas:

extern void foo(char (*p)[10]);
void bar() {
  char p[10];
  foo(p);
}

La valeur par défaut de la promotion d'un tableau est un pointeur non qualifiés.

Voir aussi cette question, à l'aide de foo(&p) devrait fonctionner.

1voto

Carl Smotricz Points 36400

Bien, tout simplement, C ne fait pas les choses de cette façon. Un tableau de type T est passé autour comme un pointeur vers le premier T dans le tableau, et c'est tout ce que vous obtenez.

Cela permet de cool et élégant algorithmes, comme une boucle dans le tableau avec des expressions comme

*dst++ = *src++

L'inconvénient est que la gestion de la taille est à vous. Malheureusement, le défaut de le faire consciencieusement, a aussi entraîné des millions de bugs dans C de codage et/ou des possibilités d'exploitation malveillante.

Ce qui se rapproche de ce que vous demandez, C est de passer un struct (en valeur) ou un pointeur vers un (par référence). Tant que le même type struct est utilisé sur les deux côtés de cette opération, à la fois le code à la main la référence et le code qui l'utilise sont d'accord sur la taille des données manipulées.

Votre structure peut contenir toutes les données que vous souhaitez; il pourrait contenir votre tableau d'une taille définie.

Pourtant, rien de vous empêche de vous ou un incompétent ou malveillant coder de l'aide de moulages de tromper le compilateur dans le traitement de votre structure comme l'un d'une taille différente. Presque sans chaines capacité à faire ce genre de chose est une partie de C de la conception.

1voto

s1n Points 1224

Vous pouvez déclarer un tableau de caractères d'un certain nombre de façons:

char p[10];
char* p = (char*)malloc(10 * sizeof(char));

Le prototype d'une fonction qui prend un tableau en valeur est de:

void foo(char* p); //cannot modify p

ou par référence:

void foo(char** p); //can modify p, derefernce by *p[0] = 'f';

ou par la syntaxe de tableau:

void foo(char p[]); //same as char*

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X