Xét hai files sau đây:
/* foo.c */
#include <stdio.h>
void f(void);
int x=1;
int main() {
f();
printf("x = %d\n", x);
return 0;
}
và
/* bar.c */
int x;
void f() {
x = 2;
}
Nếu bây giờ mình dịch và liên kết chúng
[NQH] MFM:~/tmp$ gcc -o foobar foo.c bar.c
Thì gcc có báo lỗi không? Nếu không thì khi chạy chương trình foobar cho ra kết quả gì, tại sao?

11 Comments
GCC biên dịch ko có lỗi.
Kết quả là x = 2.
Thật ra em ko giỏi C lắm, nên ban đầu nghĩ là dịch sẽ có lỗi (vì khai báo x hai lần). Nhưng đúng là C ko chặt chẽ, nó bỏ qua dòng khai báo int x để tiếp tục compile. Một khi đã compile được thì chắc chắn nó sẽ chạy hàm f(), và kết quả là 2.
Em đã thử compile với g++ thì nhảy lỗi multiple definition ngay.
Còn tại sao gcc nhảy qua dòng int x thì em ko biết tại sao. Chắc đó là dòng không có operator nên compiler ko thực thi?
C nó phân biệt declaration với cả definition nên nó ko báo lỗi chứ ko phải bỏ qua đâu.
Xin lỗi, nhầm. Ansi C cho phép multiple definitions:
G.5.11 Multiple external definitions
There may be more than one external definition for the identifier of an object, with or without the explicit use of the keyword extern. If the
definitions disagree, or more than one is initialized, the behavior is undefined.
C++ ko cho phép:
The One Definition Rule (ODR) is an important concept in the C++ programming language. It’s defined in the ISO C++ Standard(ISO/IEC 14882) 2003, at section 3.2.
Cám ơn bạn 9diov.
Nếu vậy tại sao khi definition int x = 3 trong file bar.c thì compiler lại báo lỗi?
Hi HolyNoob, 9diov,
Trên Unix, các symbols được linker (ld) chia làm 2 loại: strong symbols & weak symbols. Các functions và initialized global variables như (x=2) ở foo.c là strong symbols. Còn uninitialized global variables được gán weak symbols.
Khi có conflict của 2 strong symbols thì sẽ báo lỗi (đó là lý do tại sao nếu gán x=3 trong bar.c thì compiler báo lỗi).
Nếu có conflict giữa một weak symbol & một trong symbol thì chọn strong symbol (đây là câu trả lời cho câu đố).
Còn khi có conflict giữa các weak symbols thì chọn bất kỳ cái nào, behavior undefined.
Cám ơn thầy Hưng. Vừa google được cái link này cho mọi người tham khảo: (cuốn sách “Linkers and loaders”)
http://books.google.com.sg/books?id=h34d_jr2iikC&pg=PA129&lpg=PA129&dq=strong+symbols+%26+weak+symbols&source=bl&ots=IxJdONLV0_&sig=Iofak2NDpnwlEMbyTLr6sFosSFc&hl=en&ei=dcStSp-DKpSIkAXe4d2VBg&sa=X&oi=book_result&ct=result&resnum=7#v=onepage&q=strong%20symbols%20%26%20weak%20symbols&f=false
Theo ISO/IEC 9899 thì:
(Section 6.9.2) “A declaration of an identifier for an object that has file scope without an initializer, and without a storage-class specifier or with the storage-class specifier static, constitutes a tentative definition.”
trong khi:
(Section 6.9, 5) “If an identifier declared with external linkage is used in an expression (other than as part of the operand of a sizeof operator whose result is an integer constant), somewhere in the entire program there shall be exactly one external definition for the identifier; otherwise, there shall be no more than one.137)”
Tức là khai báo x ở bar.c là tentative definition, không bị tính là external definition; khai báo x với initializer bị tính là external definition (lưu ý external ở đây không liên quan gì đến external linkage) (trả lời HolyNob)
Đây là ví dụ “trong sách”
int i1 = 1; // definition, external linkage
static int i2 = 2; // definition, internal linkage
extern int i3 = 3; // definition, external linkage
int i4; // tentative definition, external linkage
static int i5; // tentative definition, internal linkage
int i1; // valid tentative definition, refers to pre vious
int i2; // 6.2.2 renders undefined, linkage disagreement
int i3; // valid tentative definition, refers to pre vious
int i4; // valid tentative definition, refers to pre vious
int i5; // 6.2.2 renders undefined, linkage disagreement
extern int i1; // refers to pre vious, whose linkage is external
extern int i2; // refers to pre vious, whose linkage is internal
extern int i3; // refers to pre vious, whose linkage is external
extern int i4; // refers to pre vious, whose linkage is external
extern int i5; // refers to pre vious, whose linkage is internal
Tóm lại gcc theo đúng hướng dẫn
Nói thêm là nếu xài gcc với flag “-fno-common” sẽ tạo lỗi “multi-definition”.
Cám ơn aHưng, 9diov và Đức đã chỉ giáo
Cám ơn bạn Đức, hồi trước có chép lại mấy cài này vào sổ tay mà giờ ko tìm lại được nữa. Cũng tại lâu rồi ko code C.
chào bạn admin bạn cho mình hỏi bạn có tài liệu gì hay về lập trình php không có thể chia sẻ cho mình được không hiện tại mình mới theo học lập trình nhưng hiểu biết ít quá nên muốn tìm ít tài liệu đọc mà không biết tài liệu nào hay mình nghĩ bạn đi trước nên biết nhiều có thể chia sẻ cho mình nhé .Có gì bạn cứ email vào dịa chỉ mình đk đó . Cảm ơn bạn nhiều