2011-07-07 13 views
9

He encontrado una pieza extraña de código en un proyecto que tengo que mantener. Hay un miembro vacío de la matriz de una clase que no conduce a un error de compilación. He probado algunas variaciones de un código de este tipo con MSVC 10.0:Declaración de matriz vacía: comportamiento extraño del compilador

template<class T> struct A { 
    int i[]; 
}; // warning C4200: nonstandard extension used : zero-sized array in struct/union 

template<class T> struct B { static int i[]; }; 
template<class T> int B<T>::i[]; 

struct C { 
    int i[]; 
}; //warning C4200: nonstandard extension used : zero-sized array in struct/union 

template<class T> struct D { static int i[]; }; 
template<class T> int D<T>::i[4]; 
template<>  int D<int>::i[] = { 1 }; 


int main() 
{ 
    A<void> a; 
    B<void> b; 
    C c; 
    D<void> d0; 
    D<int> d1; 

    a.i[0] = 0;  // warning C4739: reference to variable 'a' exceeds its storage space 

    b.i[0] = 0;  // warning C4789: destination of memory copy is too small 

    c.i[0] = 0;  // warning C4739: reference to variable 'c' exceeds its storage space 

    int i[];  // error C2133: 'i' : unknown size 

    d0.i[0] = 0; // ok 
    d0.i[1] = 0; // ok 

    return 0; 
} 

El mensaje de error al int i[] es absolutamente razonable para mí. El código que se muestra con la clase D es C++ estándar bien formado. Pero ¿qué pasa con las clases A, B y C? ¿Qué tipo de tipos son las variables miembro int i[] en estas clases?

Respuesta

6

EDIT:

tu duda se explica por el definition of the extension to the language, lo que permite cero de tamaño matrices al final de structs/sindicatos. No lo he probado, pero si declaras otro miembro después de la matriz de tamaño cero, debería fallar.

por lo tanto, si asigna una variable en la pila, debe conocer su tamaño; La excepción a la regla es cuando se asigna una matriz al final de una estructura/unión, donde es posible algún truco típico de C.

En C++ esto genera una advertencia porque el constructor de copia predeterminado y el operador de asignación probablemente no funcionen.

respuesta anterior:

El compilador le advierte sobre el hecho de que usted está tratando de definir una matriz con tamaño cero. Esto no está permitido en C/C++ estándar.

Veamos las diferencias clase por clase.

En la clase D:

template<class T> struct D { static int i[]; };

funciona dado que está declarando el tipo de una variable miembro estática. Para que esto enlazar, es necesario también definir la matriz real, en una sentencia de definición como lo hace:

template<>  int D<int>::i[] = { 1 }; 

aquí también especificar el tamaño de la matriz a través del inicializador.

Con la clase B, que está haciendo algo similar, pero la definición es:

template<class T> int B<T>::i[]; 

es decir, que no especifican el tamaño y obtener la advertencia.

Con clase A, más de lo mismo, está definiendo una variable miembro de tipo array sin el tamaño.

+0

La pregunta es: ¿Por qué las advertencias (relacionadas con las clases 'A',' B' y 'C') son advertencias en lugar de errores? En mi opinión, esto se compara asimétricamente con el error que obtengo en la declaración de la variable local. – 0xbadf00d

+0

Ver mi edición, por favor. – sergio

+0

Gracias, otra "buena" extensión de Microsoft para el estándar C++ ... – 0xbadf00d

0

Bueno uno. Solo para estar seguro, se está preguntando por qué el compilador no lo está marcando como un error, ¿verdad? En ese caso, creo que este problema es impredecible en todos los compiladores, pero soy consciente de que esto está sucediendo en MSVC todo el tiempo.

http://support.microsoft.com/kb/98409

Vamos a ver si lo puedo explicar como lo hicieron.Si tuviera que declarar una estructura con una matriz vacía como esta,

struct a 
{ 
    int x; 
    char empty[]; 
}; 

el compilador podría asignar 4 bytes para x y probablemente otros 4 bytes para el puntero char. empty contendrá la dirección 4 bytes más allá del inicio de la estructura a.

Dado que es una matriz de caracteres de ninguna longitud, intentar acceder a ella sería un error ya que no hay un 0 final que indique el final de la cadena.

Podría elegir inicializar la estructura más tarde para señalar el inicio de una cadena real para superar este error.

struct a myStruct = { 1, "hello world"}; // empty now points to the start of "hello world" 

Dado que una estructura es básicamente una clase, resulta que puede hacer lo mismo con una clase si se asegura su un agregado y no una clase completa.

Así que ya ve. Los compiladores de MSVC tratan las matrices sin un tamaño fijo como un puntero cuando se declaran dentro de una estructura/clase. Recuerde que las definiciones de clase son simplemente declaraciones. El compilador no les asigna espacio hasta que crea una instancia para él. Cuando comienzas a pensar en ello, sorta hace desde entonces. ¿Cómo sabrá el compilador si planea asignar almacenamiento más adelante? Se convierte en un artefacto en tiempo de ejecución, pero el compilador todavía era lo suficientemente inteligente como para advertirte sobre el problema.

Cuestiones relacionadas