52 votes

Comprendre la différence entre f() et f(void) en C et C++ une fois pour toutes

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(); et void f(void); signifie exactement la même chose, la fonction f 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 fonction f 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 utiliser va_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?

61voto

Dietrich Epp Points 72865

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 int1 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.

  1. Il est bon de laisser char * à une fonction qui attend void *, et vice versa.

  2. 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.

7voto

paxdiablo Points 341644

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

3voto

rs2012 Points 21

En C++, f() et f(void) est la même

En C, ils sont différents et n'importe quel nombre d'arguments peuvent être passés alors que l'appel de fonction f (), mais aucun argument peut être passé dans f(void)

0voto

PiotrNycz Points 8145

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);
}

-2voto

Software_Developer Points 3082

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

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