Đố

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;
}

/* 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?

Chủ đề : Lập trình. Bookmark the permalink. Trackbacks are closed, but you can post a comment.

11 Comments

  1. HolyNoob
    Posted 11/09/2009 at 2:30 pm | Permalink

    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?

  2. 9diov
    Posted 12/09/2009 at 3:20 am | Permalink

    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.

  3. 9diov
    Posted 12/09/2009 at 3:49 am | Permalink

    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.

  4. HolyNoob
    Posted 12/09/2009 at 4:52 pm | Permalink

    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?

  5. Posted 13/09/2009 at 3:28 am | Permalink

    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.

  6. 9diov
    Posted 13/09/2009 at 8:25 pm | Permalink

    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

  7. Đức
    Posted 15/09/2009 at 2:02 pm | Permalink

    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 :)

  8. Đức
    Posted 15/09/2009 at 2:06 pm | Permalink

    Nói thêm là nếu xài gcc với flag “-fno-common” sẽ tạo lỗi “multi-definition”.

  9. HolyNoob
    Posted 15/09/2009 at 2:24 pm | Permalink

    Cám ơn aHưng, 9diov và Đức đã chỉ giáo

  10. 9diov
    Posted 19/09/2009 at 11:32 pm | Permalink

    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.

  11. Posted 16/07/2010 at 7:42 pm | Permalink

    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

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>