web-dev-qa-db-fra.com

Pourquoi la fin d'une fonction non vide sans retour d'une valeur ne génère-t-elle pas une erreur de compilation?

Depuis que j'ai compris il y a de nombreuses années que cela ne produisait pas d'erreur par défaut (du moins dans GCC), je me suis toujours demandé pourquoi?

Je comprends que vous pouvez émettre des drapeaux de compilation pour générer un avertissement, mais cela ne devrait-il pas toujours être une erreur? Pourquoi est-il judicieux pour une fonction non vide de ne pas renvoyer une valeur pour qu'elle soit valide?

Un exemple comme demandé dans les commentaires:

#include <stdio.h>
int stringSize()
{
}

int main()
{
    char cstring[5];
    printf( "the last char is: %c\n", cstring[stringSize()-1] ); 
    return 0;
}

... compile.

156
Catskul

Les normes C99 et C++ n'exigent pas que les fonctions renvoient une valeur. L'instruction de retour manquante dans une fonction renvoyant une valeur sera définie (pour renvoyer 0) uniquement dans la fonction main.

La logique inclut que vérifier si chaque chemin de code renvoie une valeur est assez difficile, et une valeur de retour pourrait être définie avec un assembleur intégré ou d'autres méthodes délicates.

De C++ 11 brouillon:

§ 6.6.3/2

Découler à la fin d'une fonction [...] entraîne un comportement indéfini dans une fonction renvoyant des valeurs.

§ 3.6.1/5

Si le contrôle atteint la fin de main sans rencontrer d'instruction return, l'effet est celui de l'exécution

return 0;

Notez que le comportement décrit dans C++ 6.6.3/2 n'est pas le même en C.


gcc vous avertira si vous l'appelez avec l'option -Wreturn-type.

- Wreturn-type Avertir à chaque fois qu'une fonction est définie avec un type de retour dont la valeur par défaut est int. Avertissez également de toute instruction de retour sans valeur de retour dans une fonction dont le type de retour n'est pas vide (le fait de tomber de la fin du corps de la fonction est considéré comme renvoyant sans valeur), ainsi Le type de retour est nul.

Cet avertissement est activé par - Wall .


Juste comme une curiosité, regardez ce que fait ce code:

#include <iostream>

int foo() {
   int a = 5;
   int b = a + 1;
}

int main() { std::cout << foo() << std::endl; } // may print 6

Ce code a un comportement formellement indéfini, et en pratique, il est convention d'appel et architecture . Sur un système particulier, avec un compilateur particulier, la valeur de retour est le résultat de la dernière évaluation d'expression, stockée dans le registre eax du processeur de ce système.

144

par défaut, gcc ne vérifie pas que tous les chemins de code renvoient une valeur car, en général, cela ne peut pas être fait. Cela suppose que vous sachiez ce que vous faites. Prenons un exemple courant utilisant des énumérations:

Color getColor(Suit suit) {
    switch (suit) {
        case HEARTS: case DIAMONDS: return RED;
        case SPADES: case CLUBS:    return BLACK;
    }

    // Error, no return?
}

Vous, le programmeur, savez que sauf méthode, cette méthode retourne toujours une couleur. gcc pense que vous savez ce que vous faites afin de ne pas vous obliger à mettre un retour au bas de la fonction.

en revanche, javac essaie de vérifier que tous les chemins de code renvoient une valeur et renvoie une erreur s’il ne peut pas prouver qu’ils le font tous. Cette erreur est imposée par la spécification du langage Java. Notez qu’elle est parfois fausse et que vous devez insérer une instruction de retour inutile.

char getChoice() {
    int ch = read();

    if (ch == -1 || ch == 'q') {
        System.exit(0);
    }
    else {
        return (char) ch;
    }

    // Cannot reach here, but still an error.
}

C'est une différence philosophique. C et C++ sont des langages plus permissifs et de confiance que Java ou C # et ainsi certaines erreurs dans les nouveaux langages sont des avertissements en C/C++ et certains avertissements sont ignorés ou désactivés par défaut.

42
John Kugelman

Vous voulez dire, pourquoi sortir de la fin d’une fonction renvoyant une valeur (c’est-à-dire quitter sans un return explicite) n’est pas une erreur?

Premièrement, en C, le fait de savoir si une fonction renvoie ou non quelque chose de significatif n’est critique que lorsque le code en cours d’exécution tilise la valeur renvoyée. Peut-être que la langue ne voulait pas vous forcer à retourner quoi que ce soit quand vous savez que vous n'allez pas l'utiliser de toute façon la plupart du temps.

Deuxièmement, apparemment, la spécification du langage ne voulait pas forcer les auteurs du compilateur à détecter et à vérifier tous les chemins de contrôle possibles pour détecter la présence d'un return explicite (bien que, dans de nombreux cas, cela ne soit pas si difficile à faire). De plus, certains chemins de contrôle peuvent mener à fonctions non-retournantes - le trait généralement inconnu du compilateur. De tels chemins peuvent devenir une source de faux positifs gênants.

Notez également que C et C++ diffèrent dans leurs définitions du comportement dans ce cas. En C++, la fin d'une fonction renvoyant une valeur renvoie toujours un comportement indéfini (que le résultat de la fonction soit utilisé ou non par le code appelant). En C, cela ne provoque un comportement indéfini que si le code appelant tente d'utiliser la valeur renvoyée.

13
AnT

Il est légal en C/C++ de ne pas revenir d'une fonction qui prétend renvoyer quelque chose. Il existe un certain nombre de cas d'utilisation, tels que l'appel de exit(-1) ou une fonction qui l'appelle ou lève une exception.

Le compilateur ne rejettera pas le C++ légal, même s'il mène à UB si vous le lui demandez. En particulier, vous demandez que aucun avertissement soit généré. (Gcc en active toujours certains par défaut, mais une fois ajoutés, ceux-ci semblent s'aligner sur les nouvelles fonctionnalités et non sur les nouveaux avertissements pour les anciennes.)

Changer le gcc no-arg par défaut pour émettre certains avertissements pourrait constituer un changement radical pour les scripts existants ou la création de systèmes. Ceux bien conçus soit -Wall et gérer les avertissements, ou basculer les avertissements individuels.

Apprendre à utiliser une chaîne d'outils C++ est un obstacle à l'apprentissage du programmeur C++, mais les chaînes d'outils C++ sont généralement écrites par et pour des experts.

Dans quelles circonstances cela ne produit-il pas une erreur? Si elle déclare un type de retour et ne renvoie pas quelque chose, cela ressemble à une erreur pour moi.

La seule exception à laquelle je peux penser est la fonction main(), qui n'a pas du tout besoin d'une instruction return (au moins en C++; je ne dispose d'aucun des standards C à portée de main ). S'il n'y a pas de retour, il agira comme si return 0; est la dernière déclaration.

0
David Thornley

On dirait que vous devez activer les avertissements de votre compilateur:

$ gcc -Wall -Wextra -Werror -x c -
int main(void) { return; }
cc1: warnings being treated as errors
<stdin>: In function ‘main’:
<stdin>:1: warning: ‘return’ with no value, in function returning non-void
<stdin>:1: warning: control reaches end of non-void function
$
0
Chris Lutz

Je crois que cela est dû au code hérité (C n'a jamais demandé d'instruction return, contrairement à C++). Il existe probablement une énorme base de code reposant sur cette "fonctionnalité". Mais au moins il y a -Werror=return-type drapeau sur de nombreux compilateurs (y compris gcc et clang).

0
d.lozinski