đúng rồi, chọn 1 trong 2 tĩnh hoặc động thôi, mà liên kết động thì khỏi vướng víu mấy cái license LGPL phức tạp :V
khi biên dịch code thành exe có 2 tiến trình:
- biên dịch các file code ví dụ .cpp thành mã máy, ví dụ .o
- liên kết mã máy từ các file .o thành file thực thi .exe, hoặc thư viện tĩnh .a/.lib, hoặc thư viện động .dll
ở bước 1 biên dịch, ko cần biết các hàm nó hoạt động ra sao, ví dụ gọi sqrt()
thì nó ko cần biên dịch mã của sqrt()
mà nó chỉ “goto” tới phần mã máy của sqrt()
ở chỗ khác. Chỗ nào thì bước 2 liên kết sẽ quyết định
ví dụ biên dịch file main.cpp có curl:
g++ -c main.cpp -Ipath/to/curl/include
-c
là chỉ biên dịch, ko liên kết
-I<path>
là bảo g++ cho thêm <path>
vào thư mục header khi nó cần tìm ví dụ #include <curl.h>
khi này g++ sẽ biên dịch main.cpp
thành mã máy main.o
, và ví dụ trong main có gọi curl_easy_init()
thì main.o
chả có chứa mã máy gì của curl_easy_init()
init cả, chỉ có chứa 1 cái địa chỉ ảo của curl_easy_init()
và goto địa chỉ này. Sở dĩ nó biết có hàm curl_easy_init()
là vì trong curl.h
có chứa definition của hàm này, còn hàm này nó làm cái gì thì khi biên dịch nó ko biết vì trong main.cpp
ko có implementation của hàm này. Khi gọi curl_easy_init()
trong main.cpp thì nó chỉ biên dịch ra đơn giản là lưu địa chỉ hiện tại trong main, đi tới địa chỉ của curl_easy_init, và thực thi tiếp các mã lệnh trong curl_easy_init, và hy vọng khi thực hiện xong các mã lệnh của curl_easy_init này nó sẽ trở lại địa chỉ trước đó trong main :V
khi liên kết thành file .exe:
liên kết động:
g++ main.o -Lpath/to/curl/lib -lcurl
-L<path>
(chữ L hoa) tương tự như -I
, cho thêm <path>
vào các thư mục mà g++ sẽ tìm kiếm khi liên kết các mã máy lại với nhau.
-lcurl
(chữ L thường)
g++ lúc này sẽ đi tìm địa chỉ của curl_easy_init()
, nếu quên cho -lcurl
vào thì nó sẽ báo là ko thấy địa chỉ tới implementation của curl_easy_init: undefined reference to _imp__curl_easy_init
, còn nếu cho vào thì nó sẽ biết mà tìm địa chỉ này trong file libcurl.dll.a
: chữ -l
được “chuyển hóa” thành lib<tên thư viện>.dll.a
, curl là tên thư viện nên file chứa địa chỉ cần tìm sẽ là libcurl.dll.a
, và nó sẽ tìm trong lib
của g++ hoặc path/to/curl/lib
mà mình đã thêm vào trong flag -L
ở trên. Tìm ra rồi nó sẽ chuyển địa chỉ ảo trong main.o
thành địa chỉ tương đối và nhét vào file .exe. Tương đối ở đây là tương đối với địa chỉ của dll libcurl.dll. Vì là liên kết động nên mỗi lần bấm file .exe này lên Windows sẽ tự động tìm libcurl.dll
ở folder chứa file exe, nếu ko thấy thì nó sẽ tìm tiếp trong các folder trong PATH
environment, nếu tìm thấy thì Windows sẽ load file dll này lên chạy cùng với file exe, và địa chỉ thật của curl_easy_init()
sẽ được xác định là địa chỉ của libcurl.dll + địa chỉ tương đối trong file exe.
còn liên kết tĩnh:
g++ main.o libcurl.a -Lpath/to/curl/lib
y hệt như khi liên kết mấy file .o lại với nhau g++ file1.o file2.o ...
Khi này toàn bộ mã máy của curl_easy_init()
sẽ được nhét vào file .exe, và địa chỉ tuyệt đối của nó cũng được xác định nằm trong file exe, khi bật lên thì Windows nó ko cần tìm file dll nào hết vì mã của nó ở trong file exe rồi.
liên kết tĩnh thì chương trình chạy hơi hơi lẹ hơn liên kết động, nhưng 1 số license như LGPL ko cho phép liên kết tĩnh, trừ phi phải open source toàn bộ chương trình :V :V
lần sau cứ thấy lỗi undefined reference to _imp__...
là tự hiểu nó ko tìm thấy mã máy của hàm ...
, nghĩa là quên thêm -l<tên thư viện>
trong phần liên kết rồi.
để cho dễ hình dung thì cứ nghĩ các file .cpp là 1 chương của 1 cuốn sách, khi viết sách thì ta viết từng chương, chương này có thể nhắc tới (refer) các phần trong chương khác trong cùng cuốn sách (liên kết tĩnh) hoặc các phần trong các chương của cuốn sách khác (liên kết động). Khi đóng gói 1 cuốn sách thì ta nối/liên kết các chương lại với nhau, sắp xếp có thứ tự, nếu “liên kết tĩnh” thì ta có thể tính ra “địa chỉ tuyệt đối”/số trang hiện tại của các phần trong các chương khác dễ dàng. Còn “liên kết động” thì ta cũng làm tương tự như liên kết tĩnh như khi đọc sách hay chạy chương trình :V thì ta phải mở 2 cuốn sách lên mà đọc, và khi đọc phần này của cuốn khác thì phải quay đầu qua cuốn đó mà đọc nên chậm hơn đọc trong 1 cuốn sách :V Khi nối các chương lại với nhau, người nối sẽ kiểm tra các reference trong các chương, nếu tìm ko thấy reference sẽ la lên undefined reference to _imp__...
nghĩa là tôi có thể nối nhưng người đọc ko thể hiểu phần này nghĩa là gì vì đâu có reference tới trang nào đâu :V
khác biệt của liên kết động và tĩnh có nhiều, ví dụ cái lợi của liên kết động là khi mua 3 cuốn sách A,B,C cùng nhắc tới cuốn sách X thì ta chỉ cần mua 1 cuốn X là đủ, còn liên kết tĩnh thì 3 cuốn sách AX,BX,CX coi như ta phải mua sách X 3 lần vì X nó gắn kèm trong 3 cuốn :V Cái bất tiện là đọc chậm hơn vì phải ngó qua ngó lại :V cái thiệt hại là mua quyển X ko đúng edition ví dụ đòi edition 3 năm 2019 mà đi mua X edition 1 năm 2000 thì ko đúng số trang, đọc A,B,C ko được :V