2012-06-23 14 views
5

Quiero usar la variable a de foo.c en main.c, y escribo:¿Qué hay de malo con este programa C que utiliza la palabra clave extern?

foo.c 
#include <stdio.h> 

int a[] = {3, 2}; 

void foo() 
{ 
    printf("foo\taddress of a:%x\n", a); 
    printf("foo\tvalue of a[0]:%x\n", a[0]); 
} 

main.c 
#include <stdio.h> 

extern int *a; 

int main(void) 
{ 
    foo(); 
    printf("main\taddress of a : %x\n", a); 
    printf("main\tvalue of a[0] : %x\n", a[0]); 

    return 0; 
} 

y la salida de resultado:

foo address of a:804a014 
foo value of a[0]:3 
main address of a : 3 
Segmentation fault (core dumped) 

por qué?

+0

¿Está utilizando un sistema operativo de 64 bits? –

+0

@PaulR Yo uso 32 bits ubuntu 12.04 –

Respuesta

8

El tipo de a es int[2], no int*. Inténtelo de nuevo con

extern int a[2]; 

El compilador de C no puede escribir a comprobar a través de los archivos de origen. Por lo tanto, cuando diga int* a, el compilador asumirá que está diciendo la verdad, usará semántica de puntero y no emitirá ningún error de compilación.

Existen diferencias sutiles entre las matrices y los punteros. Supongamos un sistema de 32 bits. A continuación, el contenido de "a" se distribuirá como esto:

 
    a 
0x100  0x104  0x108 ← address 
    +-----------+----------+ 
    |   3 |  2 | ← content 
    +-----------+----------+ 

Cuando a es un array,

  • El valor de la expresión a se se convertirá en la dirección de a. Por lo tanto, cuando imprima a, obtendrá su dirección, es decir, "0x100".
  • La operación a[n] en C es equivalente a *(a + n), es decir, avanza la dirección a por n unidades, y luego lo desreferencia para obtener el contenido. Por lo tanto, a[0] es equivalente a *0x100, que devuelve el contenido a 0x100, es decir, "3".

Cuando a es un puntero ,

  • El "valor" de a es el contenido en la dirección indicada. De hecho, esta es la norma, el tipo de matriz es un caso especial. Por lo tanto, cuando imprima a, obtendrá el contenido en esa dirección, es decir, "3".
  • La operación a[n] sigue siendo *(a + n). Por lo tanto, a[0] es equivalente a *3, lo que causa un error de segmentación porque la dirección "3" no es válida.
+0

Pero 'int a [2]' es un puntero, también, excepto que el compilador simplemente sabe que es el tamaño en tiempo de compilación. ¿O mi entendimiento está mal aquí? –

+0

Pero este programa puede compilarse correctamente, sin error. –

+4

@NiklasR: No 'int a [2]' no es un puntero, es una matriz. 'extern int a [];' funcionaría también así que la diferencia no es el tamaño conocido. –

1

Debe usar tipos consistentes para los objetos declarados en diferentes unidades de traducción.

Dado int a[] = {2, 3};, cualquiera de las declaraciones extern int a[]; o extern int a[2]; sería compatible, mientras que extern int *a; no como punteros y matrices son tipos completamente separados.

La única cosa especial acerca de las matrices es el cuando el nombre de una matriz aparece en cualquier contexto expresión que no sea como un operando a "dirección de" (unario &) o sizeof, se convierten automáticamente a un puntero a su primera elemento.Esto es lo que proporciona la compatibilidad de sintaxis entre matrices e indicadores, pero no son del mismo tipo.

Considere estas dos funciones para un ejemplo comentado. Tenga en cuenta que la expresión a se convierte en un puntero a su primer elemento en la segunda (y técnicamente tercera) printf de la primera función, pero no en la primera printf donde es el operando a &.

#include <stdio.h> 

void print_array_info(void) 
{ 
    extern int a[]; 
    printf("address of a: %p\n", (void*) &a); // prints address of a 
    printf(" converted a: %p\n", (void*) a); // prints address of a[0] 
    printf("value of a[0]: %x\n", a[0]);   // prints value of a 
} 

void print_pointer_info(void) { 
    extern int a[]; 
    int *b = a; // == &a[0] 

    printf("address of b: %p\n", (void*) &b); // prints address of b 
    printf(" value of b: %p\n", (void*) b); // prints value of b (== &a[0]) 
    printf("value of b[0]: %x\n", b[0]);  // prints value of b[0] (== a[0]) 
} 

Nota que utilizo para imprimir %p punteros y convertir explícitamente a void*.

Cuestiones relacionadas