2009-10-27 5 views
55

Qu'est-ce qui est utile à propos de cette syntaxe C - en utilisant les déclarations de fonction de style 'K & R'?Syntaxe alternative (K & R) C pour la déclaration de fonction par rapport aux prototypes

int func (p, p2) 
    void *p; 
    int p2; 
{ 
    return 0; 
} 

j'ai pu écrire ceci dans Visual Studios 2010beta

//yes the params are flipped 
void f() 
{ 
    void *v=0; 
    func(5,v); 
} 

Je ne comprends pas. Quel est le point de cette syntaxe? Je peux écrire:

int func (p, p2) 
    int p2; 
{ 
    return 0; 
} 
//and write 
int func (p, p2) 
{ 
    return 0; 
} 

La seule chose qu'il semble spécifier est le nombre de paramètres qu'il utilise et le type de retour. Je suppose que les paramètres sans types est plutôt cool, mais pourquoi l'autoriser et le int paranName après le déclarateur de fonction? C'est étrange.

Aussi est-ce encore standard C?

Répondre

124

La question que vous posez est vraiment deux questions, pas une. La plupart des réponses ont jusqu'ici essayé de couvrir toute la question avec une couverture générique "c'est K & R style" réponse, alors qu'en fait seulement une petite partie a quelque chose à voir avec ce qui est connu comme K & R style (sauf si vous voyez la langue entière C comme « K & R style » d'une manière ou d'une autre :)

la première partie est la syntaxe étrange utilisée en fonction définition

int func(p, p2) 
void *p; 
int p2; /* <- optional in C89/90, but not in C99 */ 
{ 
    return 0; 
} 

celui-ci est en fait un K & Définition de la fonction de style R D'autres réponses ont couvert cela plutôt bien. Et il n'y a pas grand-chose en réalité. La syntaxe est obsolète, mais toujours entièrement supportée même en C99 (à l'exception de la règle "pas d'implicite int" dans C99, ce qui signifie que dans C99 vous ne pouvez pas omettre la déclaration de p2).

La deuxième partie a peu à voir avec K & R-style. Je me réfère au fait que la fonction peut être appelée avec des arguments "échangés", c'est-à-dire qu'aucun contrôle de type de paramètre n'a lieu dans un tel appel. Cela a très peu à voir avec K & R-définition de style en soi, mais il a tout à voir avec votre fonction n'ayant pas de prototype. Vous voyez, en C lorsque vous déclarez une fonction comme celle

int foo(); 

il déclare en fait une fonction qui prend fooun nombre indéterminé de paramètres de type inconnu. Vous pouvez l'appeler comme

foo(2, 3); 

et comme

j = foo(p, -3, "hello world"); 

ans ainsi de suite (vous voyez l'idée); Seul l'appel avec les arguments appropriés fonctionnera (ce qui signifie que les autres produisent un comportement indéfini), mais c'est à vous de vous assurer de son exactitude. Le compilateur n'est pas obligé de diagnostiquer les incorrectes même s'il connaît magiquement les types de paramètres corrects et leur nombre total.

En fait, ce comportement est une fonctionnalité du langage C. Un dangereux, mais une caractéristique néanmoins. Il vous permet de faire quelque chose comme ça

void foo(int i); 
void bar(char *a, double b); 
void baz(void); 

int main() 
{ 
    void (*fn[])() = { foo, bar, baz }; 
    fn[0](5); 
    fn[1]("abc", 1.0); 
    fn[2](); 
} 

à savoir mélanger les différents types de fonctions dans un tableau « polymorphes » sans typecasts (types de fonction variadique ne peuvent pas être utilisés ici même). Encore une fois, les dangers inhérents à cette technique sont assez évidents (je ne me souviens pas l'avoir jamais utilisé, mais je peux imaginer où cela peut être utile), mais c'est C après tout.

Enfin, le bit qui relie la deuxième partie de la réponse à la première. Lorsque vous définissez une définition de fonction de type R K &, elle n'introduit pas de prototype pour la fonction. En ce qui concerne le type de fonction est concerné, votre définition func déclare func comme

int func(); 

dire ni les types, ni le nombre total de paramètres sont déclarés. Dans votre message original, vous dites "... il semble spécifier combien de paramètres il utilise ...".Formellement parlant, ce n'est pas! Après votre définition à deux paramètres K & R style func vous pouvez toujours appeler func comme

func(1, 2, 3, 4, "Hi!"); 

et il n'y aura pas une violation de contrainte en elle. (Normalement, un compilateur de qualité vous donnera un avertissement).

En outre, un surplombé parfois fait que

int f() 
{ 
    return 0; 
} 

est aussi un K & définition de la fonction R-style qui ne présente pas un prototype. Pour le rendre « moderne » que vous auriez à mettre un void explicite dans la liste des paramètres

int f(void) 
{ 
    return 0; 
} 

Enfin, contrairement à une croyance populaire, les deux K & définitions de fonction R-type et des déclarations de fonction non-prototypées sont entièrement supporté en C99. Le premier a été abandonné depuis C89/90, si je me souviens bien. C99 exige que la fonction soit déclarée avant la première utilisation, mais il n'est pas nécessaire que la déclaration soit un prototype. La confusion provient apparemment de la confusion terminologique populaire: beaucoup de gens appellent toute déclaration de fonction "un prototype", alors qu'en fait "déclaration de fonction" n'est pas la même chose que "prototype".

+6

très bien expliqué, esp. la partie "Fonction sans prototype". –

+1

Mais quelle est la différence entre une fonction varargs et une fonction non protégée? – Sebastian

+1

@Sebastian Godelet varargs sont incroyablement strictes. Par exemple, appeler une fonction vararg avec un int, et l'ouvrir en octets est invalide. Ainsi est la définition d'une fonction avec seulement les paramètres vararg. – YoYoYonnY

4

Il s'agit simplement d'une ancienne syntaxe antérieure à la syntaxe "ANSI C" que vous connaissez peut-être mieux. Il s'appelle généralement "K&R C".

Les compilateurs le supportent pour être complet, et pour être capable de gérer les anciennes bases de code, bien sûr.

17

Il s'agit d'une syntaxe R C assez ancienne (antérieurs à ANSI/ISO C). De nos jours, vous ne devriez plus l'utiliser (car vous avez déjà remarqué son inconvénient majeur: le compilateur ne vérifiera pas les types d'arguments pour vous). Le type d'argument par défaut est int dans votre exemple.

À l'époque, cette syntaxe a été utilisée, on trouve parfois des fonctions comme

foo(p, q) 
{ 
    return q + p; 
} 

qui était en fait une définition valable, comme les types de foo, p et q par défaut à int.

1

C'est une relique de quand C avait aucun prototype pour les fonctions. À l'époque, (je pense) les fonctions étaient supposées retourner int et tous ses arguments étaient supposés être int. Aucune vérification n'a été effectuée sur les paramètres de la fonction.

Vous êtes beaucoup mieux d'utiliser des prototypes de fonctions dans le langage C actuel.
Et vous devez les utiliser dans C99 (C89 accepte toujours l'ancienne syntaxe).

Et C99 nécessite des fonctions à déclarer (éventuellement sans prototype). Si vous écrivez une nouvelle fonction à partir de zéro, vous devez fournir une déclaration ... faites-en un prototype aussi: vous ne perdez rien et gagnez un contrôle supplémentaire du compilateur.

+3

Incorrect. Dans C99, les fonctions doivent être explicitement déclarées avant d'être appelées. Néanmoins, C99 ne nécessite pas de déclaration prorotypée. C99 prend entièrement en charge la syntaxe K & R avec un changement: la règle implicite 'int' a été supprimée. – AnT

+0

Vous avez raison, merci. Edited ma réponse. – pmg

1

Ceci est la syntaxe originale de K & R avant la normalisation de C en 1989. C89 a introduit des prototypes de fonctions, empruntés à C++, et a désapprouvé la syntaxe K & R.Il n'y a aucune raison de l'utiliser (et beaucoup de raisons de ne pas le faire) dans un nouveau code.

Questions connexes