Đây là một sự khác biệt khá nổi tiếng giữa các hệ thống tương tự Windows và Unix.
Không có vấn đề gì:
- Mỗi quy trình có không gian địa chỉ riêng, có nghĩa là không bao giờ có bất kỳ bộ nhớ nào được chia sẻ giữa các quy trình (trừ khi bạn sử dụng một số thư viện liên lạc hoặc tiện ích mở rộng liên tiến trình).
- Các Rule Một Definition (ODR) vẫn được áp dụng, có nghĩa là bạn chỉ có thể có một định nghĩa của biến toàn cầu có thể nhìn thấy liên kết tại thời gian (tĩnh hoặc liên kết động).
Vì vậy, vấn đề quan trọng ở đây là tầm nhìn thực sự .
Trong mọi trường hợp, static
các biến toàn cục (hoặc hàm) không bao giờ được nhìn thấy từ bên ngoài một mô-đun (dll / so hoặc thực thi). Tiêu chuẩn C ++ yêu cầu những cái này có liên kết bên trong, nghĩa là chúng không thể nhìn thấy bên ngoài đơn vị dịch (trở thành tệp đối tượng) trong đó chúng được xác định. Vì vậy, điều đó giải quyết vấn đề đó.
Nơi nó trở nên phức tạp là khi bạn có extern
các biến toàn cục. Ở đây, các hệ thống giống Windows và Unix hoàn toàn khác nhau.
Trong trường hợp Windows (.exe và.), extern
biến toàn cục không phải là một phần của các ký hiệu được xuất. Nói cách khác, các mô-đun khác nhau không nhận thức được các biến toàn cục được xác định trong các mô-đun khác. Điều này có nghĩa là bạn sẽ gặp lỗi liên kết nếu bạn thử, ví dụ, để tạo một tệp thực thi được cho là sử dụng một extern
biến được định nghĩa trong DLL, vì điều này không được phép. Bạn sẽ cần cung cấp một tệp đối tượng (hoặc thư viện tĩnh) với định nghĩa về biến ngoài đó và liên kết tĩnh với cả tệp thực thi và DLL, dẫn đến hai biến toàn cục riêng biệt (một thuộc về tệp thực thi và một thuộc về DLL ).
Để thực sự xuất một biến toàn cục trong Windows, bạn phải sử dụng một cú pháp tương tự như cú pháp xuất / nhập hàm, nghĩa là:
#ifdef COMPILING_THE_DLL
#define MY_DLL_EXPORT extern "C" __declspec(dllexport)
#else
#define MY_DLL_EXPORT extern "C" __declspec(dllimport)
#endif
MY_DLL_EXPORT int my_global;
Khi bạn làm điều đó, biến toàn cục được thêm vào danh sách các ký hiệu được xuất và có thể được liên kết giống như tất cả các hàm khác.
Trong trường hợp môi trường giống Unix (như Linux), các thư viện động, được gọi là "đối tượng chia sẻ" với phần mở rộng .so
xuất tất cả các extern
biến toàn cục (hoặc hàm). Trong trường hợp này, nếu bạn thực hiện liên kết thời gian tải từ bất kỳ đâu đến tệp đối tượng được chia sẻ, thì các biến toàn cục sẽ được chia sẻ, tức là được liên kết với nhau như một. Về cơ bản, các hệ thống giống Unix được thiết kế để làm cho nó hầu như không có sự khác biệt giữa liên kết với một thư viện tĩnh hoặc thư viện động. Một lần nữa, ODR áp dụng trên bảng: mộtextern
biến toàn cục sẽ được chia sẻ trên các mô-đun, có nghĩa là nó chỉ nên có một định nghĩa trên tất cả các mô-đun được tải.
Cuối cùng, trong cả hai trường hợp, đối với các hệ thống tương tự Windows hoặc Unix, bạn có thể thực hiện liên kết thời gian chạy của thư viện động, tức là sử dụng LoadLibrary()
/ GetProcAddress()
/ FreeLibrary()
hoặc dlopen()
/ dlsym()
/ dlclose()
. Trong trường hợp đó, bạn phải tự lấy một con trỏ tới từng ký hiệu bạn muốn sử dụng và bao gồm các biến toàn cục bạn muốn sử dụng. Đối với các biến toàn cục, bạn có thể sử dụng GetProcAddress()
hoặc dlsym()
giống như bạn làm cho các hàm, miễn là các biến toàn cục là một phần của danh sách ký hiệu được xuất (theo quy tắc của các đoạn trước).
Và tất nhiên, như một lưu ý cuối cùng cần thiết: nên tránh các biến toàn cục . Và tôi tin rằng văn bản bạn trích dẫn (về những điều "không rõ ràng") đang đề cập chính xác đến sự khác biệt về nền tảng mà tôi vừa giải thích (thư viện động không thực sự được xác định theo tiêu chuẩn C ++, đây là lãnh thổ dành riêng cho nền tảng, có nghĩa là nó là ít đáng tin cậy / di động).