2016-08-27 53 views
11

Hãy nói rằng tôi có chương trình sau đây (hello.c):Linking file/header trong C

#include <stdio.h> 
#include <math.h> 

#define NAME "ashoka" 


int main(int argc, char *argv[]) 
{ 
    printf("Hello, world! My name is %s\n", NAME); 
} 

Vì vậy, như tôi hiểu nó quá trình biên soạn chương trình này là:

  1. Tiền xử lý: sẽ sao chép-dán các tờ khai stdio.hmath.h và thay thế NAME với "ashoka".

    clang -E hello.c 
    
  2. Biên soạn: sẽ biến mã vào lắp ráp đang

    clang -S hello.c 
    

    tập sản xuất: hello.s

  3. Lắp ráp: transform lắp ráp mã để đối tượng đang

    clang -c hello.s 
    

    tập sản xuất: hello.o

  4. Linking: kết hợp đối tượng tập tin vào một tập tin mà chúng tôi sẽ thực hiện.

    clang hello.o -lm 
    

    OR (giả sử tôi cũng muốn liên kết hello2.o)

    clang hello.o hello2.o 
    

Vì vậy, ở đây đến các câu hỏi:

  1. là quá trình mô tả đúng?

  2. Trong giai đoạn liên kết, chúng tôi liên kết với nhau .o (Mã đối tượng) tệp. Tôi biết rằng math.h nằm trong thư mục /usr/include. Đâu là math.o? Làm thế nào để liên kết tìm thấy nó?

  3. .a (thư viện tĩnh) và .so (thư viện động) nào trong Linux? Và chúng liên quan như thế nào với các tệp .o và giai đoạn liên kết?

  4. Giả sử tôi muốn chia sẻ thư viện mà tôi đã tạo với thế giới. Tôi có một tệp mylib.c, trong đó tôi đã khai báo và triển khai các chức năng của mình. Làm cách nào để tôi chia sẻ điều này để mọi người có thể đưa nó vào dự án của họ bằng cách thực hiện một trong hai cách #include <mylib.h> hoặc #include "mylib.h"?

+0

Mã của bạn sẽ phát ra lỗi biên dịch do sự vắng mặt của việc đóng '' ' – MikeCAT

+0

@MikeCat thx. Đã chỉnh sửa – padawanTony

+0

Cảm ơn tất cả câu trả lời của bạn. Tôi cũng sẽ tạo một bài đăng mới về câu hỏi 4 sau khi tôi thực hiện một số nghiên cứu. – padawanTony

Trả lời

5
  1. Có, mặc dù thông qua lắp ráp là một bước bổ sung (bạn chỉ có thể biên dịch nguồn C thành đối tượng). Bên trong, trình biên dịch sẽ có nhiều giai đoạn hơn: phân tích cú pháp mã thành một AST, tạo mã trung gian (ví dụ: bitcoin LLVM cho clang), tối ưu hóa, v.v.
  2. math.h chỉ định nghĩa protypes cho thư viện toán chuẩn libm.a (mà bạn liên kết với -lm)). Các chức năng tự tồn tại trong các tệp đối tượng được lưu trữ bên trong libm.a (xem bên dưới).
  3. Thư viện tĩnh chỉ là lưu trữ của tệp đối tượng. Trình liên kết sẽ kiểm tra những ký hiệu nào được sử dụng và sẽ trích xuất và liên kết các tệp đối tượng xuất các ký hiệu đó. Các thư viện đó có thể được điều khiển bằng ar (ví dụ: ar -t liệt kê các tệp đối tượng trong thư viện). Thư viện động (hoặc được chia sẻ) không được bao gồm trong tệp nhị phân đầu ra. Thay vào đó, các ký hiệu mà mã của bạn cần được tải khi chạy.
  4. Bạn chỉ đơn giản là sẽ tạo ra một tập tin tiêu đề với extern nguyên mẫu ed của bạn:

    #ifndef MYLIB_H 
    #define MYLIB_H 
    
    extern int mylib_something(char *foo, int baz); 
    
    #endif 
    

    và vận chuyển nó với thư viện của bạn. Tất nhiên nhà phát triển cũng phải liên kết (thực tế) với thư viện của bạn.

Ưu điểm của thư viện tĩnh là đáng tin cậy: vì bạn đã liên kết mã của mình với phiên bản chính xác mà bạn chắc chắn rằng nó hoạt động. Các trường hợp khác có thể hữu ích khi bạn đang sử dụng các thư viện không phổ biến hoặc xuất hiện và bạn không muốn cài đặt chúng dưới dạng chia sẻ. Điều này đi kèm với chi phí tăng kích thước nhị phân.

Thư viện được chia sẻ tạo ra các tệp nhị phân nhỏ hơn (vì thư viện không ở dạng nhị phân) với dấu chân RAM nhỏ hơn (vì hệ điều hành có thể tải thư viện một lần và chia sẻ trong nhiều quy trình), nhưng chúng đòi hỏi nhiều hơn một chút để đảm bảo bạn đang tải chính xác những gì bạn muốn (ví dụ: xem DLL Hell trên Windows).

Như ghi chú @iharob, lợi thế của chúng không chỉ dừng ở kích thước nhị phân. Ví dụ, nếu một lỗi được cố định trong một thư viện được chia sẻ, tất cả các chương trình sẽ được hưởng lợi từ nó (miễn là nó không phá vỡ tính tương thích). Ngoài ra, các thư viện chia sẻ cung cấp sự trừu tượng giữa giao diện bên ngoài và việc triển khai thực hiện. Ví dụ, nói rằng một hệ điều hành cung cấp một thư viện cho các ứng dụng để giao tiếp với nó. Với các bản cập nhật, thay đổi giao diện hệ điều hành và việc triển khai thư viện theo dõi những thay đổi đó. Nếu nó được biên dịch như một thư viện tĩnh, tất cả các chương trình sẽ phải được biên dịch lại với phiên bản mới. Nếu đó là thư viện được chia sẻ, họ thậm chí sẽ không nhận thấy nó (miễn là giao diện bên ngoài vẫn giữ nguyên). Một ví dụ khác là các thư viện Linux bao bọc các khía cạnh hệ thống/phân phối cụ thể cho một giao diện chung.

+0

Tôi thích câu trả lời của bạn rất nhiều, nhưng * bạn không tải phiên bản không tương thích * có chút sai lệch. Nó là khá an toàn để giả định rằng nếu mã của bạn đã được biên dịch chống lại phiên bản chính xác của thư viện và liên kết với nó, sẽ không có bất ngờ. Ngoài ra, các thư viện chia sẻ có nhiều ưu điểm hơn các thư viện tĩnh bên cạnh kích thước của các tệp nhị phân. Một hệ điều hành hoàn chỉnh không có thư viện chia sẻ sẽ là một thử thách. –

+0

@iharob Viết lại phần đó và thêm nhiều hơn vào libs được chia sẻ. –

+0

@AndreaBiondo Câu trả lời hay. Nhưng bạn làm cho tôi có thêm một câu hỏi. Hãy nhìn vào câu hỏi mới được tạo ra 4. – padawanTony

2
  1. Vâng, đây là quá trình nói chung.
  2. Không có math.o tập tin, các -lm chuyển đổi các liên kết đến libm.so (một đối tượng chia sẻ, do đó: .so) nơi mà tất cả những biểu tượng theo yêu cầu của chức năng toán học khai báo trong math.hđược xác định.
  3. phép trả lời này trong hai phần

    thư viện tĩnh

          Are chỉ đơn giản là bộ sưu tập các đối tượng được lưu trong một định dạng lưu trữ.

    chung thư viện

          Are (trên linux) file ELF với các biểu tượng được xác định như chúng được định nghĩa trong các tập tin thực thi, bạn liên kết các chương trình để có thể sử dụng các biểu tượng này trong thời gian chạy và có bộ nạp tải các biểu tượng đó vào chương trình được sử dụng.

          này là khá nhiều giống nhau trên các nền tảng khác, như .dll s trên cửa sổ, họ về cơ bản được biên soạn chương trình mà thiếu một main() chức năng nên họ không thể được thực hiện trực tiếp. Thye chứa mã thực thi được nạp vào thời gian chạy. Bạn có thể tự mình thực hiện bằng cách sử dụng dlopen(3) trên Linux.


Note: trong mã bạn được đăng một số điều sẽ không xảy ra, bởi vì bạn đã không sử dụng bất cứ điều gì từ math.h để liên kết đến libm.so là hoàn toàn uneeded. Trình biên dịch cũng cố gắng tối ưu hóa mã được tạo và trong trường hợp của bạn, chương trình tương đương với đơn giản nhất Hello World trong . Nhưng phần còn lại của câu hỏi là hợp lệ và nó có ý nghĩa để trả lời.

+0

Câu trả lời hay. Nhưng bạn làm cho tôi có thêm một câu hỏi. Hãy xem câu hỏi mới được tạo ra 4. – padawanTony

+0

@padawanTony Rất vui khi được giúp đỡ và khiến bạn có nhiều câu hỏi hơn là thực sự tốt. Nhưng xin vui lòng, hãy hỏi một câu hỏi khác. Không chỉnh sửa của bạn sau khi câu trả lời đã được đăng. –

4

Quy trình bạn mô tả ở trên là chính xác. Tuy nhiên, trong phần lớn các trường hợp, mã C được xử lý trước và lắp ráp theo một bước như sau:

clang -c hello.c 

Thực hiện xử lý trước riêng thường chỉ được thực hiện để gỡ lỗi. Chuyển đổi sang lắp ráp hầu như không bao giờ được thực hiện trừ khi bạn định thực hiện tối ưu hóa mức lắp ráp thủ công, điều này hiếm khi cần thiết.

Liên quan đến liên kết, tùy chọn -l yêu cầu trình liên kết tìm kiếm thư viện được chia sẻ có dạng "lib {name} .so". Trong ví dụ của bạn, -lm yêu cầu trình liên kết liên kết với libm.so. Theo mặc định nó sẽ tìm trong/usr/lib, tuy nhiên bạn có thể sử dụng tùy chọn -L để cung cấp cho nó một danh sách các thư mục để tìm kiếm thư viện.

Bạn sử dụng -B cờ để chuyển đổi giữa các liên kết với các thư viện tĩnh hoặc thư viện động:

clang hello.o -lm -Bstatic -lstaticlib -B dynamic -ldynamiclib 

này sẽ liên kết với libm.so, libstaticlib.a, và libdynamiclib.so

thư viện tĩnh là được liên kết trực tiếp với tệp thực thi của bạn như tệp .o. Ngược lại, các thư viện động được giữ riêng biệt với tệp thực thi của bạn và được tải lên vào lúc chạy.

+0

Câu trả lời hay. Nhưng bạn làm cho tôi có thêm một câu hỏi. Vui lòng xem câu hỏi mới được tạo ra 4. – padawanTony