¿Por qué no inicializado en lugar de fuera de los límites?


En el siguiente código, ¿por qué b[9] no está inicializado en lugar de fuera de los límites?

#include <stdio.h>

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

    return 0;
}

Llamada del compilador:

% 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.

Actualizar: Ahora esto es impar:

#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;
}

Compilar esto resulta en las advertencias que uno esperaría:

% 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]);

De repente gcc ve lo fuera de los límites por lo que es.

Author: Antti Haapala, 2018-07-17

4 answers

Creo que este podría ser el caso aquí: en el primer código, GCC nota que no necesita toda la matriz char, solo b[9], por lo que puede reemplazar el código con

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

Ahora, esta es una completamente transformación legal, porque como se accedió al array fuera de los límites, el comportamiento es completamente indefinido. Solo en la última fase se da cuenta de que esta variable, que es un sustituto de b[9], no está inicializada, y emite el diagnóstico mensaje.

¿por Qué creo esto? Porque si añado solo cualquier código que haga referencia a la dirección del array en memoria , por ejemplo printf("%p\n", &b[8]);en cualquier lugar, el array ahora se realiza completamente en memoria, y el compilador diagnosticará que el subíndice del array está por encima de los límites del array .


Lo que encuentro aún más interesante es que GCC no diagnostica el acceso fuera de los límites a menos que las optimizaciones estén habilitadas. Este nuevo sugieren que cuando estás escribiendo un programa nuevo programa debe compilarlo con optimizaciones habilitadas para hacer que los errores sean muy visibles en lugar de mantenerlos ocultos con el modo de depuración;)

 57
Author: Antti Haapala,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-17 13:47:24

El comportamiento al leer b[9] o b[10] es indefinido.

Su compilador está emitiendo una advertencia (no tiene que hacerlo), aunque el texto de advertencia es un poco engañoso, pero no técnicamente incorrecto. En mi opinión, es bastante inteligente. (Un compilador C es no requerido para emitir un diagnóstico para el acceso fuera de límites.)

Con respecto a &b[9], al compilador no se le permite desreferenciar eso, y debe evaluarlo como b + 9. Se le permite establecer un puntero más allá del final de una matriz. El comportamiento de establecer un puntero a &b[10] es undefined.

 17
Author: Bathsheba,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-17 13:08:38

Algunos resultados experimentales adicionales.


Usando char b[9] en lugar de char b[] parece no hacer ninguna diferencia, gcc todavía advierte lo mismo con char b[9].

Curiosamente, inicializar el elemento de un solo paso a través del miembro "siguiente" en un struct 1) hace callar la advertencia "no inicializada" y 2) no advierte sobre la accesibilidad fuera del array.

#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;
}

Salida

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

Notas:
gcc-std = c11 - O3-g3-pedantic-Wall-Wextra-Wconversión - c-fmessage-length=0 - v-MMD-MP ...
gcc / gcc-7.3.0-2.i686

 1
Author: chux,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-17 14:39:28

Cuando compila el código con-O2 la trivialidad del ejemplo hace que esta variable esté optimizada. Así que la advertencia es 100% correcta

 -2
Author: P__J__,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-17 22:43:45