Variable non initialisée - Uninitialized variable

En informatique , une variable non initialisée est une variable qui est déclarée mais qui n'est pas définie sur une valeur connue définie avant d'être utilisée. Cela aura une certaine valeur, mais pas une valeur prévisible. En tant que tel, il s'agit d'une erreur de programmation et d'une source courante de bogues dans les logiciels.

Exemple du langage C

Une hypothèse commune faite par les programmeurs novices est que toutes les variables sont définies sur une valeur connue, telle que zéro, lorsqu'elles sont déclarées. Bien que cela soit vrai pour de nombreuses langues, ce n'est pas vrai pour toutes, et donc le potentiel d'erreur est là. Les langages tels que C utilisent l' espace de pile pour les variables, et la collection de variables allouées pour un sous-programme est connue sous le nom de frame de pile . Alors que l'ordinateur met de côté la quantité d'espace appropriée pour le cadre de la pile, il le fait généralement simplement en ajustant la valeur du pointeur de pile , et ne définit pas la mémoire elle-même dans un nouvel état (généralement par souci d'efficacité). Par conséquent, quel que soit le contenu de cette mémoire à ce moment-là, il apparaîtra comme des valeurs initiales des variables qui occupent ces adresses.

Voici un exemple simple en C:

void count( void )
{
    int k, i;
    
    for (i = 0; i < 10; i++)
    {
        k = k + 1;
    }
    
    printf("%d", k);
}

La valeur finale de k n'est pas définie. La réponse qu'il doit être 10 suppose qu'il a commencé à zéro, ce qui peut être vrai ou non. Notez que dans l'exemple, la variable i est initialisée à zéro par la première clause de l' for instruction.

Un autre exemple peut être celui des structures . Dans l'extrait de code ci-dessous, nous avons un struct student qui contient des variables décrivant les informations sur un étudiant. La fonction register_student perd de la mémoire car elle ne parvient pas à initialiser complètement les membres de struct student new_student . Si nous regardons de plus près, au début age , semester et student_number sont initialisés. Mais l'initialisation des membres first_name et last_name est incorrecte. En effet, si la longueur first_name et les last_name tableaux de caractères sont inférieurs à 16 octets, pendant le strcpy , nous ne parvenons pas à initialiser complètement les 16 octets de mémoire réservés à chacun de ces membres. Par conséquent, après avoir memcpy() obtenu la structure résultante output , nous perdons de la mémoire de pile à l'appelant.

struct student {
    unsigned int age;
    unsigned int semester;
    char first_name[16];
    char last_name[16];
    unsigned int student_number;
};

int register_student(struct student *output, int age, char *first_name, char *last_name)
{
    // If any of these pointers are Null, we fail.
    if (!output || !first_name || !last_name)
    {
        printf("Error!\n");
        return -1;
    }

    // We make sure the length of the strings are less than 16 bytes (including the null-byte)
    // in order to avoid overflows
    if (strlen(first_name) > 15 ||  strlen(last_name) > 15) {
      printf("first_name and last_name cannot be longer that 16 characters!\n");
      return -1;
    }

    // Initializing the members
    struct student new_student;
    new_student.age = age;
    new_student.semester = 1;
    new_student.student_number = get_new_student_number();
    
    strcpy(new_student.first_name, first_name);
    strcpy(new_student.last_name, last_name);

    //copying the result to output
    memcpy(output, &new_student, sizeof(struct student));
    return 0;
}


Dans tous les cas, même lorsqu'une variable est implicitement initialisée à une valeur par défaut telle que 0, ce n'est généralement pas la valeur correcte . Initialisé ne signifie pas correct si la valeur est une valeur par défaut. (Cependant, l'initialisation par défaut à 0 est une bonne pratique pour les pointeurs et les tableaux de pointeurs, car elle les rend invalides avant qu'ils ne soient réellement initialisés à leur valeur correcte.) En C, les variables avec une durée de stockage statique qui ne sont pas initialisées explicitement sont initialisées à zéro (ou nul, pour les pointeurs).

Non seulement les variables non initialisées sont une cause fréquente de bogues, mais ce type de bogue est particulièrement sérieux car il peut ne pas être reproductible: par exemple, une variable peut rester non initialisée uniquement dans une branche du programme. Dans certains cas, les programmes avec des variables non initialisées peuvent même passer des tests logiciels .

Les impacts

Les variables non initialisées sont de puissants bogues puisqu'elles peuvent être exploitées pour fuir de la mémoire arbitraire ou pour obtenir un écrasement arbitraire de la mémoire ou pour obtenir l'exécution de code, selon le cas. Lors de l'exploitation d'un logiciel qui utilise la randomisation de la disposition de l'espace d'adressage , il est souvent nécessaire de connaître l' adresse de base du logiciel en mémoire. L'exploitation d'une variable non initialisée de manière à forcer le logiciel à fuir un pointeur de son espace d'adressage peut être utilisée pour contourner ASLR.

Utilisation dans les langues

Les variables non initialisées sont un problème particulier dans les langages tels que le langage assembleur, C et C ++, qui ont été conçus pour la programmation de systèmes . Le développement de ces langages impliquait une philosophie de conception dans laquelle les conflits entre performance et sécurité étaient généralement résolus en faveur de la performance. Le programmeur devait être conscient des problèmes dangereux tels que les variables non initialisées.

Dans d'autres langues, les variables sont souvent initialisées à des valeurs connues lors de leur création. Les exemples comprennent:

  • VHDL initialise toutes les variables standard en une valeur spéciale «U». Il est utilisé dans la simulation, pour le débogage, pour permettre à l'utilisateur de savoir quand le ne se soucient pas des valeurs initiales, par la logique multivaluée , affecte la sortie.
  • Java n'a pas de variables non initialisées. Les champs de classes et d'objets qui n'ont pas d'initialiseur explicite et les éléments de tableaux sont automatiquement initialisés avec la valeur par défaut de leur type (false pour les booléens, 0 pour tous les types numériques, null pour tous les types de référence). Les variables locales en Java doivent être définitivement affectées avant d'y accéder, sinon il s'agit d'une erreur de compilation.
  • Python initialise les variables locales à NULL (distinctes de None ) et lève un UnboundLocalError lorsqu'une telle variable est accédée avant d'être (ré) initialisée à une valeur valide.
  • D initialise toutes les variables à moins que le programmeur ne le spécifie explicitement.

Même dans les langages où les variables non initialisées sont autorisées, de nombreux compilateurs tenteront d'identifier l'utilisation de variables non initialisées et les rapporteront comme des erreurs de compilation .

Voir également

Les références

  1. ^ "ISO / CEI 9899: TC3 (norme C actuelle)" (PDF) . 2007-09-07. p. 126 . Récupéré le 26/09/2008 . Section 6.7.8, paragraphe 10.
  2. ^ "Spécification de langage Java: 4.12.5 Valeurs Initiales des Variables" . Sun Microsystems . Récupéré le 18/10/2008 .

Lectures complémentaires

  • CWE-457 Utilisation d'une variable non initialisée [1] .