Double Possible:
est f(void) obsolète moderne en C et C++Ok, donc j'ai entendu des opinions différentes sur ce sujet et je veux juste m'assurer que je comprends bien.
Pour Le C++
Les déclarations
void f();
etvoid f(void);
signifie exactement la même chose, la fonctionf
ne prend aucun paramètre. Idem pour les définitions.Pour C
Déclaration
void f(void);
signifie qu'f
ne prend aucun paramètre.Déclaration
void f();
signifie que la fonctionf
peut ou peut ne pas avoir de paramètres, et si elle le fait, nous ne savons pas ce genre de paramètres, ceux-ci sont, ou combien il y en a des. Notez qu'il n'est PAS le même que les points de suspension, nous ne pouvons pas utiliserva_list
.Maintenant, voici où les choses deviennent intéressantes.
Cas 1
Déclaration:
void f();
Définition:
void f(int a, int b, float c) { //... }
Cas 2
Déclaration:
void f();
Définition:
void f() { //... }
Question:
Ce qui se passe au moment de la compilation dans les cas 1 et 2 lorsque nous appelons
f
avec les bons arguments, les faux arguments et pas des arguments à tous? Ce qui se passe au moment de l'exécution?Question supplémentaire:
Si je déclare
f
avec des arguments, mais la définir sans eux, ça va faire une différence? Dois-je être en mesure de répondre aux arguments de la fonction du corps?
Réponses
Trop de publicités?Plus de la terminologie (C, pas du C++): un prototype d'une fonction déclare les types de ses arguments. Sinon, la fonction n'a pas un prototype.
void f(); // Declaration, but not a prototype
void f(void); // Declaration and prototype
void f(int a, int b, float c); // Declaration and protoype
Des déclarations qui ne sont pas des prototypes sont des rescapés de pré-ANSI C, de l'époque de K&R C. La seule raison d'utiliser un vieux de la déclaration de style est de maintenir la compatibilité binaire avec l'ancien code. Par exemple, dans Gtk 2, il y a une déclaration de fonction sans un prototype -- il est là par hasard, mais il ne peut pas être enlevé sans casser les fichiers binaires. Le standard C99 commentaires:
6.11.6 Fonction declarators
L'utilisation de la fonction declarators avec des parenthèses vides (pas de prototype-paramètre de format de type declarators) est la vétusté des fonctionnalité.
Recommandation: je suggère de compilation de code C dans GCC/Clang avec -Wstrict-prototypes
et -Wmissing-prototypes
, en plus de l'habituel -Wall -Wextra
.
Ce qui se passe
void f(); // declaration
void f(int a, int b, float c) { } // ERROR
La déclaration n'est pas d'accord avec le corps de la fonction! C'est en fait une compilation d'erreur, et c'est parce que vous ne pouvez pas avoir un float
argument dans une fonction sans un prototype. La raison pour laquelle vous ne pouvez pas utiliser un float
dans un unprototyped fonction est parce que quand vous appelez cette fonction, tous les arguments obtenir une promotion à l'aide de certaines défaut des promotions. Voici un fixe exemple:
void f();
void g()
{
char a;
int b;
float c;
f(a, b, c);
}
Dans ce programme, a
est promu int
1 et c
est promu double
. Ainsi, la définition de f()
doit être:
void f(int a, int b, double c)
{
...
}
Voir C99 6.7.6 paragraphe 15,
Si un type a un paramètre de type liste et l'autre type est spécifié par un la fonction de demande de déclaration qui ne fait pas partie de la définition d'une fonction et qui contient un vide identificateur de la liste, la liste des paramètres n'ont pas des points de suspension terminator et le type de chaque paramètre qui doit être compatible avec le type qui résulte de l'application de la l'argument par défaut des promotions.
Réponse 1
Ce qui se passe au moment de la compilation dans les cas 1 et 2 lorsque nous appelons
f
avec les bons arguments, les faux arguments et pas des arguments à tous? Ce qui se passe au moment de l'exécution?
Lorsque vous appelez f()
, les paramètres à obtenir une promotion à l'aide de la valeur par défaut des promotions. Si le promu types de correspondre à la réelle types de paramètres pour l' f()
, alors tout est bon. Si elles ne correspondent pas, il sera probablement de la compilation, mais vous aurez certainement obtenir un comportement indéfini.
"Un comportement indéfini" est spec-parler de "nous ne faisons aucune garantie sur ce qui va arriver." Peut-être que votre programme va planter, peut-être que cela fonctionnera très bien, peut-être qu'il va inviter vos beaux-parents pour le dîner.
Il y a deux façons d'obtenir le diagnostic au moment de la compilation. Si vous avez un système sophistiqué de compilateur avec la croix-module d'analyse statique capacités, alors vous obtiendrez probablement un message d'erreur. Vous pouvez également obtenir des messages d'onu-prototypée les déclarations de fonction avec GCC, à l'aide de -Wstrict-prototypes
-- qui, je vous recommandons de tourner dans tous vos projets (sauf pour les fichiers qui utilisent Gtk 2).
Réponse 2
Si je déclare
f
avec des arguments, mais la définir sans eux, ça va faire une différence? Dois-je être en mesure de répondre aux arguments de la fonction du corps?
Il ne devrait pas compiler.
Exceptions
Il y a en fait deux cas dans lesquels les arguments de la fonction sont autorisés à être en désaccord avec la définition de la fonction.
Il est bon de laisser
char *
à une fonction qui attendvoid *
, et vice versa.Il est bon de laisser un entier signé de type à une fonction qui attend la version non signée de ce type, ou vice versa, tant que la valeur est représentable dans les deux types (c'est à dire, il n'est pas négatif, et pas hors de portée du type signé).
Notes de bas de page
1: Il est possible qu' char
favorise unsigned int
, mais c'est très rare.
C11 section 6.7.6.3 Function declarators /14
couvre ce:
Une liste vide dans une fonction de demande de déclaration qui fait partie d'une définition de cette fonction indique que la fonction n'a pas de paramètres. La liste vide dans une fonction de demande de déclaration qui ne fait pas partie d'une définition de cette fonction spécifie qu' aucune information sur le nombre ou le type des paramètres est fournie.
Fondamentalement, ce que cela signifie est que si vous nous les fournissez pas de paramètres lors de la définition de la fonction notamment avec:
int f() { return 7; }
puis c'est la même chose que si vous avez dit:
int f(void) { return 7; }
Toutefois, si vous le faites sans définition de la fonction, tels que:
int f();
ensuite, l'information n'est pas encore disponible.
Donc:
int f();
int f(int a) { return a; }
n'est pas une erreur.
Vous pouvez les voir dans la transcription suivante:
pax> cat qq.c
#include <stdio.h>
#include <stdlib.h>
int f();
int f(int x) {
return x;
}
int main (int argc, char *argv[]) {
printf ("%d\n", f(atoi(argv[1])));
return 0;
}
pax> gcc -Wall --std=c99 -o qq qq.c
pax> ./qq 42
42
Pour C++ f()
et f(void)
moyenne de la même - la fonction ne prenant aucun paramètre.
Pour C, f(void)
signifie que la fonction ne prenant aucun paramètre, mais f()
moyen de la fonction de prise nombre quelconque de paramètres - à-dire (voir l'exemple ci-dessous) que l'appelant code peut passer pour fonction de ce qu'il veut - mais la fonction prend un strict arguments - c'est donc un très faible méthode de déclarer les fonctions - mais au K&R de l'époque c'était la seule méthode.
L' exemple montre ce C f()
dans la pratique et ses conséquences:
#include <stdio.h>
void f();
int main() {
f(); /* prints some garbage */
f(16); /* prints 16 */
f(17,0); /* prints 17 */
return 0;
}
void f(a1)
int a1;
{
printf("%d\n", a1);
}
Dans la pure C, il en résulte dans l'erreur: error C2084: function 'void __cdecl f(void )' already has a body
void f(void);
void f( );
int main() {
f(10);
f(10.10);
f("ten");
return 0;
}
void f(void) {
}
void f( ) {
}
.
fvoid.c line(19) : error C2084: function 'void __cdecl f(void )' already has a body
Mais en C++ Pur, de compiler sans erreurs.
La surcharge de fonctions C++ uniquement, C n'a pas de surcharge)
Vous surchargez le nom d'une fonction f en déclarant plus d'une fonction avec le nom f dans le même champ d'application. Les déclarations de f doit diffèrent les uns des autres par le type et/ou le nombre d'arguments dans la liste d'arguments. Lorsque vous appelez une fonction surchargée nommé f, la fonction correcte est sélectionnée par la comparaison de la liste d'arguments de l'appel de la fonction avec le paramètre de la liste de chaque de la surcharge de fonctions candidates avec le nom f.
exemple:
#include <iostream>
using namespace std;
void f(int i);
void f(double f);
void f(char* c);
int main() {
f(10);
f(10.10);
f("ten");
return 0;
}
void f(int i) {
cout << " Here is int " << i << endl;
}
void f(double f) {
cout << " Here is float " << f << endl;
}
void f(char* c) {
cout << " Here is char* " << c << endl;
}
sortie:
Here is int 10
Here is float 10.1
Here is char* ten