2014-10-22 16 views
7

Nếu tôi có:C, chức năng nội tuyến và GCC

inline int foo(void) 
{ 
    return 10 + 3; 
} 

int main(void) 
{ 
    foo(); 
} 

với GCC file biên dịch tốt nhưng mối liên kết trả undefined reference to foo

Thay vào đó nếu tôi loại bỏ inline mối liên kết được hạnh phúc!

Có vẻ như số nhận dạng của định nghĩa bên ngoài hiển thị với trình liên kết nhưng số nhận dạng của định nghĩa nội tuyến thì không.

Ngoài ra, nếu tôi biên dịch với -O3 thì cờ trình liên kết sẽ thấy số nhận dạng của định nghĩa nội dòng.

Sự cố là gì?

+3

https://gcc.gnu.org/onlinedocs/gcc/Inline.html – Ashalynd

+0

@Ashalynd câu trả lời có thể là ở đâu đó, nhưng đó là gì? – harold

+0

Tôi tự hỏi liệu sự cần thiết cho 'extern' là GCC cụ thể hoặc một phần của một số tiêu chuẩn C. 'inline' được thêm vào C99 tôi tin, nhưng tôi không biết nó ảnh hưởng đến liên kết như thế nào. –

Trả lời

4

Được rồi, vì vậy sau khi đọc qua VivienG's link, tôi nghĩ rằng tôi đã hiểu được lý do chính xác đằng sau thông báo lỗi này. Đó là khó hiểu và gây hiểu lầm (ít nhất là với tôi, nó không nên xảy ra nếu bạn đã có chỉ là một đơn vị dịch thuật), tuy nhiên nó có thể giải thích:

  • Giả sử các trình biên dịch không muốn thực sự inline mã, nó phải biết nơi để đặt chức năng đó, đặc biệt là khi nó được sử dụng trong nhiều đơn vị dịch thuật.

  • Cách tiếp cận cổ điển là tạo nhiều bản sao, một bản sao cho mỗi đơn vị dịch (hoặc ít nhất là đối với các đơn vị dịch mà nó được sử dụng).

  • Điều này có thể gây ra sự cố, ví dụ: khi cố gắng thực hiện một số phép so sánh con trỏ hàm (vẫn để nguyên câu hỏi tại sao bạn lại làm điều đó).

Để chống lại điều này (và các vấn đề khác mà tôi có thể không liệt kê ở đây), họ đã suy nghĩ của một số thực sự khá gọn gàng (mặc dù - như đã đề cập - theo ý kiến ​​của tôi gây hiểu lầm) giải pháp:

Bạn khai báo hàm như inline theo cách bạn biết, nhưng đồng thời bạn cho trình biên dịch biết nơi đặt phiên bản không nội tuyến với từ khóa extern.

Vì vậy, trong ví dụ của bạn, bạn muốn giữ cho chức năng của bạn như nó vốn có và đặt nó trong một tập tin tiêu đề (vì vậy nó được biết đến nơi nó sẽ được sử dụng):

inline int foo(void) 
{ 
    return 10 + 3; 
} 

Bên cạnh đó, để nói với trình biên dịch nơi đặt phiên bản không được nội tuyến, bạn sẽ phải thêm một bản khai báo "chuyển tiếp" khác vào một đơn vị dịch:

extern inline int foo(void); 

Vì vậy, toàn bộ khái niệm cơ bản được đảo ngược khi so sánh với các chức năng cổ điển: trong tiêu đề và sau đó khai báo ngắn chỉ trong một tệp.

Như đã đề cập, khi sử dụng tham số -O3, tất cả mã được đánh dấu bằng inline thực sự được gạch chân, điều này sẽ không gây ra sự cố.

+0

Hoặc mã hoạt động tốt nếu tôi đặt tĩnh, nhưng tôi không 't hiểu làm thế nào điều này ảnh hưởng đến mối liên kết ... – xdevel2000

+0

Đánh dấu nó 'tĩnh' đã nói với trình biên dịch" ở đây và chỉ ở đây ", do đó, nó một lần nữa biết nơi để đặt chức năng. – Mario

+0

Có, tôi có thể hiểu nhưng bạn giải thích điều này luôn từ chế độ xem trình biên dịch. Tuy nhiên tĩnh là một từ khóa liên kết có ý nghĩa cho mối liên kết. Vì vậy, xin lỗi một lần nữa, nhưng điều này không giải thích tại sao mối liên kết báo cáo lỗi ... – xdevel2000

2

Bạn cần phải vượt qua -std=gnu11 đến trình biên dịch của mình.

chính gcc. -o main -std = gnu11

EDIT: post trả lời nhiều câu hỏi này.

+1

Chăm sóc để giải thích lý do đằng sau điều này? Tôi không thấy lý do tại sao mã ban đầu không nên biên dịch với các tham số mặc định. – Mario

+0

Bạn có thể xem [bài đăng này] (http://gustedt.wordpress.com/2010/11/29/myth-and-reality-about-inline-in-c99/) – VivienG

+0

Tôi nghĩ rằng tôi đã hiểu lý do tại sao nó actuall không thành công với mã đã cho ở trên, nhưng liên kết của bạn không giải thích làm thế nào/tại sao hành vi này thay đổi với * gnu11 * một lần nữa. Đó không phải là trên trang hoặc tôi đã bỏ lỡ nó? – Mario

1

Trong ngôn ngữ C từ C99, nếu bạn muốn một hàm nội tuyến chỉ được sử dụng trong một đơn vị biên dịch, bạn nên khai báo nó là "nội tuyến tĩnh" và mọi thứ sẽ ổn. Nếu không, hàm nội tuyến được khai báo không có "tĩnh" phải được khai báo là "nội dòng bên ngoài" trong chính xác một đơn vị biên dịch.

Thông thường, bạn sẽ sử dụng "nội tuyến tĩnh" trong tệp .c hoặc bạn sẽ sử dụng "nội tuyến" trong tệp .h và khai báo hàm dưới dạng "extern inline" trong chính xác một tệp .c.

+0

này nên được sao lưu với tham chiếu chuẩn –

1

Tôi nghĩ rằng user3629249's comment nhấn móng trên đầu. Nếu bạn biên dịch mã bằng -O3, cuộc gọi tới foo() sẽ bị xóa. tức là, lắp ráp khác biệt:

Với -O3:

main: 
.LFB4: 
    .cfi_startproc 
    xorl %eax, %eax 
    ret 
    .cfi_endproc 

Without -O3:

main: 
.LFB1: 
    // ... 
    call foo 
    // ... 

Tiêu chuẩn C nói trong chú thích 161 (§6.9/5):

Vì vậy , nếu một số nhận dạng được khai báo với liên kết bên ngoài không được sử dụng trong một sự thể hiện ion, không cần định nghĩa bên ngoài cho nó.

Hàm tại phạm vi tệp có liên kết bên ngoài nhưng hàm được khai báo nội tuyến mà không có từ khóa bên ngoài là định nghĩa nội tuyến. Trong §6.9/5 nó nói:

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

4 [...] Như đã thảo luận trong 6.7, một tuyên bố cũng khiến lưu trữ được dành riêng cho một đối tượng hoặc một hàm có tên là định danh.

tức là, sử dụng thông số lớp lưu trữ static hoặc extern.

+0

Vì vậy, định nghĩa bên ngoài hoạt động như khai báo quá và trình liên kết hoạt động tốt ... nhưng định nghĩa nội tuyến không ... – xdevel2000

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