2011-07-31 31 views
8

Hay dos archivos fuente en mi programa.¿No debería coincidir la declaración con su definición cuando se trata de una matriz?

Una matriz se define en A.cpp.

// compiler: MSVC2005 SP2  
// A.cpp 

// defines an array of type "int [100]" 
int a[100] = {3}; 

Se utiliza en B.cpp.

// B.cpp 

// declares an array of type "int []" 
extern int a[]; 

int main() 
{ 
    // prints 3 correctly 
    cout << a[0] << endl; 
    return 0; 
} 

yo sepa, enlazador generará un error si no se puede encontrar ninguna definición emparejados por una declaración si se utiliza el identificador declarado. Aquí, int [] y int [100] son dos tipos diferentes, obviamente.

¿Por qué, en este caso, no hay ningún error de enlace? ¿Está garantizado por el estándar que el tamaño de la matriz es trivial durante la coincidencia de declaración/definición? ¿O solo es específico de la implementación? Una cotización de la Norma será apreciada si alguna.

Editar: iammilind mencionado en su respuesta que enlazador puede funcionar correctamente (el compilador gcc es) incluso si el tipo no coincide entre la declaración y definición. ¿LO REQUIERE el estándar o solo una forma de gcc? Supongo que este es un problema mucho más importante que resolver.

+2

El error está en "obviamente". –

+0

@Alf. Nop. Cita del estándar 8.3.4 "El tipo " matriz de tipo lista de declaradores derivados de NT "es un tipo diferente del tipo" lista-tipo-lista-de-declaradores matriz de límite desconocido de T " –

+0

@Erik : gracias por la cita. En lugar de entrar en una discusión sobre lo que "obviamente" conllevó, que por supuesto usted conoce mejor !, le sugiero que seleccione la respuesta de AndreyT como una solución, aunque en mi opinión su uso de la palabra "coincida" "es un poco engañoso.De todos modos, la razón por la que puede hacer que las cosas se vinculen donde los tipos no se ajustan a las reglas de lenguaje para la coincidencia, es que los compiladores no hacen un gran esfuerzo para detectar errores aquí. –

Respuesta

7

En C y C++ una declaración de un objeto a de tipo incompleto coincidirá con la definición del objeto a donde se completa el tipo. Lo que observa simplemente ilustra el hecho de que en C++ puede usar tipos incompletos en declaraciones no definitorias. Pero una vez que llegue a la definición, el tipo debe estar completo.

Este comportamiento no está restringido a las matrices.Por ejemplo, se puede declarar

extern class X x; 

para una clase totalmente desconocida X, y luego, cuando class X ya está completamente definido, puede definir

X x; 

que coincidirá con la declaración anterior.

Lo mismo sucede con su matriz. En primer lugar se declara un objeto de tipo incompleto

extern int a[]; 

y luego definir con completa Tipo

int a[100]; 

Los tipos aquí de hecho no son coincidentes. Sin embargo, el lenguaje C++ nunca requirió que coincidieran. Por ejemplo, 3,9/7 establece explícitamente

El tipo declarado de un objeto de matriz puede ser una matriz de tamaño desconocido y por lo tanto ser incompleta en un punto en una unidad de traducción y completa más adelante; los tipos de matriz en esos dos puntos ("matriz de límite desconocido de T" y "matriz de N T") son tipos diferentes.

Esto significa que la misma array objeto puede tener tipo incompleto inicialmente, pero adquirir un tipo completo más adelante. (Ver también el ejemplo en 3.9/7). Esto no significa, por supuesto, que pueda declarar a como int y luego definirlo como double. La única libertad relacionada con el tipo que tiene aquí es, una vez más, completar un tipo incompleto. No más.

+0

Eso tiene sentido para el "tipo incompleto". Pero ¿por qué todavía enlaza si declaro "extern int a [50]"? "int [50]" es un tipo completo y diferente (que int [100]). –

+0

@Eric, ahora, eso es debido a la destrucción, idealmente esto no debería compilarse, pero su compilador no incluye la dimensión en el nombre destrozado. – unkulunkulu

+0

@unkulunkulu, ok, ¿requiere Standard que la declaración coincida con la definición si ambos son tipos completos? –

0

Aquí dice que "C++ permite la posibilidad de dejar los corchetes vacíos []. En este caso, el compilador asumirá un tamaño para la matriz que coincida con el número de valores incluidos entre llaves {}". Entonces, si esta referencia es correcta, parece ser parte del estándar.

http://www.cplusplus.com/doc/tutorial/arrays/

+0

@Laurent, lo que quiere decir es una definición de matriz int a [] = {3} y lo que estoy pidiendo es una declaración no definitoria extern int a []; –

0

Su pregunta es genuino. Aquí hay algunos problemas.

Cuando declara dos símbolos con los mismos nombres; entonces no hay ningún cheque de linker para su tipo/tamaño, etc. Solo los resuelve y los correlacionan juntos. Por ejemplo, intente declarar extern float a[]; o extern int a[3];, pero seguirá funcionando. Esta es la forma en que el enlazador C++ está (desafortunadamente) diseñado.

Así que la solución es declararlos en el archivo de encabezado y #include tanto en a.cpp como en b.cpp.

+0

¿Estás seguro de que declarar una variable de tipo diferente pero con el mismo nombre puede pasar el enlace? NO es el caso, al menos en MSVC. –

+0

@Eric: intenté 'extern int * a'' extern double * a', 'extern char * a' y similar a lo que mencionó @Marcelo Cantos, también probé' extern int (* a) (int, char *) ', todos pasaron el enlace. Pero no en MSVC. Estoy usando gcc-4.5. Pero sí, eliminé la declaración 'cout'. –

+0

@Eric, en gcc pasa; el enlazador coincide con 2 variables independientes con el mismo nombre en diferentes unidades de traducción. Por lo tanto, es mejor 'extern' en un archivo de cabecera y' # include' en cualquier '.cpp'. – iammilind

1

Antes que nada, el código no debería dar ningún error. La especificación int a[] es diferente de a[100] pero es compatible con ella (está incompleta).

Un segundo punto importante es que no puede contar con el compilador/enlazador de C++ que da errores para la incoherencia de módulos cruzados. Por ejemplo, en un programa C++ no se puede tener la misma clase definida dos veces con una definición diferente, pero el compilador no está OBLIGADO para informarle sobre este problema, es una carga para los programadores.

Si una implementación detecta y señala este tipo de problema, entonces está bien, pero una implementación que no dice nada sobre ellos y simplemente crea archivos ejecutables bloqueados sigue siendo perfectamente compatible.

0

Esto siguiente enlace:

http://www.lysator.liu.se/c/c-faq/c-2.html

tiene la explicación que busca.

+0

Los enlaces deben ir en comentarios, no en respuestas. Las respuestas deben contener algún contexto. – unkulunkulu

+0

seguro ... pero ¿y si el enlace contiene la respuesta exacta? –

+0

Eche un vistazo a http://stackoverflow.com/questions/how-to-answer :) Le sugiero que cite alguna parte del contenido vinculado. – unkulunkulu

Cuestiones relacionadas