Skip to content
Tags

,

void ou rien ?

12 avril 2017

Pour définir une fonction en C ou une méthode en Java, on écrit dans l’ordre :

  • son type de retour (c’est-à-dire le type de la valeur qu’elle va renvoyer),
  • l’identificateur de la fonction,
  • des parenthèses, avec entre elles la liste des types et noms des paramètres formels (séparés par des virgules),
  • un bloc entre accolades qui contient les instructions formant le corps de la fonction.

Exemple :

double distance(int x1, int y1, int x2, int y2)
{
        return sqrt((x2−x1)*(x2−x1) + (y2−y1)*(y2−y1));
}

void en type de retour

Normalement, si la fonction ne renvoie pas de valeur, il faut l’indiquer en écrivant à la place du type de retour le mot clé void.
Exemple :

void afficherPrix(double prix)
{
        printf("%.2f DA", prix); // ou System.out.printf en Java
}

La seule exception est en Java et concerne les constructeurs, qui ne sont pas des méthodes (d’après la documentation officielle): ils ne doivent rien renvoyer, mais il ne faut pas mettre void avant leur nom (sinon le compilateur les considère comme des méthodes).

Mais que se passe-t-il si l’on ne met ni type de retour ni void à la place ?

En Java

La réponse, simple, est donnée par le compilateur :

error: invalid method declaration; return type required

erreur : déclaration de méthode invalide ; type de retour obligatoire

En C

Bizarrement, cela ne provoque pas d’erreur (du moins, pas avec le compilateur GCC) mais un avertissement (warning) :

warning: return type defaults to ‘int’ [-Wimplicit-int]

ce qui veut dire « le type de retour ‘int’ a été pris par défaut ». C’est ce qui s’appelle un int implicite.

Le int implicite existait dans les anciennes versions du C. Il a été supprimé des nouvelles normes C99 et C11 mais les compilateurs l’ont gardé pour assurer la compatibilité avec les anciens programmes. Il s’agit d’une règle étrange qui indique que si une variable ou une fonction est déclarée sans type, le compilateur considère implicitement que son type est int. Regardez par exemple ce programme en C :

#include <stdio.h>
n = 42;
int main(void)
{
        printf("%d\n", n);
}

Vous croyiez qu’il provoquerait une erreur de compilation ? Eh bien non ! tout juste un avertissement comme celui de tout à l’heure. Le compilateur considère que le type de la variable est int, implicitement.

C’est la même chose pour les fonctions :

#include <stdio.h>

somme(int a, int b) {
        return a+b;
}

void main(void) {
        printf("%d\n", somme(27,15));
}

La fonction somme fonctionne très bien, elle renvoie un int. Le compilateur donne un fichier exécutable mais il vous avertit car ce n’est pas normal : peut-être que vous avez oublié le type de retour et que ce ne devait pas être int !

Autrefois certains programmeurs se permettaient probablement de ne pas mettre de type (en sachant que le compilateur mettrait int) mais cela a dû provoquer beaucoup de bugs car on ne peut savoir si cela a été omis exprès ou si c’est un oubli…

Aujourd’hui il ne faut surtout pas faire cela car en plus, ce n’est plus dans la norme du C. Il peut y avoir des compilateurs (ou même les prochaines versions de GCC) qui refuseront l’absence de type et provoqueront une erreur.

Reprenons la fonction de l’exemple de tout à l’heure dans un programme en C :

#include <stdio.h>
afficherPrix(double prix)
{
        printf("%.2f DA", prix);
}
int main(void){
        printf("Le prix est ");
        afficherPrix(42.1337);
        puts(".");
}

Comme on n’a rien mis à la place du type de retour de la fonction, le compilateur va mettre int, pas void, mais cela ne pose pas vraiment de problème. À la compilation on obtient juste l’avertissement de tout à l’heure.
Cependant, si l’on active l’option -Wall de GCC, il nous en ajoute un autre :

In function ‘afficherPrix’:
5:1: warning: control reaches end of non-void function [-Wreturn-type]

C’est pourquoi il faut toujours compiler avec -Wall. L’avertissement indique que la fonction, dont le type de retour n’est pas void, s’est terminée jusqu’à la fin, c’est-à-dire sans rencontrer d’instruction return.

En C, ce n’est pas une erreur pour le compilateur, contrairement au compilateur Java qui refuserait une telle situation.

De toute façon dans notre exemple, nous n’utilisons pas la valeur de retour, donc void ou un int c’est la même chose. Que ce soit en C ou en Java, on a le droit d’utiliser une fonction qui retourne une valeur comme si elle n’en retournait pas (c’est-à-dire utiliser une fonction comme si c’était une procédure) et ignorer sa valeur de retour.

Mais que renvoie cette fonction ? Vous pouvez essayer de mettre l’appel à la fonction dans une expression si vous êtes curieux. Elle peut renvoyer n’importe quelle valeur entière (comme celle que l’on trouve dans une variable locale qui n’a pas été initialisée). En C, la valeur renvoyée par une fonction qui n’a pas d’instruction return est indéfinie.

En conclusion, toujours mettre un type de retour correct au début de la définition d’une fonction. Ne rien mettre, ce n’est pas une bonne idée, et si la fonction ne renvoie rien il faut mettre void.

void comme paramètre

Quand une fonction en C ou une méthode ou un constructeur en Java n’a pas besoin de paramètres, il faut quand-même mettre les parenthèses après l’identificateur dans la définition. Mais doit-on les laisser vides, ou bien faut-il mettre void ?

En Java

Si l’on essaie avec void comme ceci :

/// NE SE COMPILE PAS !!///
class Voiture {
        void demarrer(void) {
                System.out.println("Vroom!");
        }
}

le compilateur refuse :

Voiture.java:3: error:  expected
void demarrer(void){
              ^
1 error

Il dit qu’il attendait un identificateur, mais si on en ajoute un :

        void demarrer(void x) {

il donne une autre erreur :

Voiture.java:3: error: 'void' type not allowed here
void demarrer(void x){
              ^
1 error

Type ‘void’ non autorisé ici

Donc en Java, void n’est jamais utilisé dans les paramètres de la définition d’une méthode (ni d’un constructeur). Avec Java, c’est toujours clair :-)…

En C

La norme du C dit que pour indiquer qu’une fonction ne prend pas de paramètres, elle doit être déclarée avec le mot-clé void entre les parenthèses.

Exemple:

void toto(void){
    printf("loulou");
}

Et si l’on ne met rien ? La norme dit que ne rien mettre, cela signifie qu’on ne précise pas quels sont le nombre et les types des paramètres. Cela ne veut pas dire qu’il n’y en a pas (ni qu’il y en a).

Qu’est-ce que cela implique ? La fonction de l’exemple précédent doit être appelée avec l’instruction :

toto();

mais si, par erreur on croyait qu’elle a un paramètre entier et si on essayait de l’appeler avec :

toto(42);

cela provoquerait une erreur du compilateur :

error: too many arguments to function ‘toto’

trop de paramètres passés à la fonction

Par contre si on déclare la fonction ainsi :

void toto(){
    printf("loulou");
}

Le compilateur accepterait sans problème l’appel :

toto(42);

Le programmeur qui a écrit cette instruction a clairement une idée fausse sur le comportement de la fonction, mais le compilateur ne l’a pas aidé à s’en rendre compte… Bonjour les bugs !

Conclusion : En C, habituez-vous à toujours mettre void entre les parenthèses quand une fonction ne prend pas de paramètres. Ne rien mettre c’est s’exposer à des bugs difficiles à détecter.

La méthode main() du C

Contrairement à Java, qui a une seule et unique façon d’écrire la méthode main, en C, on en voit de toutes les couleurs :

void main(void)
{ ...
main(void) 
{ ...
main() 
{ ...
int main() 
{ ...
int main(void) 
{ ...
int main(int argc, char* argv[]) 
{ ...
int main(int argc, char** argv) 
{ ...

…et toutes ces formes fonctionnent ! Mais sont-elles toutes correctes ? « Bien sûr, puisqu’elles fonctionnent ! » :-/ NON !!!

La norme, ou plutôt les normes de 1999 et 2011 (les anciennes sont dépassées) sont claires : seules les deux formes suivantes sont admises :

int main(void)
int main(int argc, char* argv[]) 

La première si les paramètres donnés à l’appel du programme sont ignorés, la deuxième si le programme les utilise.

Donc dans la liste précédente, des 7 possibilités qui fonctionnent, seules les trois dernières (la dernière est équivalente à celle qui la précède) sont correctes… Pourquoi ?

Cela a évidemment un rapport avec ce que l’on a dit de void, mais pas seulement:

Publicités
Laisser un commentaire

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

%d blogueurs aiment cette page :