74 votes

Pourquoi gcc permet-il aux arguments d'être transmis à une fonction définie d'être sans arguments?

Je ne comprends pas pourquoi ce code est compilé?

 #include <stdio.h>
void foo() {
    printf("Hello\n");
}

int main() {
    const char *str = "bar";
    foo(str);
    return 0;
}
 

gcc ne lance même pas un avertissement indiquant que je transmets trop d'arguments à foo (). Est-ce comportement attendu?

84voto

ecatmur Points 64173

En C, une fonction déclarée avec un vide de la liste des paramètres accepte un nombre arbitraire d'arguments lors de l'appelé, qui sont soumis à l'habitude de l'arithmétique des promotions. Il est de la responsabilité de l'appelant à s'assurer que les arguments fournis sont appropriés pour la définition de la fonction.

Pour déclarer une fonction prenant des arguments zéro, vous devez écrire void foo(void);.

C'est pour des raisons historiques; à l'origine, les fonctions C de ne pas avoir des prototypes, comme C évolué à partir de B, sans type de langue. Lorsque les prototypes ont été ajoutés, l'original sans type déclarations ont été laissés dans la langue pour assurer la compatibilité ascendante.

Pour obtenir gcc afin de les avertir vide listes de paramètres, utilisez -Wstrict-prototypes:

Avertir si une fonction est déclarée ou définie, sans préciser les types d'argument. (Une ancienne définition de fonction est permise sans un avertissement s'il est précédé par une déclaration qui précise les types d'argument.)

53voto

Eric Postpischil Points 36641

Pour des raisons d'héritage, la déclaration d'une fonction avec () pour une liste de paramètres qui signifie essentiellement "comprendre les paramètres lors de l'appel de la fonction". Pour spécifier qu'une fonction n'a pas de paramètres, utilisez (void).

Edit: j'ai l'impression que je suis en profitant de la réputation dans ce problème de la vieillesse. Juste pour vous, les enfants savent ce que la programmation utilisé pour être comme, ici, c'est mon premier programme. (Pas de C; il vous montre ce que nous avons eu à travailler avec avant que.)

10voto

ouah Points 75311
void foo() {
    printf("Hello\n");
}

foo(str);

en C, ce code ne viole une contrainte (il le serait, si elle a été définie dans son prototype, avec void foo(void) {/*...*/}) et comme il n'y a pas de violation de contrainte, le compilateur n'est pas tenu d'émettre un diagnostic.

Mais ce programme a un comportement indéfini selon le C suivant les règles:

De:

(C99, 6.9.1p7) "Si la déclaration comprend un paramètre de type liste, la liste indique également les types de tous les paramètres; une telle déclaration sert également comme un prototype de fonction pour les appels ultérieurs à la même fonction dans la même unité de traduction. Si la déclaration comprend une liste d'identifiants,142) les types des paramètres doit être déclaré dans un déclaration suivante de la liste."

l' foo fonction n'est pas de fournir un prototype.

De:

(C99, 6.5.2.2p6) "Si l'expression qui désigne la fonction appelée a un type qui ne comprend pas un prototype [...] Si le nombre d'arguments n'est pas égal au nombre de paramètres, le comportement est indéfini."

l' foo(str) appel de fonction est un comportement indéfini.

C n'a pas de mandat la mise en œuvre d'émettre un diagnostic pour un programme qui invoque un comportement indéfini, mais votre programme est encore une erreur de programme.

6voto

Steve Siegel Points 170

À la fois le Standard C99 (6.7.5.3) et la Norme C11 (6.7.6.3) état:

Une liste d'identifiants déclare seulement les identifiants des paramètres de la fonction. Une liste vide dans une fonction de demande de déclaration qui est de la partie d'une définition de cette fonction indique que la fonction n'a pas de les paramètres. La liste vide dans une fonction de demande de déclaration qui n'est pas partie d'une définition de cette fonction spécifie qu'aucun d'informations sur le nombre ou le type des paramètres est fournie.

Depuis la déclaration de foo est une partie de la définition, de la déclaration spécifie que les foo prend 0 arguments, donc, l'appel foo(str) est à moins moralement mauvais. Mais comme décrit ci-dessous, il existe différents degrés de "mauvais" dans C, et les compilateurs peuvent diffèrent dans la façon dont ils traitent avec certains types de "mauvais".

Pour prendre un peu plus simple exemple, considérons le programme suivant:

int f() { return 9; }
int main() {
  return f(1);
}

Si je compile le dessus à l'aide de Clang:

tmp$ cc tmp3.c
tmp3.c:4:13: warning: too many arguments in call to 'f'
  return f(1);
         ~  ^
1 warning generated.

Si je compile avec gcc 4.8 je n'ai pas d'erreurs ou d'avertissements, même avec le Mur. Une réponse précédente a suggéré d'utiliser -Wstrict-prototypes, ce qui indique correctement que la définition de f n'est pas sous forme de prototype, mais ce n'est vraiment pas le point. La Norme(s) de permettre la définition d'une fonction dans une non-forme de prototype tel que celui ci-dessus et les Normes clairement que cette définition indique que la fonction prend 0 arguments.

Maintenant, il y a une contrainte (C11 Sec. 6.5.2.2):

Si l'expression qui désigne la fonction appelée a un type qui comprend un prototype, le nombre d'arguments est d'accord avec le nombre de paramètres.

Cependant, cette contrainte ne s'applique pas dans ce cas, puisque le type de la fonction ne pas inclure un prototype. Mais ici, c'est un rapport suivant dans la sémantique de l'article (et pas une "contrainte"), qui ne s'appliquent:

Si l'expression qui désigne la fonction appelée a un type qui ne comprend pas un prototype ... Si le nombre d'arguments n'est pas égal au nombre de paramètres, le comportement est indéfini.

D'où l'appel de fonction n'entraîner un comportement non défini (c'est à dire, le programme n'est pas "strictement conforme"). Cependant, la Norme ne nécessite une mise en œuvre de signaler un message de diagnostic quand une réelle contrainte est violée, et dans ce cas, il n'y a pas de violation d'une contrainte. Donc gcc n'est pas nécessaire de signaler une erreur ou un avertissement pour être "conforme mise en œuvre".

Donc, je pense que la réponse à la question, pourquoi ne gcc permet-il?, est que gcc n'est pas nécessaire de déclarer quoi que ce soit, puisque ce n'est pas une violation de contrainte. En outre gcc n'a pas la prétention de faire rapport à chaque type de comportement indéfini, même avec un Mur ou -Wpedantic. C'est un comportement indéfini, ce qui signifie la mise en œuvre peut choisir la façon de traiter avec elle, et la gcc a choisi de le compiler sans avertissements (et apparemment, il ignore l'argument).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X