Après avoir vu (et posé!) Tant de questions similaires à
Que
int (*f)(int (*a)[5])
signifie en C?
et même en voyant qu'ils avaient fait un programme pour aider les gens à comprendre la syntaxe C, je ne peux m'empêcher de me demander:
Pourquoi la syntaxe de C a-t-elle été conçue de cette façon?
Par exemple, si je concevais des pointeurs, je traduirais "un pointeur vers un tableau de 10 éléments de pointeurs" en
int*[10]* p;
et non
int* (*p)[10];
ce que je pense que la plupart des gens seraient d'accord est beaucoup moins simple.
Je me demande donc pourquoi la, euh, syntaxe non intuitive? Y a-t-il un problème spécifique résolu par la syntaxe (peut-être une ambiguïté?) Que je ne connais pas?
cdecl
commande est très pratique pour décoder des déclarations C complexes. Il y a aussi une interface Web sur cdecl.org .Réponses:
Ma compréhension de l'histoire de celui-ci est basée sur deux points principaux ...
Premièrement, les auteurs du langage ont préféré rendre la syntaxe centrée sur les variables plutôt que centrée sur le type. Autrement dit, ils voulaient qu'un programmeur regarde la déclaration et pense "si j'écris l'expression
*func(arg)
, cela se traduira par unint
; si j'écris,*arg[N]
j'aurai un flotteur" plutôt que "func
doit être un pointeur vers une fonction prenant ceci et le retour que ».L' entrée C sur Wikipedia affirme que:
... citant la p122 de K & R2 que, hélas, je n'ai pas besoin de trouver pour vous trouver le devis étendu.
Deuxièmement, il est vraiment très difficile de trouver une syntaxe de déclaration cohérente lorsque vous avez affaire à des niveaux d'indirection arbitraires. Votre exemple pourrait bien fonctionner pour exprimer le type que vous avez pensé au départ, mais est-il adapté à une fonction prenant un pointeur vers un tableau de ces types et renvoyant un autre désordre hideux? (Peut-être que oui, mais avez-vous vérifié? Pouvez-vous le prouver? ).
Rappelez-vous, une partie du succès de C est due au fait que les compilateurs ont été écrits pour de nombreuses plates-formes différentes, et il aurait donc été préférable d'ignorer un certain degré de lisibilité pour faciliter l'écriture des compilateurs.
Cela dit, je ne suis pas un expert en grammaire linguistique ou en écriture de compilateur. Mais j'en sais assez pour savoir qu'il y a beaucoup à savoir;)
la source
Beaucoup de bizarreries du langage C s'expliquent par le fonctionnement des ordinateurs lors de sa conception. Il y avait des quantités très limitées de mémoire de stockage, il était donc très important de minimiser la taille des fichiers de code source eux-mêmes. La pratique de la programmation dans les années 70 et 80 consistait à s'assurer que le code source contenait le moins de caractères possible, et de préférence aucun commentaire excessif sur le code source.
C'est bien sûr ridicule aujourd'hui, avec un espace de stockage pratiquement illimité sur les disques durs. Mais cela fait partie de la raison pour laquelle C a une syntaxe si étrange en général.
En ce qui concerne spécifiquement les pointeurs de tableau, votre deuxième exemple devrait être
int (*p)[10];
(ouais la syntaxe est très déroutante). Je pourrais peut-être lire cela comme "un pointeur int sur un tableau de dix" ... ce qui est quelque peu logique. Sinon pour la parenthèse, le compilateur l'interpréterait comme un tableau de dix pointeurs à la place, ce qui donnerait à la déclaration une signification entièrement différente.Étant donné que les pointeurs de tableau et les pointeurs de fonction ont tous deux une syntaxe assez obscure en C, la chose raisonnable à faire est de taper la bizarrerie. Peut-être comme ça:
Exemple obscur:
Exemple équivalent non obscur:
Les choses peuvent devenir encore plus obscures si vous avez affaire à des tableaux de pointeurs de fonction. Ou le plus obscur d'entre eux: des fonctions renvoyant des pointeurs de fonction (légèrement utiles). Si vous n'utilisez pas de typedefs pour de telles choses, vous deviendrez rapidement fou.
la source
C'est assez simple:
int *p
signifie que*p
c'est un int;int a[5]
signifie quea[i]
c'est un int.Signifie que
*f
c'est une fonction,*a
est un tableau de cinq entiers, toutf
comme une fonction prenant un pointeur sur un tableau de cinq entiers, et retournant int. Cependant, en C, il n'est pas utile de passer un pointeur sur un tableau.Les déclarations C se compliquent très rarement.
En outre, vous pouvez clarifier l'utilisation de typedefs:
la source
typedef
,const
,volatile
et la possibilité d'initialiser les choses dans les déclarations. De nombreuses ambiguïtés gênantes de la syntaxe de déclaration (par exemple, si ellesint const *p, *q;
doivent être liéesconst
au type ou à la déclaration ) ne pouvaient pas apparaître dans le langage tel que conçu à l'origine. Je souhaite que la langue ait ajouté deux points entre le type et le declarand, mais a permis son omission lors de l'utilisation de types de "mots réservés" intégrés sans qualificatifs. Le sensint: const *p,*q;
etint const *: p,*q;
aurait été clair.Je pense que vous devez considérer * [] comme des opérateurs attachés à une variable. * est écrit avant une variable, [] après.
Lisons l'expression de type
L'élément le plus intérieur est p, une variable, donc
signifie: p est une variable.
Avant la variable, il y a un *, l'opérateur * est toujours placé avant l'expression à laquelle il fait référence, par conséquent,
signifie: la variable p est un pointeur. Sans (), l'opérateur [] à droite aurait une priorité plus élevée, c'est-à-dire
serait analysé comme
L'étape suivante est []: puisqu'il n'y a pas d'autre (), [] a une priorité plus élevée que l'extérieur *, donc
signifie: (la variable p est un pointeur) vers un tableau. Ensuite, nous avons le deuxième *:
signifie: ((la variable p est un pointeur) vers un tableau) de pointeurs
Enfin, vous avez l'opérateur int (un nom de type), qui a la priorité la plus faible:
signifie: (((la variable p est un pointeur) vers un tableau) de pointeurs) en entier.
Ainsi, l'ensemble du système est basé sur des expressions de type avec des opérateurs, et chaque opérateur a ses propres règles de priorité. Cela permet de définir des types très complexes.
la source
Ce n'est pas si difficile quand vous commencez à penser et C n'a jamais été un langage très facile. Et
int*[10]* p
ce n'est vraiment pas plus facile queint* (*p)[10]
Et quel type de k seraitint*[10]* p, k;
la source
pointer to int
etint
ne sont même pas du même type, ils doivent donc être déclarés séparément. Période. Écoutez l'homme. Il a 18 000 représentants pour une raison.