2012-06-23 33 views
5

Tôi muốn sử dụng biến một từ foo.c trong main.c, và tôi viết:Điều gì là sai với chương trình C này bằng cách sử dụng từ khóa 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; 
} 

và đầu ra kết quả:

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

tại sao?

+0

Bạn đang sử dụng hệ điều hành 64 bit? –

+0

@PaulR Tôi sử dụng 32 bit ubuntu 12.04 –

Trả lời

8

Loại aint[2], không phải int*. Hãy thử lại với

extern int a[2]; 

C biên dịch không thể Type-kiểm tra trên file nguồn. Vì vậy, khi bạn nói int* a, trình biên dịch sẽ giả sử bạn đang nói sự thật, sử dụng ngữ nghĩa con trỏ, và sẽ không phát hành bất kỳ lỗi trình biên dịch nào.

Có sự khác biệt tinh tế giữa mảng và con trỏ. Giả sử một hệ thống 32-bit. Sau đó, nội dung của "a" sẽ được phân phối như thế này:

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

Khi a là một mảng,

  • Giá trị của biểu thức a được sẽ được chuyển đổi sang địa chỉ của a. Do đó, khi bạn in a, bạn sẽ nhận được địa chỉ của nó, tức là "0x100".
  • Hoạt động a[n] trong C tương đương với *(a + n), tức là tạm ứng địa chỉ a bởi n đơn vị và sau đó dereference nó để có được nội dung. Do đó, a[0] tương đương với *0x100, trả về nội dung ở 0x100, tức là "3".

Khi a là một con trỏ ,

  • Các "giá trị" của a là nội dung tại địa chỉ cung cấp. Trong thực tế, đây là tiêu chuẩn, loại mảng là một trường hợp đặc biệt. Do đó, khi bạn in a, bạn sẽ nhận được nội dung tại địa chỉ đó, tức là "3".
  • Thao tác a[n] vẫn là *(a + n). Do đó, a[0] tương đương với *3, gây ra lỗi phân đoạn vì địa chỉ "3" không hợp lệ.
+0

Nhưng 'int a [2]' cũng là con trỏ, ngoại trừ trình biên dịch chỉ biết kích thước của nó ở thời gian biên dịch. Hoặc là sự hiểu biết của tôi sai ở đây? –

+0

Nhưng chương trình này có thể biên dịch thành công, không có lỗi. –

+4

@NiklasR: Không có 'int a [2]' không phải là một con trỏ, nó là một mảng. 'extern int a [];' sẽ hoạt động tốt nên sự khác biệt không phải là kích thước đã biết. –

1

Bạn phải sử dụng các loại nhất quán cho các đối tượng được khai báo trên các đơn vị dịch khác nhau.

Cho int a[] = {2, 3};, một trong các tờ khai extern int a[]; hoặc extern int a[2]; sẽ tương thích trong khi extern int *a; sẽ không phải là con trỏ và mảng là các loại hoàn toàn riêng biệt.

Một trong những điều đặc biệt về mảng là khi tên của một mảng xuất hiện trong bất kỳ bối cảnh biểu hiện khác hơn là như một toán hạng để "địa chỉ của" (unary &) hoặc sizeof, họ sẽ được tự động chuyển đổi sang một con trỏ đến đầu tiên của họ thành phần.Đây là những gì cung cấp khả năng tương thích cú pháp giữa các mảng và con trỏ, nhưng chúng không cùng loại.

Hãy xem xét hai hàm này cho ví dụ đã nhận xét. Lưu ý rằng biểu thức a được chuyển đổi thành con trỏ thành phần tử đầu tiên trong phần thứ hai (và thứ ba về mặt kỹ thuật) printf của hàm đầu tiên, nhưng không phải trong số printf đầu tiên trong đó toán hạng là &.

#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]) 
} 

Lưu ý rằng tôi sử dụng %p để in con trỏ và rõ ràng đúc để void*.

Các vấn đề liên quan