web-dev-qa-db-fra.com

func () vs func (void) dans c99

void func() En pratique, un paramètre vide signifie que tout argument est accepté.

void func(void) n'accepte aucun argument.

Mais dans la norme C99, je trouve de telles lignes:

6.7.5.3 Déclarateurs de fonctions (y compris les prototypes)
14 Une liste d'identifiants ne déclare que les identifiants des paramètres de la fonction. Une liste vide dans un déclarant de fonction qui fait partie d'une définition de cette fonction spécifie que la fonction n'a pas de paramètres. La liste vide dans un déclarant 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.

selon la norme, func() et func(void) est la même chose?

59
anman

TL; DR

Dans les déclarations,

void func1();     // obsolescent
void func2(void);

le comportement est assez différent. Le premier déclare une fonction sans aucun prototype - et il peut prendre n'importe quel nombre d'arguments! Alors que ce dernier déclare une fonction avec un prototype, cela n'a pas de paramètres et n'accepte aucun argument.

Dans définitions

void func1() { }     // obsolescent

et

void func2(void) { }
  • Le premier déclare et définit une fonction func1 Qui n'a pas de paramètres et pas de prototype

  • Ce dernier déclare et définit une fonction func2 avec un prototype qui n'a pas de paramètres.

Ces deux se comportent distinctement en ce que, alors que le compilateur C doit afficher un message de diagnostic lors de l'appel d'une fonction prototypée avec un nombre incorrect d'arguments, il needn ' t le fait lors de l'appel d'une fonction sans prototype.

C'est-à-dire, étant donné les définitions ci-dessus

func1(1, 2, 3); // need not produce a diagnostic message
func2(1, 2, 3); // must always produce a diagnostic message 
                // as it is a constraint violation

Cependant les deux appels sont illégaux dans les programmes strictement conformes car ils sont un comportement explicitement indéfini selon 6.5.2.2p6 .

De plus, les parenthèses vides sont considérées comme une fonction obsolète:

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

et

L'utilisation de définitions de fonctions avec des identificateurs de paramètres et des listes de déclarations séparés (et non des déclarants de type de paramètres et de types de paramètres) est une fonction obsolète.

En détail

Il existe 2 concepts liés mais distincts: les paramètres et les arguments.

  • les arguments sont les valeurs transmises à la fonction.

  • les paramètres sont les noms/variables de la fonction qui sont définis sur les valeurs des arguments lorsque la fonction est entrée

Dans l'extrait suivant:

int foo(int n, char c) {
    ...
}

...

    foo(42, ch);

n et c sont des paramètres. 42 Et ch sont des arguments.

L'extrait cité ne concerne que les paramètres d'une fonction, mais ne mentionne rien sur le prototype ou les arguments de la fonction.


La déclaration void func1() signifie que la fonction func1 Peut être appelée avec n'importe quel nombre de arguments , c'est-à-dire qu'aucune information sur le nombre d'arguments n'est spécifiée (en tant que déclaration séparée, C99 le spécifie comme "fonction sans spécification de paramètre), tandis que le déclaration void func2(void) signifie que la fonction func2 n'accepte aucun argument .

La citation dans votre question signifie que dans une définition de fonction , void func1() et void func2(void) les deux signalent qu'il existe ne sont pas des paramètres , c'est-à-dire des noms de variables qui sont définis sur les valeurs des arguments lorsque la fonction est entrée. La void func() {} contraste avec void func(); la première déclare que func ne prend en effet aucun paramètre, tandis que la seconde est une déclaration pour une fonction func pour laquelle ni paramètres ni leurs types ne sont spécifiés (une déclaration sans prototype).

Cependant, ils diffèrent encore en termes de définition en ce que

  • La définition void func1() {} ne déclare pas de prototype, contrairement à void func2(void) {}, car () N'est pas une liste de types de paramètres, tandis que (void) Est un paramètre liste de types ( 6.7.5.3.1 ):

    Le cas particulier d'un paramètre sans nom de type void en tant que seul élément de la liste spécifie que la fonction n'a pas de paramètres.

    et plus loin 6.9.1.7

    Si le déclarant inclut une liste de types de paramètres, la liste spécifie également les types de tous les paramètres; un tel déclarant sert également de prototype de fonction pour les appels ultérieurs à la même fonction dans la même unité de traduction. Si le déclarant comprend une liste d'identificateurs, les types de paramètres doivent être déclarés dans une liste de déclarations suivante. Dans les deux cas, le type de chaque paramètre est ajusté comme décrit au 6.7.5.3 pour une liste de types de paramètres; le type résultant doit être un type d'objet.

    Le déclarant de la définition de fonction pour func1 Ne contient pas une liste de types de paramètres , et donc la fonction n'a pas de prototype.

  • void func1() { ... } peut toujours être appelé avec n'importe quel nombre d'arguments, alors que c'est une erreur de compilation d'appeler void func2(void) { ... } avec n'importe quel argument (6.5.2.2):

    Si l'expression qui dénote la fonction appelée a un type qui inclut un prototype , le nombre d'arguments doit correspondre au nombre de paramètres. Chaque argument doit avoir un type tel que sa valeur puisse être affectée à un objet avec la version non qualifiée du type de son paramètre correspondant.

    (c'est moi qui souligne)

    Il s'agit d'une contrainte , qui selon la norme indique qu'une implémentation conforme doit afficher au moins un message de diagnostic sur ce problème. Mais puisque func1 N'a pas de prototype, une implémentation conforme n'est pas requise pour produire des diagnostics.


Cependant, si le nombre d'arguments n'est pas égal au nombre de paramètres, le comportement n'est pas défini 6.5.2.2p6 :

Si l'expression qui dénote la fonction appelée a un type qui n'inclut pas de prototype , [...] Si le nombre d'arguments n'est pas égal au nombre de paramètres, le comportement n'est pas défini.

Donc, en théorie, un compilateur C99 conforme est également autorisé à l'erreur ou à diagnostiquer un avertissement dans ce cas. StoryTeller a fourni des preuves que clang pourrait diagnostiquer cela ; cependant, mon GCC ne semble pas le faire (et cela pourrait également être nécessaire pour qu'il soit compatible avec un ancien code obscur aussi):

void test() { }

void test2(void) { }

int main(void) {
    test(1, 2);
    test2(1, 2);
}

Lorsque le programme ci-dessus est compilé avec gcc -std=c99 test.c -Wall -Werror, La sortie est:

test.c: In function ‘main’:
test.c:7:5: error: too many arguments to function ‘test2’
     test2(1, 2);
     ^~~~~
test.c:3:6: note: declared here
 void test2(void) { }
      ^~~~~

Autrement dit, les arguments ne sont pas du tout vérifiés par rapport aux paramètres d'une fonction dont la déclaration en définition n'est pas prototypée (test) alors que GCC le considère comme une erreur au moment de la compilation pour spécifier les arguments d'une fonction prototypée ( test2); toute implémentation conforme doit diagnostiquer cela car il s'agit d'une violation de contrainte.

61
Antti Haapala

La partie importante de la citation est mise en évidence en gras ci-dessous:

6.7.5.3 Déclarateurs de fonctions (y compris les prototypes) 14 Une liste d'identifiants ne déclare que les identifiants des paramètres de la fonction. Une liste vide dans un déclarant de fonction qui est partie d'une définition de cette fonction spécifie que la fonction n'a pas de paramètres. La liste vide dans un déclarant de fonction qui est 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.

Ainsi, lorsque la liste des paramètres est vide pour une fonction avec son corps, ce sont les mêmes. Mais ce n'est qu'une déclaration d'une fonction.

void function1(); // No information about arguments
void function2(void); // Function with zero arguments

void function3() {
    // Zero arguments
}

void function4(void) {
    // Zero arguments
}
21
Mats

selon la norme, func () et func (void) est le même?

Non. func(void) dit que la fonction ne prend aucun argument du tout; tandis que func() dit que la fonction prend un nombre non spécifié d'arguments. Les deux sont valides mais le style func() est obsolète et ne doit pas être utilisé.

Il s'agit d'un artefact de C. pré-standard C99 marqué cela comme obsolète.

6.11.6 Déclarateurs de fonctions :

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

Depuis C11, il reste toujours obsolète et n'a pas été supprimé de la norme.

8
usr

La liste de paramètres vide à l'intérieur d'une définition de fonction signifie qu'elle n'inclut pas de prototype ni aucun paramètre.

C11 §6.9.1/7 Définitions des fonctions (l'accent est mis sur les citations en cours)

Le déclarant dans une définition de fonction spécifie le nom de la fonction en cours de définition et les identifiants de ses paramètres. Si le déclarant inclut une liste de types de paramètres , la liste spécifie également les types de tous les paramètres; un tel déclarant sert également de prototype de fonction pour les appels ultérieurs à la même fonction dans la même unité de traduction.

La question demande:

selon la norme, func() et func(void) est la même chose?

Non. La différence essentielle entre void func() et void func(void) réside dans leurs appels.

C11 §6.5.2.2/2 Appels de fonction (dans la section contraintes):

Si l'expression qui dénote la fonction appelée a un type qui inclut un prototype , le nombre d'arguments doit correspondre à le nombre de paramètres . Chaque argument doit avoir un type tel que sa valeur puisse être affectée à un objet avec la version non qualifiée du type de son paramètre correspondant.

Notez que les paramètres ≠ arguments. La fonction peut ne contenir aucun paramètre, mais elle peut avoir plusieurs arguments.

Comme la fonction définie avec des paramètres vides n'introduit pas de prototype, elle n'est pas vérifiée par rapport à ses appels, donc en théorie elle peut être fournie avec peu importe nombre d'arguments.

Cependant, c'est techniquement un comportement indéfini d'appeler une telle fonction avec au moins un argument (voir --- d'Antiti Haapala commentaires ).

C11 §6.5.2.2/6 Appels de fonction (dans la section sémantique):

Si le nombre d'arguments n'est pas égal au nombre de paramètres, le comportement n'est pas défini.

Par conséquent, la différence est subtile:

  • Lorsqu'une fonction est définie avec void, elle ne se compile pas lorsque le nombre d'arguments ne correspond pas aux paramètres (ainsi que leurs types), en raison d'une violation de constante (§6.5.2.2/2). Une telle situation nécessite un message de diagnostic du compilateur conforme.
  • S'il est défini avec des paramètres vides, il peut-être ou peut-être pas compiler (il n'y a pas d'exigence de message de diagnostic du compilateur conforme), mais c'est UB à = appel une telle fonction.

Exemple:

#include <stdio.h>

void func1(void) { puts("foo"); }
void func2()     { puts("foo"); }

int main(void)
{
    func1(1, 2); // constraint violation, it shouldn't compile
    func2(3, 4); // may or may not compile, UB when called
    return 0;
}

Notez que optimisation du compilateur peut couper les arguments dans ce cas. Par exemple, voici comment Clang compile le code ci-dessus (à l'exclusion de l'appel de func1) Avec -01 Sur x86-64 selon les conventions d'appel SysV ABI:

main:                                   # @main
        Push    rax          ; align stack to the 16-byte boundary
        call    func2        ; call func2 (no arguments given)
        xor     eax, eax     ; set zero as return value
        pop     rcx          ; restore previous stack position (RSP)
        ret
5