2009-11-04 20 views
7

Leí esta pregunta y su respuesta en un libro. Pero no entendí la justificación del libro.¿Por qué el compilador no detecta fuera de límites en la inicialización de cadena constante?

¿Va a estar compilando el siguiente código?

int main() 
{ 
    char str[5] = "fast enough"; 
    return 0; 
} 

Y la respuesta fue:

Yes.The compilador no detecta el error si límites de una matriz son superado.

No pude conseguirlo.

¿Alguien puede explicar esto?

+1

Este ejemplo es bastante claro, pero probablemente debería especificar el idioma, si no el libro. –

+0

¿Qué parte no entiendes? –

+0

@ mmyers esta parte - "El compilador nunca detecta el error si se exceden los límites de una matriz." –

Respuesta

5

En el estándar de C++, las matrices 8.5.2/2 personaje dice:

No se efectuarán más inicializadores que existen elementos de la matriz.

En el estándar C99, 6.7.8/2 inicialización dice:

Sin inicializador tratará de proporcionar un valor para un objeto no contenida dentro de la entidad ser inicializado

C90 6.5.7 Inicializadores dice similar.

Sin embargo, tenga en cuenta que para C (tanto C90 como C99) el carácter de terminación '\ 0' se colocará en la matriz si hay espacio. No es un error si el terminador no encaja (C99 6.7.8/14: "Caracteres sucesivos del literal de cadena de caracteres (incluido el carácter nulo de terminación si hay espacio o si el conjunto es de tamaño desconocido) inicializa los elementos del formación").

Por otro lado, el estándar de C++ tiene un ejemplo que indica que se debe diagnosticar un error si no hay espacio para el carácter de terminación.

en cualquiera de los casos, esto se debe diagnosticar como un error en todos los compiladores:

char str[5] = "fast enough"; 

compiladores Tal pre-ANSI no eran tan estrictos, pero cualquier compilador bastante moderna debe diagnosticar esto.

+1

GCC 3.4.5 (compilando como C) solo da una advertencia (creo que debería diagnosticar esto como un error). La compilación GCC como C++, MSVC 8, Digital Mars 8.5 y Comeau 4.3.10.1 producen un error. –

5

Su libro debe ser bastante viejo, porque gcc pone a cabo una advertencia, incluso sin -Wall encendido:

 
$ gcc c.c 
c.c: In function `main': 
c.c:6: warning: initializer-string for array of chars is too long 

Si actualizamos ligeramente el programa:

#include <stdio.h> 

int main(int argc, char **argv) 
{ 

     char str[5] = "1234567890"; 
     printf("%s\n", str); 
     return 0; 
} 

Podemos ver que gcc parece para truncar la cadena a la longitud que ha especificado; Soy asumiendo que es que es un '\0' donde str[6] estaría, porque de lo contrario deberíamos ver basura después del 5; pero tal vez gcc implícitamente hace str una matriz de longitud 6 y automáticamente pega el '\0' allí - No estoy seguro.

 
$ gcc c.c && ./a.exe 
c.c: In function `main': 
c.c:6: warning: initializer-string for array of chars is too long 
12345 
+0

"Will it compile" generalmente se usa para indicar "compilará sin errores", no "compilará". sin advertencias ". –

+0

"El compilador nunca detecta el error si se exceden los límites de una matriz". En este caso, sí. –

+0

@Brian: es por eso que '-Werror' – Christoph

0

La comprobación vinculada a la matriz ocurre en tiempo de ejecución, no en tiempo de compilación. El compilador no tiene forma de hacer el análisis estático del código anterior que sería necesario para evitar el error.

ACTUALIZACIÓN: Aparentemente, la afirmación anterior es cierta para algunos compiladores y no para otros. Si su libro dice que compilará, debe estar refiriéndose a un compilador que no hace la verificación.

+0

Claro que sí, este es un inicializador conocido por el compilador en tiempo de compilación. Simplemente no es necesario que el error salga. – bdonlan

+2

Bueno, en C, la comprobación de límites de matriz no ocurre en absoluto. Y, obviamente, el compilador puede hacer el análisis estático, ya que gcc le daría una advertencia (como lo señala otro cartel) –

+0

C no hace la comprobación de límites de matriz, por lo tanto, esta compila. El compilador puede hacer una comprobación estática, pero sigue siendo válida. C. –

0

Porque "suficientemente rápido" simplemente un puntero a una cadena terminada nula. Es demasiado trabajo para el compilador averiguar si alguna vez la asignación a un char * o char [] va a ir más allá de los límites de la matriz.

+1

No puede ser el motivo. El compilador tiene que verificar el número de inicializadores de todos modos para permitir cosas como 'char s [] =" Hello "' y 'int array [10] = {2}' – UncleBens

+0

Hay una diferencia entre "Hello" y {'H' , 'e', ​​'l', 'l', 'o', '\ 0'} Uno es un conjunto y el otro simplemente es un puntero. El puntero podría apuntar a cualquier parte, pero la matriz es constante en el momento de la compilación. – theycallmemorty

+0

@theycallmemorty: ¿Eh? ¿Cuál es un puntero? – AnT

0

Lo que está sucediendo es que estás tratando de inicializar una matriz de caracteres con más caracteres de los que la matriz tiene espacio. Aquí se explica cómo se divide:

char str[5]; 

Declara una matriz de caracteres con cinco caracteres.

char str[5] = "fast enough"; 

La segunda parte '= "suficientemente rápido";' luego intenta inicializar esa matriz con el valor "lo suficientemente rápido". Esto no funcionará, porque "lo suficientemente rápido" es más largo que la matriz.

Sin embargo, compilará. Los compiladores C y C++ generalmente no pueden realizar comprobaciones de límites en las matrices, y sobrepasar una matriz es una de las razones más comunes para las fallas de segmentación.[editar] Como señaló Mark Rushakoff, aparentemente los más nuevos lanzan advertencias, en algunos casos. [/ edit] Esto puede segfault cuando intentas ejecutarlo, más probablemente creo que la matriz simplemente se inicializará a "rápido".

+0

No, no se "compilará". es una violación de contracción, que requiere un mensaje de diagnóstico. Comeau, un compilador muy pedante, se negará a compilar esto con un mensaje de error. GCC, por otro lado, opta por una mera advertencia. Desde el punto de vista del lenguaje, esta es una violación de restricción, es decir, en términos simples es un * error *. – AnT

+0

Harsh Andrey. Obviamente se compilará - ALGUNAS VECES. Lo hace en GCC, lo hace en el compilador original de carteles. Lo hará en MUCHOS compiladores. Y si él no tiene -Wall encendido, ni siquiera dará una advertencia. Tal vez sea una violación y tal vez algunos compiladores lo capten.Pero mi respuesta no está equivocada al decir que generalmente compilará. Y mi afirmación de que en la mayoría de los casos los compiladores no pueden y no realizan la comprobación de límites también es cierta. En la inicialización es uno de los pocos casos donde pueden, y como se menciona en la edición, algunos lo hacen. –

+0

@Alcon: No, en realidad, parece fallar con un error en la mayoría de los compiladores (si no prácticamente * todos *). De hecho, GCC es la única excepción de esa regla que conozco hasta ahora. Y no, no necesita '-Wall' en GCC para activar esta advertencia. Esta advertencia se emite por defecto. Además, esto no tiene nada que ver con la comprobación de límites. Esto es solo una cuestión de suministrar a muchos inicializadores en inicialización agregada. Estoy seguro de que todos los compiladores sin una sola excepción emitirán un error si haces 'int a [1] = {1, 2}'. El literal de cadena no es realmente diferente de eso. – AnT

2

La respuesta a la pregunta que ha citado es incorrecta. La respuesta correcta es "No. El código no compilará", asumiendo un compilador de C formalmente correcto (a diferencia de los caprichos de un compilador específico).

El lenguaje C no permite el uso de un literal de cadena excesivamente largo para inicializar una matriz de caracteres de un tamaño específico. La única flexibilidad permitida por el lenguaje aquí es el carácter de terminación \0. Si la matriz es demasiado corta para acomodar la terminación \0, la terminación \0 se elimina silenciosamente. Pero los caracteres de cadena literales reales no se pueden descartar. Si el literal es demasiado largo, es una violación de restricción y el compilador debe emitir un mensaje de diagnóstico.

char s1[5] = "abc"; /* OK */ 
char s2[5] = "abcd"; /* OK */ 
char s3[5] = "abcde"; /* OK, zero at the end is dropped (ERROR in C++) */ 
char s4[5] = "abcdef"; /* ERROR, initializer is too long (ERROR in C++ as well) */ 

Quien escribió su "libro" sabía de lo que estaban hablando (al menos sobre este tema específico). Lo que dicen en la respuesta es totalmente incorrecto.

Nota: El suministro de inicializadores de cadena excesivamente largos es ilegal en C89/90, C99 y C++. Sin embargo, C++ es aún más restrictivo en este sentido. C++ prohíbe colocar el carácter de terminación \0, mientras que C permite soltarlo, como se describe anteriormente.

+0

¿Dónde se menciona esa restricción? No lo veo en la sección de inicializadores. –

+0

En C89/90, en 6.5.7: No habrá más inicializadores en un inicializador que objetos para inicializar. La excepción para terminar 0 se da más adelante en el texto. Todavía estoy buscando un equivalente en C99 ... – AnT

+0

En C99 está en el mismo lugar: 6.7.8/2. Ningún inicializador intentará proporcionar un valor para un objeto no contenido dentro de la entidad que se está inicializando. – AnT

Cuestiones relacionadas