45 votes

Pourquoi non initialisé au lieu de hors limites?

Dans le code ci-dessous, pourquoi b[9] n'est pas initialisé au lieu de hors limites?

 #include <stdio.h>

int main(void)
{
    char b[] = {'N', 'i', 'c', 'e', ' ', 'y', 'o', 'u', '!'};
    printf("b[9] = %d\n", b[9]);

    return 0;
}
 

Appel du compilateur:

 % gcc -O2 -W -Wall -pedantic -c foo.c
foo.c: In function ‘main':
foo.c:6:5: warning: ‘b[9]' is used uninitialized in this function [-Wuninitialized]
     printf("b[9] = %d\n", b[9]);
% gcc --version
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.6) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 

Mise à jour: Maintenant, c'est étrange:

 #include <stdio.h>

void foo(char *);

int main(void)
{
    char b[] = {'N', 'i', 'c', 'e', ' ', 'y', 'o', 'u', '!'};
    foo(&b[9]);
    foo(&b[10]);
    printf("b[9] = %d\n", b[9]);
    printf("b[10] = %d\n", b[10]);

    return 0;
}
 

La compilation de ces résultats donne les avertissements auxquels on peut s'attendre:

 % gcc -O2 -W -Wall -pedantic -c foo.c
foo.c: In function ‘main':
foo.c:9:5: warning: array subscript is above array bounds [-Warray-bounds]
     foo(&b[10]);
     ^
foo.c:10:29: warning: array subscript is above array bounds [-Warray-bounds]
     printf("b[9] = %d\n", b[9]);
                             ^
foo.c:11:29: warning: array subscript is above array bounds [-Warray-bounds]
     printf("b[10] = %d\n", b[10]);
 

Soudainement, gcc voit les limites pour ce que c'est.

57voto

Antti Haapala Points 11542

Je crois que cela pourrait être le cas ici: dans le premier code, le compilateur GCC avis que vous n'avez pas besoin de la totalité char tableau, tout b[9], de sorte qu'il peut remplacer le code par

char b_9; // = ???
printf("b[9] = %d\n", b_9);

Maintenant, c'est complètement légal transformer, parce que le tableau a été consulté hors des limites du terrain, le comportement est totalement indéfini. Seulement dans la dernière phase est-il alors de l'avis que cette variable, qui est un substitut de l' b[9], ne sont pas initialisés, et les questions les diagnostics message.

Pourquoi je crois que cela? Parce que si j'ajoute juste le code qui référence l'adresse du tableau en mémoire, par exemple printf("%p\n", &b[8]); n'importe où, le tableau est maintenant entièrement réalisé à la mémoire, et le compilateur va diagnostiquer indice de tableau est ci-dessus les limites du tableau.


Ce que je trouve encore plus intéressant, c'est que GCC ne permet pas de diagnostiquer les limites de l'accès à tous, à moins que les optimisations sont activées. Ce serait à nouveau suggèrent que chaque fois que vous écrivez un programme nouveau programme, vous devez le compiler avec les optimisations activées pour faire les bugs très visible, au lieu de les garder cachés avec le mode de débogage ;)

16voto

Bathsheba Points 23209

Le comportement sur la lecture de la b[9] ou b[10] est pas défini.

Votre compilateur émet un avertissement (il ne doit pas le faire), bien que le texte de l'avertissement est un peu trompeur, mais pas techniquement incorrect. À mon avis, c'est plutôt intelligent. (Un compilateur C est pas tenu d'émettre un diagnostic en dehors des limites d'accès.)

Concernant l' &b[9], le compilateur n'est pas autorisé à déréférencer, et doit l'évaluer en tant que b + 9. Vous êtes autorisé à définir un pointeur d'un passé la fin d'un tableau. Le comportement de paramètre un pointeur vers &b[10] est pas défini.

1voto

chux Points 13185

Quelques autres résultats expérimentaux.


À l'aide de char b[9] au lieu de char b[] semble pas faire de différence, gcc avertit toujours la même chose avec char b[9].

Fait intéressant, l'initialisation d'un élément passé par le "prochain" membre en struct 1) ne calme le "non initialisé" avertissement et 2) ne pas avertir à propos de l'acquisition à l'extérieur de la matrice.

#include <stdio.h>

typedef struct {
  char c[9];
  char d[9];
} TwoNines;

int main(void) {
  char b[9] = { 'N', 'i', 'c', 'e', ' ', 'y', 'o', 'u', '!' };
  printf("b[] size %zu\n", sizeof b);
  printf("b[9] = %d\n", b[9]);   // 'b[9]' is used uninitialized in this function [-Wuninitialized]

  TwoNines e = { { 'N', 'i', 'c', 'e', ' ', 'y', 'o', 'u', '!' }, //
                 { 'N', 'i', 'c', 'e', ' ', 'y', 'o', 'u', '!' } };

  printf("e size %zu\n", sizeof e);
  printf("e.c[9] = %d\n", e.c[9]);   // No warning.

  return 0;
}

Sortie

b[] size 9
b[9] = 0
e size 18    // With 18, we know `e` is packed.
e.c[9] = 78  // 'N'

Notes:
gcc-std=c11 -O3-g3 -pedantic -Wall-Wextra -Wconversion -c -fmessage-longueur=0 -v -MMD -MP ...
gcc/gcc-7.3.0-2.i686

-2voto

P__J__ Points 12922

Lorsque vous compilez le code avec -O2, la trivialité de l'exemple rend cette variable optimisée. L'avertissement est donc 100% correct

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X