2010-09-11 25 views

Trả lời

27

gcc, trong chế độ C:

Uninitialised globals mà không được công bố extern được coi là biểu tượng "chung", không những biểu tượng yếu.

Các ký hiệu chung được hợp nhất vào thời gian liên kết để tất cả chúng đều tham chiếu đến cùng một bộ nhớ; nếu có nhiều hơn một đối tượng cố gắng khởi tạo một biểu tượng như vậy, bạn sẽ nhận được một lỗi liên kết thời gian. (Nếu họ không rõ ràng khởi tạo bất cứ nơi nào, họ sẽ được đặt trong BSS, tức là khởi tạo vào 0.)

gcc, trong C++ chế độ:

Không giống nhau - nó không làm biểu tượng chung. Các hình cầu "Uninitialised" không được khai báo extern được khởi tạo ngầm định với một giá trị mặc định (0 cho các kiểu đơn giản, hoặc hàm tạo mặc định).


Trong cả hai trường hợp, ký hiệu yếu cho phép biểu tượng khởi tạo bị ghi đè bởi biểu tượng khởi tạo không yếu cùng tên tại thời gian liên kết.


Để minh họa (tập trung vào các trường hợp C ở đây), tôi sẽ sử dụng 4 biến thể của một chương trình chính, đó là tất cả cùng trừ cách mà global bị tuyên bố:

  1. main_init.c:

    #include <stdio.h> 
    
    int global = 999; 
    
    int main(void) { printf("%d\n", global); return 0; } 
    
  2. main_uninit.c, whic h bỏ qua việc khởi tạo:

    #include <stdio.h> 
    
    int global; 
    
    int main(void) { printf("%d\n", global); return 0; } 
    
  3. main_uninit_extern.c, có thêm các extern keyword:

    #include <stdio.h> 
    
    extern int global; 
    
    int main(void) { printf("%d\n", global); return 0; } 
    
  4. main_weak_init.c, mà initialises global và tuyên bố đó là một biểu tượng yếu:

    #include <stdio.h> 
    
    int global __attribute__((weak)) = 999; 
    
    int main(void) { printf("%d\n", global); return 0; } 
    

another_def.c khởi tạo cùng một toàn cầu:

int global = 1234; 

Sử dụng main_uninit.c ngày của riêng mình mang đến cho 0:

$ gcc -o test main_uninit.c && ./test 
0 

nhưng khi another_def.c được bao gồm như là tốt, global được khởi tạo một cách rõ ràng và chúng tôi nhận được kết quả mong đợi:

$ gcc -o test main_uninit.c another_def.c && ./test 
1234 

(Lưu ý trường hợp này không thành công nếu bạn đang sử dụng C++.)

Nếu chúng ta thử với cả main_init.canother.def.c thay vào đó, chúng tôi có 2 initialisations của global, mà sẽ không làm việc:

$ gcc -o test main_init.c another_def.c && ./test 
/tmp/cc5DQeaz.o:(.data+0x0): multiple definition of `global' 
/tmp/ccgyz6rL.o:(.data+0x0): first defined here 
collect2: ld returned 1 exit status 

main_uninit_extern.c ngày của riêng mình sẽ không làm việc ở tất cả - từ khóa extern gây ra biểu tượng là một tài liệu tham khảo bên ngoài bình thường chứ không phải là một biểu tượng phổ biến, do đó mối liên kết phàn nàn:

$ gcc -o test main_uninit_extern.c && ./test 
/tmp/ccqdYUIr.o: In function `main': 
main_uninit_extern.c:(.text+0x12): undefined reference to `global' 
collect2: ld returned 1 exit status 

Nó hoạt động tốt khi khởi động từ another_def.c i s bao gồm:

$ gcc -o test main_uninit_extern.c another_def.c && ./test 
1234 

Sử dụng main_init_weak.c ngày của riêng mình cung cấp cho các giá trị chúng ta khởi tạo các biểu tượng yếu (999), như không có gì để ghi đè lên nó là:

$ gcc -o test main_init_weak.c && ./test 
999 

Nhưng kéo trong định nghĩa khác từ another_def.c không hoạt động trong trường hợp này, bởi vì định nghĩa mạnh mẽ có ghi đè định nghĩa yếu trong main_init_weak.c:

$ gcc -o test main_init_weak.c another_def.c && ./test 
1234 
+0

Câu trả lời hay, thx. Tôi có thể hỏi nếu nó áp dụng cho C++? –

+0

Giải thích tuyệt vời với các ví dụ. Bạn cũng có thể bao gồm tương tác yếu với các chức năng không? – Tomek

+0

@Maciej - điểm tốt, quên điều này đã được gắn thẻ với cả hai khi viết câu trả lời của tôi ban đầu! Không, nó không giống với C++, vì vậy tôi đã cập nhật câu trả lời cho phù hợp. –

3

Đây có phải là ý của bạn không?

weak.c

#include <stdio.h> 

int weak; /* global, weak, zero */ 

int main(void) { 
    printf("weak value is %d.\n", weak); 
    return 0; 
} 

strong.c

int weak = 42; /* global, strong, 42 */ 

mẫu chạy

$ gcc weak.c 
$ ./a.out 
weak value is 0. 
$ gcc weak.c strong.c 
$ ./a.out 
weak value is 42.

Các int weak; trong weak.c là một tuyên bố, không phải là một định nghĩa. Hoặc bạn có thể nói đó là định nghĩa dự kiến. Định nghĩa thực là trong strong.c khi tệp đối tượng đó được liên kết trong chương trình cuối cùng hoặc trong weak.c nếu không. Đây là một phần mở rộng phổ biến, một phần mở rộng mà gcc sử dụng (nhờ Andrey).

+2

Không đúng sự thật. 'int weak' trong' weak.c' là một định nghĩa.Một dự kiến, nhưng định nghĩa tuy nhiên. Chương trình ở trên * không * vi phạm các quy tắc của ngôn ngữ, nhưng được hỗ trợ dưới dạng tiện ích mở rộng không chuẩn. – AnT

3

Bất kỳ định nghĩa nào của một biểu tượng toàn cầu là hành vi không xác định, vì vậy gcc (hay đúng hơn là trình liên kết binutils GNU) là miễn phí để làm bất cứ điều gì nó muốn. Trong thực tế, nó tuân theo hành vi truyền thống để tránh vi phạm mã dựa vào hành vi này.

+0

Bạn có ý nghĩa nhiều ** định nghĩa ** không? – pmg

+0

Có, sửa chữa. –

9

Câu hỏi được dựa trên tiền đề không chính xác. Các biến toàn cục chưa được khởi tạo không phải là các biểu tượng yếu.

Dường như câu hỏi đề cập đến khả năng xác định cùng một đối tượng chưa được khởi tạo với liên kết bên ngoài trong nhiều đơn vị dịch. Chính thức, nó không được phép - nó là một lỗi trong cả C và C++.Tuy nhiên, ít nhất là trong C được công nhận theo tiêu chuẩn C99 là "mở rộng chung" của ngôn ngữ, thực hiện trong nhiều trình biên dịch thực tế cuộc sống

J.5 mở rộng Common

J.5.11 Nhiều ngoài định nghĩa

có thể có nhiều hơn một bên ngoài định nghĩa cho các định danh của một đối tượng , có hoặc không có rõ ràng sử dụng từ khóa bên ngoài; nếu các định nghĩa không đồng ý hoặc nhiều hơn được khởi tạo, hành vi là không xác định (6.9.2).

Lưu ý rằng, trái với niềm tin phổ biến, ngôn ngữ C nghiêm cấm giới thiệu nhiều định nghĩa của thực thể có liên kết bên ngoài trong chương trình, giống như C++.

6,9 định nghĩa bên ngoài

Một định nghĩa bên ngoài là một tuyên bố bên ngoài đó cũng là một nét của một hàm (trừ một định nghĩa inline) hoặc một đối tượng. Nếu một định tuyên bố với bên ngoài liên kết được sử dụng trong một biểu thức (trừ như một phần của toán hạng của một nhà điều hành sizeof mà kết quả là một hằng số số nguyên), ở đâu đó trong toàn bộ chương trình có phải chính xác một định nghĩa bên ngoài cho số nhận dạng ; nếu không, sẽ có không nhiều hơn.

Tuy nhiên, phần mở rộng cho phép điều này khá phổ biến với nhiều trình biên dịch C, trong đó GCC chỉ xảy ra là một.

+1

Tính năng trình liên kết hỗ trợ là bắt buộc để triển khai các khối phổ biến FORTRAN. Bạn sẽ nhận thấy rằng GCC không bao gồm trình biên dịch FORTRAN trong bộ sưu tập. Vì các thư viện hỗ trợ được triển khai trong C, nên không có gì ngạc nhiên khi có một lượng nhỏ rò rỉ vô hại từ trình liên kết. – RBerteig

+0

Bạn có thể đưa ra ví dụ về định nghĩa bên ngoài và khai báo bên ngoài không? – Thomson

+0

Nó không chỉ FORTRAN, C++ nội tuyến chức năng và các mẫu instantiated ngầm cũng yếu. – MSalters

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