2014-12-29 12 views
5

Nói rằng tôi có một đơn vị biên soạn file1.c, mà tuyên bố một biến file-phạm vi như vậy:Có hợp lệ để xử lý một extern global như const khi định nghĩa không phải là const?

int my_variable = 12; 

Sau đó, trong một đơn vị biên soạn file2.c, tôi tạo ra một tuyên bố bên ngoài cho rằng biến, nhưng khai báo nó như const:

extern const int my_variable; 

này sẽ biên dịch và làm việc tốt với gcc, sử dụng -Wall -Wextra -ansi -pedantic. Tuy nhiên, tiêu chuẩn C89 cho biết Đối với hai loại đủ điều kiện để tương thích, cả hai loại phải có phiên bản đủ điều kiện giống nhau của loại tương thích. Thêm const vào khai báo sẽ thêm hạn chế thay vì tránh một. Đây có phải là C an toàn và hợp lệ không? Thực hành tốt nhất trong việc thiết lập điều này với các tệp tiêu đề là gì?

+2

Có thể trình biên dịch sẽ khiếu nại nếu đó là cách khác, nếu bạn loại bỏ bộ tách phân đoạn 'const'. Nhưng tôi không chắc lắm. –

+2

Tôi khá chắc chắn [C - Truy cập thông tin không const thông qua const] (http://stackoverflow.com/q/8051969/1708801) bao gồm trường hợp này. –

+0

Có lẽ đáng để chỉ định phiên bản chuẩn C chính xác mà bạn đang đề cập đến. – Clifford

Trả lời

5

Rõ ràng là không được xác định là các khai báo không khớp. Như bạn đã lưu ý, const intint không phải là loại tương thích. Chỉ cần chẩn đoán khi chúng xuất hiện trong cùng một phạm vi.

Nó không phải là an toàn trong thực tế một trong hai, xem xét

$ cat test1.c 
#include <stdio.h> 

extern const int n; 
void foo(void); 

int main(void) { 
    printf("%d\n", n); 
    foo(); 
    printf("%d\n", n); 
} 
$ cat test2.c 
int n; 
void foo(void) { ++n; } 
$ gcc -std=c99 -pedantic test1.c test2.c && ./a.out 
0 
1 
$ gcc -O1 -std=c99 -pedantic test1.c test2.c && ./a.out 
0 
0 

Gcc giả định rằng n là không thay đổi bởi foo() khi tối ưu hóa, bởi vì nó có thể giả định nghĩa của n là của một loại tương thích, do đó const .

Cơ hội là bạn nhận được hành vi mong đợi cũng như volatile -qualifying n trong test1.c, nhưng theo tiêu chuẩn C thì điều này vẫn chưa được xác định.

Cách tốt nhất tôi có thể nghĩ ra để ngăn chặn người dùng vô tình sửa đổi n là khai báo một con trỏ đến const, cái gì đó dọc

int my_real_variable; 
const int *const my_variable = &my_real_variable; 

hoặc có lẽ một số vĩ mô

#define my_variable (*(const int *)&my_variable) 

Với C99, my_real_variable có thể tránh được thông qua một hợp chất theo nghĩa đen:

const int *const my_variable_ptr = &(int){ 12 }; 

Sẽ là hợp pháp khi bỏ đi const tại đây (vì chính đối tượng int không phải là const), nhưng sẽ cần phải thực hiện, ngăn ngừa sửa đổi tình cờ.

+0

Điều này trả lời câu hỏi tốt; ví dụ đó là những gì tôi đang tìm kiếm. – Quackmatic

4

Trong trường hợp này định nghĩa và khai báo xuất hiện trong các đơn vị dịch riêng biệt, do đó trình biên dịch không thể thực hiện bất kỳ kiểm tra loại hoặc vòng loại nào. Các biểu tượng được giải quyết bởi liên kết và trong trường hợp này có vẻ như trình liên kết không thực thi khớp định tính này.

Nếu định nghĩa và khai báo xuất hiện trong cùng một đơn vị dịch; ví dụ: nếu bạn đặt tuyên bố extern trong một tệp tiêu đề và , hãy bao gồm nó trong tệp1.c, thì tôi sẽ tưởng tượng rằng trình biên dịch sẽ khiếu nại. Bằng cách đặt chúng trong các đơn vị dịch riêng biệt, trình biên dịch không bao giờ nhìn thấy cả hai vì vậy không thể thực hiện kiểm tra.

+0

Tôi giả định từ điều này rằng hành vi của mối liên kết trong tình huống này được thực hiện cụ thể? Trong tình huống này, tất cả những gì tôi biết là trang trí tên C không chú ý đến 'const' (trong khi C++ làm ví dụ.) – Quackmatic

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