IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C Discussion :

différences entre void fontion() et void fonction(void)


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Femme Profil pro
    Étudiant
    Inscrit en
    Mai 2013
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 35
    Localisation : France, Morbihan (Bretagne)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Enseignement

    Informations forums :
    Inscription : Mai 2013
    Messages : 15
    Par défaut différences entre void fontion() et void fonction(void)
    Salut les gurus du C

    j'ai un peu besoin de vos lumières car dans une revu de code avec un prof il nous a signalé que c'est pas bien d'avoir dans les headers un prototype comme :
    mais qui fallait utiliser à la place
    .
    Tout se qu'il m'a expliqué c'est que la première forme ne dit pas que la fonction a pas de paramètres alors que l'autre oui et que c'est comme ça qui fallait faire. Pas plus d’explications sauf que je devais googler pour savoir plus si je voulais . Alors j'ai googler pis je suis paumé
    J'ai trouvé que c'est du à cause d'une ancienne norme qui autorisait ça et que ça traine toujours pour rester compatible avec, que c'est pas la même chose ne c++ où c'est () et (void) c'est pareil
    Alors je me pose la question de savoir pourquoi c'est gardé et à quoi ça sert au final ? J'ai pas compris non plus les problèmes si j'oublie void et pourquoi j'ai pas de warnings même quand je compile avec les -wall et -wextra ?
    J'ai pas compris à quoi ça correspond quand j'ai lu que la première forme c'est pour une fonction qui prend un nombre fixe de paramètres alors qu'en en déclare pas

  2. #2
    Membre Expert
    Avatar de kwariz
    Homme Profil pro
    Chef de projet en SSII
    Inscrit en
    Octobre 2011
    Messages
    898
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Chef de projet en SSII
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2011
    Messages : 898
    Par défaut
    Bonsoir,

    Il faut faire la différence entre la déclaration d'une fonction (avertir le compilateur qu'une fonction va être définie ici ou ailleurs mais qu'elle existe), et la déclaration la définition d'une fonction (donner le corps de la fonction pour que le compilateur puisse la compiler).

    Tu peux déclarer une fonction de plusieurs manières. Les dernières normes (C99 et C11) n'imposent pas mais recommandent d'utiliser une liste de paramètres lors de la déclaration : c'est-à-dire de donner au moins le type des paramètres. Je parle de recommandation dans le sens où il est précisé au §6.11.6
    Citation Envoyé par Draft n1570 C11
    The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature.
    => une traduction rapide et simple : c'est mieux de ne plus utiliser cette caractéristique lorsqu'on définit une fonction.
    Tu trouves au §6.7.6.3.10
    Citation Envoyé par Draft n1570 C11
    The special case of an unnamed parameter of type void as the only item in the list specifies that the function has no parameters.
    => si une fonction n'a pas besoin de paramètres on le signale avec une liste de paramètres contenant juste void, remarque qu'on le fait à la définition et à la déclaration.
    Le texte de la norme est disponible dans les versions de travail et si tu t'y intéresses sont toujours à garder à portée de clic => www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf‎
    C'est la première bonne raison pour ne pas utiliser les parenthèses vides à la déclaration d'une fonction.
    Auparavant il était possible d'utiliser void function(); pour dire «function est une fonction ne retournant rien et qui aura un nombre fixe de paramètres mais on verra ça par la suite lors de la définition». Le problème avec cette approche est que le compilateur ne connait pas avant la définition le nombre de paramètres ni leurs types. Cela implique que c'est à ta charge de bien l'utiliser.

    Passons aux warnings ... Si tu utilises -Wall -Wextra alors tu as un warning si tu utilises une fonction entre sa définition et sa déclaration, un warning qui devrait ressembler à warning: call to function ‘f’ without a real prototype [-Wunprototyped-calls]. Si tu l'utilises après sa déclaration le compilateur est au courant de tout et ne râlera plus.Si tu veux un warning à chaque fois il existe l'option -Wold-style-definition qui te donnera un message ressemblant à warning: old-style function definition [-Wold-style-definition].
    À nouveau la lecture de la doc aide beaucoup : La liste des warnings acceptés par GCC (effectue une recherche sur prototype dans cette page par exemple).

    À la question si c'est encore utile je dirais non pas vraiment. C'est dangereux de l'utiliser exprès car ce sera à toi de ne pas te planter et le compilateur ne sera que peu utile pour t'aider à trouver les erreurs. Le seul moment où ça peut être utile est lorsque tu veux utiliser un tableau de fonction hétérogènes (qui n'ont pas le même prototype mais le même type de retour), par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    #include <stdio.h>
     
    void f_string(const char* str);
    void f_int(int i);
    void f_both(const char* str, int i);
     
    void f_string(const char* str)
    {
      printf("f_string : '%s'\n", str);
    }
     
    void f_int(int i)
    {
      printf("f_int    : %d\n", i);
    }
     
    void f_both(const char* str, int i)
    {
      printf("f_both   : '%s' & %d\n", str, i);
    }
     
    int main(void)
    {
      void (*funcs[])() = {f_string, f_int, f_both};
     
      funcs[0]("Hello");
      funcs[1](1);
      funcs[2]("Bye", 2);
     
      return 0;
    }
    Ici funcs sera un tableau de pointeurs de fonctions qui retournent void et qui «acceptent des paramètres en nombre fixe». Je ne recommande pas cette utilisation.
    Typiquement quand on définit une API et que l'on a besoin d'un paramètre qui est une fonction alors on ajoute à la liste des paramètres de cette fonction un paramètre de type void * qui peut contenir à loisir une struct regroupant tous les paramètres utiles, ou un tableau, ou ...
    Cela permet d'avoir une vérification des types un peu plus forte (maintenant on peut toujours faire n'importe quoi avec le paramètre void* mais c'est une autre histoire).
    Remarque que si tu utilises -Wold-style-definition quand tu compiles, et que tu déclares int main() {...} alors tu te retrouves avec un warning. Il faut que je perde l'habitude de le définir ainsi, pff en plus c'est bien écrit dans la norme au §5.1.2.2.1.

    Pour la partie c++ je ne pourrais pas t'en dire plus et si malgré tes recherches tu as toujours des questions le forum c++ sera plus adapté. Au final, C et C++ sont deux langages différents et C n'est pas un sous-ensemble de C++.

  3. #3
    Membre émérite Avatar de orfix
    Homme Profil pro
    Inscrit en
    Avril 2007
    Messages
    707
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Maroc

    Informations professionnelles :
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Avril 2007
    Messages : 707
    Par défaut
    Citation Envoyé par joliette
    Alors je me pose la question de savoir pourquoi c'est gardé et à quoi ça sert au final ? J'ai pas compris non plus les problèmes si j'oublie void et pourquoi j'ai pas de warnings même quand je compile avec les -wall et -wextra ?
    Ne pas spécifier le nombre/type des arguments c'est demander au compilo de ne faire aucune vérification sur ces derniers lors des appels. En voici un exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #include  <stdio.h>
    #include  <stdlib.h>
     
    void foo();
     
    int main(void)
    {
    	foo(3.14159265359);
     
    	return 0;
    }
     
    void foo(char *s) 
    {
    	puts(s);
    }
    La compilation se passera sans problème, mais tu recevras une erreur de segmentation lors de l'exécution, si tu utilises GCC rajoute l'option -Wstrict-prototypes à ta ligne de commande:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    $ gcc -Wall -Wextra -Wstrict-prototypes joliette.c
    joliette.c:4: warning: function declaration isn’t a prototype
    $ ./a.out 
    Erreur de segmentation

  4. #4
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    @orfix : en compilant avec -Wall -Wextra, on a une erreur de segmentation. L'erreur de segmentation ne vient pas de la "mauvaise" déclaration de la fonction mais de sa mauvaise utilisation ! 3.14159265359 n'est (très probablement) pas une adresse valide, d'où l'erreur de segmentation.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #include  <stdio.h>
    #include  <stdlib.h>
     
    void foo();
     
    int main(void)
    {
    	const char * str = "ca marche !";
    	foo(str);
     
    	return 0;
    }
     
    void foo(char *s) 
    {
    	puts(s);
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    gcc -Wall -Wextra dvpez-void.c  && ./a.out 
    ca marche !
    L'option supplémentaire -Wstrict-prototypes avertit quand à une déclaration sans prototype complet mais n'est en aucun cas liée à l'erreur de segmentation.

    Est-ce bien cela que tu voulais montrer ici ?

    De plus, ce n'est pas exactement lui demander de ne pas vérifier mais de ne pas lui donner toutes les informations pour vérifier. Si la fonction est utilisée entre déclaration et définition, il ne regarde pas le prototype complet effectivement ; mais si la définition est connue, il devient regardant sur les types de paramètres passés.

  5. #5
    Membre émérite Avatar de orfix
    Homme Profil pro
    Inscrit en
    Avril 2007
    Messages
    707
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Maroc

    Informations professionnelles :
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Avril 2007
    Messages : 707
    Par défaut
    Citation Envoyé par Bktero Voir le message
    L'erreur de segmentation ne vient pas de la "mauvaise" déclaration de la fonction mais de sa mauvaise utilisation !
    ...
    L'option supplémentaire -Wstrict-prototypes avertit quand à une déclaration sans prototype complet mais n'est en aucun cas liée à l'erreur de segmentation.
    Exact, c'est justement ce que je voulais démontrer... si le prototype de la fonction avait été complet void foo(char *s); le compilateur aurait lancé un Warning expected ‘char *’ but argument is of type ‘double et cette erreur d'utilisation ne serait pas passée inaperçu...

    Citation Envoyé par Bktero Voir le message
    Si la fonction est utilisée entre déclaration et définition, il ne regarde pas le prototype complet effectivement ; mais si la définition est connue, il devient regardant sur les types de paramètres passés.
    Il ne devient regardant sur les types/nomre de paramètres passés que si le prototype complet de la dite fonction apparaît avant sa première utilisation.

  6. #6
    Membre averti
    Femme Profil pro
    Étudiant
    Inscrit en
    Mai 2013
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 35
    Localisation : France, Morbihan (Bretagne)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Enseignement

    Informations forums :
    Inscription : Mai 2013
    Messages : 15
    Par défaut
    Merci à vous pour vos super réponses. Tout ça est plus clair maintenant

    Quel options de warnings sont le plus utiles quand on développe ?

Discussions similaires

  1. Réponses: 2
    Dernier message: 11/01/2014, 16h57
  2. Différence entre "((void *) 0)" et "(void *)0"
    Par skeleton18 dans le forum Débuter
    Réponses: 1
    Dernier message: 10/04/2012, 18h16
  3. Réponses: 4
    Dernier message: 11/08/2008, 14h49
  4. différence entre fonction() et fonction(void)
    Par ram-0000 dans le forum C++
    Réponses: 8
    Dernier message: 07/11/2007, 17h31
  5. utiliser une fonction VOID pour des blocs relationnels ?
    Par ctobini dans le forum PostgreSQL
    Réponses: 3
    Dernier message: 11/09/2006, 16h16

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo