Kévin Subileau

Espace personnel

C++

C++ : Déclarer une variable dans un switch

En C++, il est possible de déclarer une nouvelle variable à tout moment dans le code... ou presque !

Ainsi, l'extrait de code ci-dessous est évidemment parfaitement valide et compile sans erreur :

for (int i = 0; i < 10; ++i) {
    std::cout << "Itération n°" << i << std::endl;
    int dummy;
    dummy = i * i;
    std::cout << "Le carré de " << i << " est " << dummy  << std::endl;
}

On note que la variable dummy est définie au milieu du code, sans que cela ne pose problème. Partant de ce constat, on pourrait croire que le code suivant compile également sans problème (en supposant que la variable choix est définie) :

switch (choix) {
    case 1:
        std::cout << "Choix 1." << std::endl;
        int dummy = time(NULL); // Erreur !!
        std::cout << "Il est " << dummy << std::endl;
        break;
    default:
        std::cout << "Choix inconnu." << std::endl;
        // std::cout << "Il est " << dummy << std::endl;
        break;
}

Et bien non ! Si vous compiler un code de ce genre, le compilateur le refusera en indiquant une erreur du type :

error: jump to case label
   crosses initialization of 'int dummy'

ou encore, selon les cas :

error: initialization of 'dummy' is skipped by 'case' label

La raison de cette erreur est que la portée de la variable définie à l'intérieur d'un case s'étend à l'ensemble du switch, et pas seulement à ce case. Le case n'est qu'un label, et ne limite pas la portée de la variable, qui n'est limitée que par les accolades englobantes du switch. Que se passerait il alors si le compilateur ne disait rien et que je décommentais la ligne 9 du code précédent ? Et bien, dans le cas où choix serait différent de 1, l'initialisation de la variable faite à la ligne 4 serait sautée puisque seul le bloc default serait exécuté. La variable dummy serait donc définie (puisque sa portée est globale à tout le switch) mais non initialisée (puisque l'initialisation n'a pas été exécutée). Dans cet exemple, dummy est un entier, il contiendrait simplement une valeur indéterminée. Mais ce pourrait tout aussi bien être un objet, et dans ce cas, le constructeur ne serait pas appelé si choix est différent de 1 ! Ce comportement risqué est donc interdit par le compilateur.

Pour résoudre ce problème, il faut explicitement limiter la portée de la variable en entourant le code du case par une paire d'accolades, comme ci-dessous :

switch (choix) {
    case 1:
    {
        std::cout << "Choix 1." << std::endl;
        int dummy = time(NULL); // Ok !!
        std::cout << "Il est " << dummy << std::endl;
        break;
    }
    default:
        std::cout << "Choix inconnu." << std::endl;
        // Désormais interdit car la variable est hors de portée !
        // std::cout << "Il est " << dummy << std::endl;
        break;
}

La portée de la variable dummy est ainsi limitée au case, et il devient impossible de l'utiliser dans un autre case.

Voilà pour cette petite astuce de programmation en C++ assez simple, il suffit juste d'y penser ! A noter que ce problème et sa solution s'applique aussi lorsque l'on déclare une variable entre un goto et le label ciblé par ce goto. Mais bon, c'est bien connu, plus personne n'utilise le goto de nos jours...

Source