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

  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 : 53
    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 : 38
    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 ?

  7. #7
    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 : 38
    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
    Perso, avec gcc, j'utilise toujours -Wall -Wextra. J'en active des fois quelques autres supplémentaires mais je ne m'en souviens pas là comme ça, ils relèvent un peu du détails.

    Il peut-être utile d'activer -pedantic des fois, pour voir tout ce qui n'est pas purement conforme à la norme C. C'est surtout utile si tu veux du code portable par un autre compilateur.

    Pense aussi à bien choisir la norme suivie : -ansi ou -std=c99. Par défaut, gcc ne les suit pas : il utilise gnu90. Voir ici.

  8. #8
    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 : 53
    Localisation : France, Moselle (Lorraine)

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

    Informations forums :
    Inscription : Octobre 2011
    Messages : 898
    Par défaut
    Pour suivre les bons conseils de bktero : -Wall et -Wextra sont les flags minimum à mettre. Mais le tout n'est pas uniquement de mettre des flags, il faut aussi se préoccuper des warnings émis. Tous ne seront pas des signes d'erreurs, mais tu dois savoir pourquoi tu ignores un warning, éventuellement même le signaler en commentaire dans ton code
    Le lien vers la doc gcc de mon post précédant te montre qu'il existe beaucoup de flag pour activer ou désactiver les warnings.
    Ces flags permettent donc d'activer ou non les warnings pour tous les fichiers traités par la commande. Cependant il existe des situations où tu aimerais pouvoir choisir plus finement si un warning doit être émis ou non. Les compilateurs proposent des mécanismes pour le faire souvent par l'intermédiaire de directives pragmas (cf la doc gcc) ou parfois par un autre mécanisme (gcc permet de taire ou d'obliger certains warning aussi avec les attributs de fonctions ou de variables). Il s'agit là d'une utilisation avancée car ces mécanismes sont la plupart du temps incompatibles entre différents compilateurs
    Certains compilateurs sont plus clairs au niveau des messages émis que d'autres, si tu as l'occasion compare les messages émis par gcc et ceux émis par clang (on lit souvent que clang est plus clair).
    Bref, l'utilité de ces fameux flags sont dépendants à la fois du compilateur, du projet particulier, du type de portabilité souhaitée, et surtout de tes connaissances. Teste les si tu as le temps, c'est le meilleur moyen de comprendre le pourquoi du comment ... il y aurait tellement de choses à dire.

    Comme le signale bktero, les options que tu donnes à gcc ne concernent pas uniquement les diagnostiques. Il y a les options de sélection de la norme du langage (très personnellement je te recommande c99 dès qu'il est raisonnable de l'utiliser, mais c'est un avis très personnel ).
    Il y a aussi l'incontournable option -g en phase de développement. Cette option va te permettre de créer des fichiers qui contiendront des informations utilisées par un debugger. Savoir utiliser un debugger est une compétence indispensable qu'il ne faut surtout pas négliger.

    Je pourrais encore parler des options de profilage, les options d'optimisations, les options du linker, ... mais je sors du périmètre de ta question et au final la doc gcc le fait mieux que moi
    Je parle souvent de gcc (car c'est une des chaîne de compilation la plus répandue), mais il y a en a d'autre qu'il est parfois bon d'avoir vu au moins une fois comme le déjà cité clang, mais aussi icc le compilo d'intel, celui d'oracle oscc ... ils méritent d'être connus à défaut d'être utilisés sans oublier qu'un compilateur C est fortement lié à la bibliothèque standard, qu'il en existe aussi plusieurs, dans différente versions, ... finalement c'est super : un monde merveilleux s'ouvre à toi

    Bonne exploration !

  9. #9
    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 : 38
    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
    Pour changer, je plussoie ce que vient de dire kwariz.

    J'ai retrouvé un warning que j'avais découvert ici (il me semble que c'était par gangsoleil) : -Wwrite-strings.

    Exemple de son effet avec le code suivant, trouvé sur Stackoverflow :
    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>
    #include <string.h>
     
    void somefunc(char buffer[10]) {
        int i;
     
        for (i = 0;   i < 10;   i++)
           buffer[i] = 0;
    }
     
    int main(void) {
     
        somefunc("Literal");
        return 0;
    }
    Sans l'option, il compile sans erreur ou warning. Avec, on a un warning :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    D:\...\main.c|14|warning: passing argument 1 of 'somefunc' discards 'const' qualifier from pointer target type [enabled by default]|
    C'est bien car on va essayer d'écrire dans un string litteral ce qui va forcément pas bien marcher (doux euphémisme pour dire qu'il va y avoir une erreur de segmentation).

    Son effet est subtil et la documentation de gcc conseille de ne l'utiliser que si on est minutieux (mais j'ai pas encore trop vu pourquoi ^^) :
    -Wwrite-strings
    When compiling C, give string constants the type const char[length] so that copying the address of one into a non-const char * pointer produces a warning. These warnings help you find at compile time code that can try to write into a string constant, but only if you have been very careful about using const in declarations and prototypes. Otherwise, it is just a nuisance. This is why we did not make -Wall request these warnings.

  10. #10
    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 pour toutes vos réponses !!! plus je pose des question ici et plus je m'en pose d'autre
    bin je crois que je vais beaucou lire la doc

  11. #11
    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 : 53
    Localisation : France, Moselle (Lorraine)

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

    Informations forums :
    Inscription : Octobre 2011
    Messages : 898
    Par défaut
    Citation Envoyé par Bktero Voir le message
    Son effet est subtil et la documentation de gcc conseille de ne l'utiliser que si on est minutieux (mais j'ai pas encore trop vu pourquoi ^^) :
    Avec du retard ...
    Il faut être minutieux (rigoureux) dans le sens où pour que cette option émette des warnings utiles (sans plein de faux positifs) il faut que tu qualifie tous tes char * de const quand c'est le cas. Par exemple dans la fonction :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void print_string(char *str)
    {
      printf("string : '%s'\n", str);
    }
    le paramètre devrait être constifié puisque la chaine pointée n'est jamais modifiée. Mais si tu utilises -Wwrite-string et que tu écris print_string("Hello world"); alors un warning est émis puisque les chaînes littérales sont de type const char * et que le paramètre est de type char * : on t'averti que tu passes outre un const.
    Et la base de code C n'est pas forcément const correcte ...

  12. #12
    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 : 38
    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
    Merci pour la précision

    C'est vrai que j'ai tendance à ne mettre const que très rarement dans les prototypes des fonctions, pourtant, on devrait presque toujours très souvent le mettre.

    PS : et un petit article pour résumer tout ça. Prêt à être dégainé pour un prochain post !

  13. #13
    Membre très actif

    Femme Profil pro
    Collégien
    Inscrit en
    Juillet 2010
    Messages
    592
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : Afghanistan

    Informations professionnelles :
    Activité : Collégien

    Informations forums :
    Inscription : Juillet 2010
    Messages : 592
    Par défaut
    Bonjour,

    Personnellement j'use et j'abuse du const.
    Cela permet de détecter 80 % de mes erreurs d'étourderie, et cela donne plus d'informations au compilo pour optimiser le code. Prochaine étape user et abuser de restrict.

    Souvent je me suis rendu compte que le code fonctionne en -O0 mais en mode release -O2 ce n’était plus le cas. On se dit : " C'est quoi ce compilo bugger!!".
    En fait le bug se trouve toujours entre la chaise et l'écran...
    Du coup je debug en mode Release avec des traces.
    Ça marche Bien!!

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