Typedefs pour les types de données complexes

J’essaie de comprendre les mécanismes sous-jacents de la façon dont C gère les typedefs complexes, du sharepoint vue de la syntaxe.

Examinez les exemples ci-dessous (références incluses à la fin de la question).

typedef int (*p1d)[10]; 

est la déclaration appropriée, c’est-à-dire que p1d est un pointeur sur un tableau de 10 entiers, exactement comme il était dans la déclaration utilisant le type Array. Notez que ceci est différent de

 typedef int *p1d[10]; 

ce qui ferait de p1d le nom d’un tableau de 10 pointeurs à taper int.

Donc, si je considère la priorité des opérateurs pour les deux exemples (je les réécrirai):

 int *p1d[10]; // Becomes ... int* p1d[10]; 

Donc, en lisant de gauche à droite et en utilisant la priorité des opérateurs, je reçois: “Pointeur pour taper int, nommé p1d, de taille 10”, ce qui est faux. En ce qui concerne l’autre / premier cas:

 int (*p1d)[10]; 

Ce que je lis comme “p1d est un pointeur, de type int, et est un tableau de 10 tels éléments”, ce qui est également faux.

Quelqu’un pourrait-il expliquer les règles appliquées pour déterminer ces types de caractères? J’aimerais aussi les appliquer aux pointeurs de fonction, et j’espère que cette discussion expliquera également la logique derrière les const constantes (c’est-à-dire: pointeur sur les données constantes vs const pointeur sur les données variables).

Je vous remercie.

Références:


  1. Didacticiel C: pointeurs vers les tableaux: http://www.taranets.net/cgi/ts/1.37/ts.ws.pl?w=329;b=285
  2. Priorité des opérateurs: http://www.swansontec.com/sopc.html

Un de mes professeurs a écrit ce petit guide pour lire ce type de déclaration . Lisez-le, ça vaudra la peine et, espérons-le, répondez à toutes les questions.

Tout le mérite revient à Rick Ord ( http://cseweb.ucsd.edu/~ricko/ )

 The "right-left" rule is a completely regular rule for deciphering C declarations. It can also be useful in creating them. First, symbols. Read * as "pointer to" - always on the left side [] as "array of" - always on the right side () as "function returning" - always on the right side as you encounter them in the declaration. STEP 1 ------ Find the identifier. This is your starting point. Then say to yourself, "identifier is." You've started your declaration. STEP 2 ------ Look at the symbols on the right of the identifier. If, say, you find "()" there, then you know that this is the declaration for a function. So you would then have "identifier is function returning". Or if you found a "[]" there, you would say "identifier is array of". Continue right until you run out of symbols *OR* hit a *right* parenthesis ")". (If you hit a left parenthesis, that's the beginning of a () symbol, even if there is stuff in between the parentheses. More on that below.) STEP 3 ------ Look at the symbols to the left of the identifier. If it is not one of our symbols above (say, something like "int"), just say it. Otherwise, translate it into English using that table above. Keep going left until you run out of symbols *OR* hit a *left* parenthesis "(". Now repeat steps 2 and 3 until you've formed your declaration. Here are some examples: int *p[]; 1) Find identifier. int *p[]; ^ "p is" 2) Move right until out of symbols or right parenthesis hit. int *p[]; ^^ "p is array of" 3) Can't move right anymore (out of symbols), so move left and find: int *p[]; ^ "p is array of pointer to" 4) Keep going left and find: int *p[]; ^^^ "p is array of pointer to int". (or "p is an array where each element is of type pointer to int") Another example: int *(*func())(); 1) Find the identifier. int *(*func())(); ^^^^ "func is" 2) Move right. int *(*func())(); ^^ "func is function returning" 3) Can't move right anymore because of the right parenthesis, so move left. int *(*func())(); ^ "func is function returning pointer to" 4) Can't move left anymore because of the left parenthesis, so keep going right. int *(*func())(); ^^ "func is function returning pointer to function returning" 5) Can't move right anymore because we're out of symbols, so go left. int *(*func())(); ^ "func is function returning pointer to function returning pointer to" 6) And finally, keep going left, because there's nothing left on the right. int *(*func())(); ^^^ "func is function returning pointer to function returning pointer to int". As you can see, this rule can be quite useful. You can also use it to sanity check yourself while you are creating declarations, and to give you a hint about where to put the next symbol and whether parentheses are required. Some declarations look much more complicated than they are due to array sizes and argument lists in prototype form. If you see "[3]", that's read as "array (size 3) of...". If you see "(char *,int)" that's read as "function expecting (char *,int) and returning...". Here's a fun one: int (*(*fun_one)(char *,double))[9][20]; I won't go through each of the steps to decipher this one. Ok. It's: "fun_one is pointer to function expecting (char *,double) and returning pointer to array (size 9) of array (size 20) of int." As you can see, it's not as complicated if you get rid of the array sizes and argument lists: int (*(*fun_one)())[][]; You can decipher it that way, and then put in the array sizes and argument lists later. Some final words: It is quite possible to make illegal declarations using this rule, so some knowledge of what's legal in C is necessary. For instance, if the above had been: int *((*fun_one)())[][]; it would have been "fun_one is pointer to function returning array of array of ^^^^^^^^^^^^^^^^^^^^^^^^ pointer to int". Since a function cannot return an array, but only a pointer to an array, that declaration is illegal. Illegal combinations include: []() - cannot have an array of functions ()() - cannot have a function that returns a function ()[] - cannot have a function that returns an array In all the above cases, you would need a set of parens to bind a * symbol on the left between these () and [] right-side symbols in order for the declaration to be legal. Here are some legal and illegal examples: int i; an int int *p; an int pointer (ptr to an int) int a[]; an array of ints int f(); a function returning an int int **pp; a pointer to an int pointer (ptr to a ptr to an int) int (*pa)[]; a pointer to an array of ints int (*pf)(); a pointer to a function returning an int int *ap[]; an array of int pointers (array of ptrs to ints) int aa[][]; an array of arrays of ints int af[](); an array of functions returning an int (ILLEGAL) int *fp(); a function returning an int pointer int fa()[]; a function returning an array of ints (ILLEGAL) int ff()(); a function returning a function returning an int (ILLEGAL) int ***ppp; a pointer to a pointer to an int pointer int (**ppa)[]; a pointer to a pointer to an array of ints int (**ppf)(); a pointer to a pointer to a function returning an int int *(*pap)[]; a pointer to an array of int pointers int (*paa)[][]; a pointer to an array of arrays of ints int (*paf)[](); a pointer to a an array of functions returning an int (ILLEGAL) int *(*pfp)(); a pointer to a function returning an int pointer int (*pfa)()[]; a pointer to a function returning an array of ints (ILLEGAL) int (*pff)()(); a pointer to a function returning a function returning an int (ILLEGAL) int **app[]; an array of pointers to int pointers int (*apa[])[]; an array of pointers to arrays of ints int (*apf[])(); an array of pointers to functions returning an int int *aap[][]; an array of arrays of int pointers int aaa[][][]; an array of arrays of arrays of ints int aaf[][](); an array of arrays of functions returning an int (ILLEGAL) int *afp[](); an array of functions returning int pointers (ILLEGAL) int afa[]()[]; an array of functions returning an array of ints (ILLEGAL) int aff[]()(); an array of functions returning functions returning an int (ILLEGAL) int **fpp(); a function returning a pointer to an int pointer int (*fpa())[]; a function returning a pointer to an array of ints int (*fpf())(); a function returning a pointer to a function returning an int int *fap()[]; a function returning an array of int pointers (ILLEGAL) int faa()[][]; a function returning an array of arrays of ints (ILLEGAL) int faf()[](); a function returning an array of functions returning an int (ILLEGAL) int *ffp()(); a function returning a function returning an int pointer (ILLEGAL) 

Simplifier un peu les règles de KepaniHaole, cela revient à:

  1. Trouver l’identifiant le plus à gauche
  2. Travaillez à votre guise en gardant à l’esprit le regroupement explicite absent entre parenthèses, function-call () et [] bind avant * .
  3. Appliquer récursivement à tous les parameters de fonction.

Ainsi, T *a[] est un tableau de pointeur tp T , T (*a)[] est un pointeur sur un tableau de T , T *f() est une fonction renvoyant un pointeur sur T , et T (*f)() est un pointeur sur une fonction renvoyant T

En prenant un exemple qui donne des brûlures d’estomac à beaucoup de gens, nous pouvons regarder le prototype de la fonction de signal POSIX:

 void (*signal( int sig, void (*func)( int )))( int ); 

qui se lit comme

  signal -- signal signal( ) -- is a function with signal( sig ) -- parameter sig signal( int sig ) -- of type int signal( int sig, func ) -- and parameter func signal( int sig, (*func) ) -- of type pointer to signal( int sig, (*func)( )) -- a function with signal( int sig, (*func)( int )) -- an int parameter signal( int sig, void (*func)( int )) -- returning void (*signal( int sig, void (*func)( int ))) -- returning a pointer to (*signal( int sig, void (*func)( int )))( ) -- a function with (*signal( int sig, void (*func)( int )))( int ) -- an int parameter void (*signal( int sig, void (*func)( int )))( int ) -- returning void