web-dev-qa-db-fra.com

Est-il préférable d'utiliser les arguments C void "void foo (void)" ou non "void foo ()"?

Quoi de mieux: void foo() ou void foo(void)? Avec void, ça a l'air moche et incohérent, mais on m'a dit que c'était bon. Est-ce vrai?

Edit: Je sais que de vieux compilateurs font des choses bizarres, mais si j'utilise uniquement GCC, est-ce que void foo() Ok? Est-ce que foo(bar); sera alors accepté?

196
Zifre
void foo(void);

C’est la bonne façon de dire "pas de paramètres" en C, et cela fonctionne également en C++.

Mais:

void foo();

Cela signifie différentes choses en C et C++! En C, cela signifie "pourrait prendre un nombre quelconque de paramètres de types inconnus", et en C++, cela aurait la même signification que foo(void).

Les fonctions de liste d'arguments variables sont intrinsèquement non typées et doivent être évitées autant que possible.

233
Daniel Earwicker

Il existe deux manières de spécifier des paramètres dans C. L'une consiste à utiliser une liste d'identifiants et l'autre à utiliser une liste de types de paramètres. La liste d'identifiants peut être omise, mais la liste de types ne peut pas. Donc, pour dire qu'une fonction ne prend aucun argument dans une définition de fonction, vous le faites avec une liste d'identifiants (omis)

void f() {
    /* do something ... */
}

Et ceci avec une liste de types de paramètres:

void f(void) {
    /* do something ... */
}

Si, dans une liste de types de paramètres, le seul type de paramètre est void (il ne doit alors pas y avoir de nom), cela signifie que la fonction ne prend aucun argument. Mais ces deux manières de définir une fonction ont une différence quant à ce qu'elles déclarent.

Listes d'identifiants

La première définit que la fonction prend un nombre spécifique d'arguments, mais ni le compte, ni les types de ce qui est nécessaire - comme dans toutes les déclarations de fonction utilisant des listes d'identificateurs. L'appelant doit donc connaître les types et le nombre précisément à l'avance. Donc, si l'appelant appelle la fonction en lui donnant un argument, le comportement n'est pas défini. La pile pourrait par exemple être corrompue, car la fonction appelée s'attend à une présentation différente lorsqu'elle prend le contrôle.

L'utilisation de listes d'identifiants dans les paramètres de fonction est obsolète. Il était utilisé autrefois et est toujours présent dans de nombreux codes de production. Ils peuvent causer un grave danger en raison de ces promotions d'arguments (si le type d'argument promu ne correspond pas au type de paramètre de la définition de la fonction, le comportement n'est pas non plus défini!) Et est beaucoup moins sûr, bien sûr. Donc, utilisez toujours le void thingy pour les fonctions sans paramètres, à la fois dans les déclarations et définitions de fonctions uniquement.

Liste des types de paramètres

La seconde définit que la fonction ne prend aucun argument et la communique également - comme dans tous les cas où la fonction est déclarée à l'aide d'une liste de types de paramètres, appelée prototype. Si l'appelant appelle la fonction et lui donne un argument, c'est une erreur et le compilateur génère une erreur appropriée.

La deuxième façon de déclarer une fonction présente de nombreux avantages. Bien sûr, la quantité et les types de paramètres sont vérifiés. Une autre différence est que, comme le compilateur connaît les types de paramètres, il peut appliquer des conversions implicites des arguments au type des paramètres. Si aucune liste de types de paramètre n'est présente, cela ne peut pas être fait et les arguments sont convertis en types promus (appelé promotion par défaut). char deviendra int, par exemple, tandis que float deviendra double.

Type composite pour les fonctions

À propos, si un fichier contient à la fois une liste d'identifiants omis et une liste de types de paramètres, la liste de types de paramètres "gagne". Le type de la fonction à la fin contient un prototype:

void f();
void f(int a) {
    printf("%d", a);
}

// f has now a prototype. 

En effet, les deux déclarations ne disent rien de contradictoire. La seconde, cependant, avait quelque chose à dire en plus. Quel est cet argument est accepté. La même chose peut être faite en sens inverse

void f(a) 
  int a;
{ 
    printf("%d", a);
}

void f(int);

La première définit une fonction à l'aide d'une liste d'identifiants, la seconde en fournit ensuite un prototype, à l'aide d'une déclaration contenant une liste de types de paramètres.

93

void foo(void) est préférable car il dit explicitement: aucun paramètre n'est autorisé.

void foo() signifie que vous pouvez (sous certains compilateurs) envoyer des paramètres, du moins s'il s'agit de la déclaration de votre fonction plutôt que de sa définition.

18
Uri

C99 quotes

Cette réponse vise à citer et à expliquer les parties pertinentes du projet de norme C99 N1256 .

Définition du déclarant

Le terme déclarateur viendra souvent, comprenons-le.

D'après la grammaire linguistique, les caractères de soulignement suivants sont des déclarateurs:

int f(int x, int y);
    ^^^^^^^^^^^^^^^

int f(int x, int y) { return x + y; }
    ^^^^^^^^^^^^^^^

int f();
    ^^^

int f(x, y) int x; int y; { return x + y; }
    ^^^^^^^

Les déclarateurs font partie des déclarations de fonction et des définitions.

Il existe 2 types de déclarateurs:

  • liste de type de paramètre
  • liste d'identifiant

Liste des types de paramètres

Les déclarations ressemblent à:

int f(int x, int y);

Les définitions ressemblent à:

int f(int x, int y) { return x + y; }

C'est ce qu'on appelle la liste des types de paramètres car il faut donner le type de chaque paramètre.

Liste d'identifiants

Les définitions ressemblent à:

int f(x, y)
    int x;
    int y;
{ return x + y; }

Les déclarations ressemblent à:

int g();

Nous ne pouvons pas déclarer une fonction avec une liste d'identifiants non vide:

int g(x, y);

parce que 6.7.5.3 "Déclarateurs de fonctions (y compris les prototypes)" dit:

3 Une liste d'identifiants dans un déclarateur de fonction qui ne fait pas partie d'une définition de cette fonction doit être vide.

Il s’appelle liste d’identifiants car nous ne donnons que les identifiants x et y sur f(x, y), les types viennent après.

Cette méthode est ancienne et ne devrait plus être utilisée. 6.11.6 Déclarateurs de fonction dit:

1 L'utilisation de déclarateurs de fonction avec des parenthèses vides (et non des déclarateurs de type de paramètre au format prototype) est une fonctionnalité obsolète.

et le Introduction explique ce qu'est un fonctionnalité obsolète:

Certaines caractéristiques sont obsolètes, ce qui signifie qu'elles peuvent faire l'objet d'un retrait lors de futures révisions de la présente Norme internationale. Ils sont conservés en raison de leur utilisation répandue, mais leur utilisation dans les nouvelles implémentations (pour les fonctionnalités d'implémentation) ou les nouveaux programmes (pour le langage [6.11] ou les fonctionnalités de bibliothèque [7.26]) est déconseillée.

f () vs f(void) pour les déclarations

Quand vous écrivez juste:

void f();

c'est nécessairement une déclaration de liste d'identifiants, car 6.7.5 "Déclarateurs" dit définit la grammaire comme:

direct-declarator:
    [...]
    direct-declarator ( parameter-type-list )
    direct-declarator ( identifier-list_opt )

de sorte que seule la version de la liste d'identifiants peut être vide car elle est optionnelle (_opt).

direct-declarator Est le seul nœud de grammaire qui définit la partie entre parenthèses (...) Du déclarant.

Alors, comment pouvons-nous lever l’ambiguïté et utiliser la meilleure liste de types de paramètres sans paramètres? 6.7.5.3 Déclarateurs de fonctions (y compris les prototypes) dit:

10 Le cas particulier d'un paramètre non nommé de type void en tant qu'élément unique de la liste indique que la fonction n'a pas de paramètre.

Alors:

void f(void);

est la voie.

Ceci est une syntaxe magique explicitement autorisée, car nous ne pouvons pas utiliser un argument de type void:

void f(void v);
void f(int i, void);
void f(void, int);

Que peut-il se passer si j'utilise une déclaration f()?)

Peut-être que le code compilera parfaitement: 6.7.5.3 Déclarateurs de fonctions (y compris les prototypes):

14 La liste vide dans un déclarateur de fonction qui ne fait pas partie d'une définition de cette fonction spécifie qu'aucune information sur le nombre ou les types de paramètres n'est fournie.

Donc, vous pouvez vous en tirer avec:

void f();
void f(int x) {}

D'autres fois, UB peut ramper (et si vous avez de la chance, le compilateur vous le dira), et vous aurez du mal à comprendre pourquoi:

void f();
void f(float x) {}

Voir: Pourquoi une déclaration vide fonctionne-t-elle pour les définitions avec des arguments int mais pas pour les arguments float?

f () et f(void) pour les définitions

f() {}

contre

f(void) {}

sont similaires, mais pas identiques.

6.7.5.3 Déclarateurs de fonctions (y compris les prototypes) dit:

14 Une liste vide dans un déclarateur de fonction qui fait partie d'une définition de cette fonction spécifie que la fonction n'a pas de paramètre.

qui ressemble à la description de f(void).

Mais encore ... il semble que:

int f() { return 0; }
int main(void) { f(1); }

est conforme comportement indéfini, tandis que:

int f(void) { return 0; }
int main(void) { f(1); }

est non conforme comme discuté à: Pourquoi gcc permet-il aux arguments d'être transmis à une fonction définie d'être sans arguments?

TODO comprend exactement pourquoi. Doit être un prototype ou non. Définir le prototype.

Outre les différences syntaxiques, de nombreuses personnes préfèrent également utiliser void function(void) pour des raisons pratiques:

Si vous utilisez la fonction de recherche et souhaitez rechercher l'implémentation de celle-ci, vous pouvez rechercher function(void), et le prototype ainsi que l'implémentation seront renvoyés.

Si vous avez omis le second void, vous devez rechercher function() et donc également trouver tous les appels de fonctions, ce qui rend plus difficile la recherche de l'implémentation réelle.

3
maja

En C++, il y a non différence entre main() et main(void).

Mais en C, main() sera appelée avec un nombre quelconque de paramètres.

Exemple:

main ( ){
    main(10,"abc",12.28);
    //Works fine !
    //It won't give the error. The code will compile successfully.
    //(May cause Segmentation fault when run)
}

main(void) sera appelé sans aucun paramètre. Si nous essayons de passer, cela aboutira à une erreur du compilateur.

Exemple:

main (void) {
     main(10,"abc",12.13);
     //This throws "error: too many arguments to function ‘main’ "
}
1
Sandeep_black