Liên kết bên ngoài và liên kết nội bộ là gì?


337

Tôi muốn hiểu mối liên kết bên ngoài và liên kết nội bộ và sự khác biệt của chúng.

Tôi cũng muốn biết ý nghĩa của

constcác biến nội bộ liên kết theo mặc định trừ khi có tuyên bố khác là extern.

Câu trả lời:


278

Khi bạn viết một tập tin thực thi ( .cpp, .cxx, vv) trình biên dịch của bạn tạo ra một đơn vị dịch thuật . Đây là tệp nguồn từ triển khai của bạn cộng với tất cả các tiêu đề bạn #included trong đó.

Liên kết nội bộ đề cập đến mọi thứ chỉ trong phạm vi của một đơn vị dịch thuật .

Liên kết bên ngoài đề cập đến những thứ tồn tại ngoài một đơn vị dịch thuật cụ thể. Nói cách khác, có thể truy cập thông qua toàn bộ chương trình , là sự kết hợp của tất cả các đơn vị dịch (hoặc tệp đối tượng).


112
Tôi đã nâng cấp điều này ngoại trừ một trục trặc: Đơn vị dịch thuật không phải là "bằng cách nào đó tệp đối tượng", đó là mã nguồn mà trình biên dịch tạo tệp đối tượng.
sbi

4
@FrankHB, "điều gì quan trọng hơn" mà câu trả lời bị thiếu là gì?
toán học

2
@MathIALian Xin lỗi vì đến muộn ... Tôi nghĩ vấn đề nên rõ ràng (bên cạnh tính chính xác của từ ngữ). Câu trả lời này không đầy đủ, vì câu hỏi về quy tắc của constcác biến (cũng như mục đích của nó) hoàn toàn bị bỏ qua ở đây.
FrankHB

293

Như dudewat đã nói liên kết ngoài có nghĩa là biểu tượng (hàm hoặc biến toàn cục) có thể truy cập được trong suốt chương trình của bạn và liên kết bên trong có nghĩa là nó chỉ có thể truy cập được trong một đơn vị dịch thuật .

Bạn có thể kiểm soát rõ ràng mối liên kết của một biểu tượng bằng cách sử dụng externstatictừ khóa. Nếu liên kết không được chỉ định thì liên kết mặc định externdành cho các constbiểu tượng không và static(bên trong) cho constcác biểu tượng.

// in namespace or global scope
int i; // extern by default
const int ci; // static by default
extern const int eci; // explicitly extern
static int si; // explicitly static

// the same goes for functions (but there are no const functions)
int foo(); // extern by default
static int bar(); // explicitly static 

Lưu ý rằng thay vì sử dụng staticcho liên kết nội bộ, tốt hơn là sử dụng các không gian tên ẩn danh mà bạn cũng có thể đặt classes. Liên kết cho các không gian tên ẩn danh đã thay đổi giữa C ++ 98 và C ++ 11, nhưng điều chính là chúng không thể truy cập được từ các đơn vị dịch thuật khác.

namespace {
   int i; // external linkage but unreachable from other translation units.
   class invisible_to_others { };
}

11
Việc thực hiện từ khóa "xuất khẩu" đã làm nổi bật sự khác biệt giữa một hàm được khai báo là 'tĩnh' và một hàm được khai báo trong không gian tên không tên. Để tóm tắt tốt nhất có thể, một mẫu hàm được khai báo với từ khóa xuất trong một đơn vị dịch có thể tham chiếu đến một hàm được xác định trong một không gian tên không tên của một đơn vị dịch khác do kết quả tra cứu 2 pha. ( ddj.com/showArticle.jhtml?articleID=184401584 )
Richard Corden

Điều gì xảy ra nếu tôi làm như sau: 1.cpp <code> const int ci; </ code> 2.cpp <code> extern const int ci; </ code>
Rajendra Uppal

2
@Rajenda bạn sẽ nhận được một lỗi biểu tượng chưa được giải quyết (xin lỗi vì sự chậm trễ chín tháng khi trả lời tôi đã bỏ lỡ nhận xét này).
Motti

4
Thông tin có thể cải thiện đáng kể câu trả lời này: 1) tĩnh không bị phản đối nữa trong C ++ 11. 2) các thành viên không gian tên ẩn danh trong C ++ 11 theo mặc định có liên kết nội bộ. Xem stackoverflow.com/questions/10832940/
Mạnh

2
"Liên kết bên ngoài nhưng không thể truy cập được từ các đơn vị dịch thuật khác" nghĩa là gì? Làm thế nào nó có thể không thể truy cập nhưng vẫn bên ngoài?
szx

101
  • Một biến toàn cục có liên kết bên ngoài theo mặc định. Phạm vi của nó có thể được mở rộng cho các tệp khác ngoài việc chứa nó bằng cách khớpextern khai báo trong tệp khác.
  • Phạm vi của một biến toàn cục có thể được giới hạn trong tệp chứa khai báo của nó bằng cách thêm tiền tố vào khai báo với từ khóa static. Các biến như vậy được cho là có liên kết nội bộ .

Xem xét ví dụ sau:

1.cpp

void f(int i);
extern const int max = 10;
int n = 0;
int main()
{
    int a;
    //...
    f(a);
    //...
    f(a);
    //...
}
  1. Chữ ký của hàm fkhai báo flà một hàm có liên kết ngoài (mặc định). Định nghĩa của nó phải được cung cấp sau trong tệp này hoặc trong đơn vị dịch thuật khác (được đưa ra dưới đây).
  2. maxđược định nghĩa là hằng số nguyên. Các liên kết mặc định cho hằng là nội bộ . Liên kết của nó được thay đổi thành bên ngoài với từ khóa extern. Vì vậy, bây giờ maxcó thể được truy cập trong các tập tin khác.
  3. nđược định nghĩa là một biến số nguyên. Liên kết mặc định cho các biến được xác định bên ngoài các thân hàm là bên ngoài .

2.cpp

#include <iostream>
using namespace std;

extern const int max;
extern int n;
static float z = 0.0;

void f(int i)
{
    static int nCall = 0;
    int a;
    //...
    nCall++;
    n++;
    //...
    a = max * z;
    //...
    cout << "f() called " << nCall << " times." << endl;
}
  1. maxđược tuyên bố là có liên kết bên ngoài . Một định nghĩa phù hợp cho max(với liên kết ngoài) phải xuất hiện trong một số tệp. (Như trong 1.cpp)
  2. nđược tuyên bố là có liên kết bên ngoài .
  3. zđược định nghĩa là một biến toàn cục với liên kết nội bộ .
  4. Định nghĩa của nCallchỉ định nCalllà một biến giữ lại giá trị của nó qua các lệnh gọi đến hàm f(). Không giống như các biến cục bộ với lớp lưu trữ tự động mặc định, nCallsẽ chỉ được khởi tạo một lần khi bắt đầu chương trình và không chỉ một lần cho mỗi lần gọi f(). Trình xác định lớp lưu trữ staticảnh hưởng đến tuổi thọ của biến cục bộ chứ không phải phạm vi của nó.

NB: Từ khóa staticđóng vai trò kép. Khi được sử dụng trong các định nghĩa của các biến toàn cục, nó chỉ định liên kết nội bộ . Khi được sử dụng trong các định nghĩa của các biến cục bộ, nó xác định rằng thời gian tồn tại của biến sẽ là thời lượng của chương trình thay vì là thời lượng của hàm.

Mong rằng sẽ giúp!


2
Điều quan trọng, khi được sử dụng trong các định nghĩa của các biến cục bộ, staticcho phép khởi tạo đơn lẻ lười biếng (có thể hữu ích nếu bạn cần một đối tượng toàn cầu nhưng phải kiểm soát khi nó được xây dựng do các vấn đề với trật tự xây dựng toàn cầu và không thể tự động phân bổ nó sử dụng newtrong khi các lược đồ khởi tạo sâu hơn có thể vượt quá những gì cần thiết cho đối tượng được đề cập; theo ngụ ý, đây chủ yếu là một vấn đề trên các hệ thống nhúng sử dụng C ++).
JAB

1
Kiểm tra rất tốt, làm cho ngày của tôi.
Blood-HaZaRd

28

Về mặt 'C' (Vì từ khóa tĩnh có ý nghĩa khác nhau giữa 'C' & 'C ++')

Hãy nói về phạm vi khác nhau trong 'C'

PHẠM VI: Về cơ bản là tôi có thể nhìn thấy một cái gì đó và bao xa.

  1. Biến cục bộ: Phạm vi chỉ bên trong một hàm. Nó nằm trong khu vực STACK của RAM. Điều đó có nghĩa là mỗi khi một hàm được gọi là tất cả các biến là một phần của hàm đó, bao gồm các đối số hàm được tạo mới và bị hủy khi kiểm soát đi ra khỏi hàm. (Bởi vì ngăn xếp được tuôn ra mỗi khi hàm trả về)

  2. Biến tĩnh: Phạm vi của điều này là cho một tập tin. Nó có thể truy cập mọi nơi trong tệp
    mà nó được khai báo. Nó nằm trong phân khúc DATA của RAM. Vì điều này chỉ có thể được truy cập bên trong một tập tin và do đó liên kết NỘI BỘ. Bất kì
    tập tin khác có thể thấy biến này. Trong thực tế, từ khóa STATIC là cách duy nhất để chúng tôi có thể giới thiệu một số mức dữ liệu hoặc hàm
    ẩn trong 'C'

  3. Biến toàn cục: Phạm vi của điều này là cho toàn bộ ứng dụng. Nó là hình thức có thể truy cập mọi nơi của ứng dụng. Các biến toàn cục cũng nằm trong phân đoạn DATA Vì nó có thể được truy cập mọi nơi trong ứng dụng và do đó Liên kết NGOẠI THẤT

Theo mặc định, tất cả các chức năng là toàn cầu. Trong trường hợp, nếu bạn cần ẩn một số chức năng trong một tệp từ bên ngoài, bạn có thể đặt tiền tố từ khóa tĩnh cho hàm. :-)


12
@Libin: Đối với 1) các biến cục bộ không phải nằm trên stack - chúng thường nằm trên stack nhưng có thể ở trong các thanh ghi và trong môi trường ARM, chúng thường ở các thanh ghi hơn là trên stack (phụ thuộc vào một số yếu tố - mức gọi, số của các cuộc tranh luận chính thức ..)
Artur

4
@Libin: Như đối với 1) Nếu bạn coi 'tuôn ra' là ghi đè - điều này là sai. Con trỏ ngăn xếp chỉ được di chuyển đến nơi khác. Không có 'vars cục bộ hợp lệ trước đây' được 'xóa' / xóa, v.v. Bạn trộn phạm vi biến với thời lượng lưu trữ. Phạm vi cho biết nơi bạn có thể truy cập một var. Thời gian lưu trữ cho biết nó tồn tại bao lâu. Bạn có thể có biến cục bộ với thời gian lưu trữ tĩnh. Nó có nghĩa là nó tồn tại "mãi mãi" nhưng có thể được truy cập từ một chức năng mà nó được khai báo.
Artur

2
Downvote cho các khái niệm không chính xác và quan niệm sai lầm rõ ràng. Nói một cách chính xác, không có "biến" toàn cầu hay "biến" (như một danh từ) được định nghĩa trong C. Bạn có thể muốn tham khảo "đối tượng phạm vi tệp" thay vì "biến toàn cục", nhưng nói về "phạm vi" (trong C nó là một tài sản của một định danh ) của nó là vô nghĩa. (Cả hai thuật ngữ được định nghĩa trong C ++ theo định nghĩa với các ý nghĩa hơi khác nhau.)
FrankHB

@Artur Tôi nghĩ rằng bạn đã quên " chỉ " trong " Nó có nghĩa là nó tồn tại" mãi mãi "nhưng có thể được truy cập (chỉ) từ một chức năng mà nó được khai báo. " - Đây là một chi tiết quan trọng, do đó tôi muốn chỉ ra Điều đó rõ ràng.
RobertS hỗ trợ Monica Cellio

1
@RobertSsupportsMonicaCellio - bạn đã đúng
Artur

14

Trước khi nói về câu hỏi, tốt hơn là nên biết chính xác thuật ngữ đơn vị dịch thuật , chương trình và một số khái niệm cơ bản về C ++ (nói chung liên kết là một trong số chúng nói chung). Bạn cũng sẽ phải biết thế nào là phạm vi .

Tôi sẽ nhấn mạnh một số điểm chính, đặc biệt. những người mất tích trong câu trả lời trước.

Liên kết là một thuộc tính của một tên , được giới thiệu bởi một tuyên bố . Các tên khác nhau có thể biểu thị cùng một thực thể (thông thường, một đối tượng hoặc một chức năng). Nói về liên kết của một thực thể thường là vô nghĩa, trừ khi bạn chắc chắn rằng thực thể đó sẽ chỉ được gọi bằng tên duy nhất từ ​​một số khai báo cụ thể (mặc dù thường là một tuyên bố).

Lưu ý một đối tượng là một thực thể, nhưng một biến thì không. Trong khi nói về mối liên kết của một biến, thực sự tên của thực thể được ký hiệu (được giới thiệu bởi một tuyên bố cụ thể) có liên quan. Liên kết của tên là một trong ba liên kết: không liên kết, liên kết nội bộ hoặc liên kết bên ngoài.

Các đơn vị dịch thuật khác nhau có thể chia sẻ cùng một khai báo theo tệp tiêu đề / nguồn (vâng, đó là cách diễn đạt của tiêu chuẩn). Vì vậy, bạn có thể giới thiệu cùng tên trong các đơn vị dịch thuật khác nhau. Nếu tên được khai báo có liên kết bên ngoài, danh tính của thực thể được gọi bằng tên cũng được chia sẻ. Nếu tên được khai báo có liên kết bên trong, cùng tên trong các đơn vị dịch khác nhau biểu thị các thực thể khác nhau, nhưng bạn có thể giới thiệu thực thể trong các phạm vi khác nhau của cùng một đơn vị dịch. Nếu tên không có liên kết, bạn chỉ đơn giản là không thể tham chiếu thực thể từ các phạm vi khác.

(Rất tiếc ... Tôi thấy những gì tôi đã gõ chỉ là lặp lại từ ngữ tiêu chuẩn ...)

Ngoài ra còn có một số điểm khó hiểu khác không được quy định trong đặc tả ngôn ngữ.

  1. Tầm nhìn (của một cái tên). Nó cũng là một thuộc tính của tên khai báo, nhưng với ý nghĩa khác với liên kết .
  2. Tầm nhìn (của một tác dụng phụ) . Điều này không liên quan đến chủ đề này.
  3. Tầm nhìn (của một biểu tượng). Khái niệm này có thể được sử dụng bởi các triển khai thực tế . Trong các triển khai như vậy, một biểu tượng có khả năng hiển thị cụ thể trong mã đối tượng (nhị phân) thường là mục tiêu được ánh xạ từ định nghĩa thực thể có tên có cùng liên kết cụ thể trong mã nguồn (C ++). Tuy nhiên, nó thường không được đảm bảo một-một. Ví dụ: một biểu tượng trong hình ảnh thư viện động chỉ có thể được chỉ định được chia sẻ trong hình ảnh đó từ mã nguồn (thường liên quan đến một số tiện ích mở rộng__attribute__ hoặc__declspec) hoặc các tùy chọn trình biên dịch, và hình ảnh không phải là toàn bộ chương trình hoặc tệp đối tượng được dịch từ một đơn vị dịch, do đó không có khái niệm tiêu chuẩn nào có thể mô tả chính xác. Vì biểu tượng không phải là một thuật ngữ quy phạm trong C ++, nó chỉ là một chi tiết triển khai, mặc dù các phần mở rộng có liên quan của phương ngữ có thể đã được áp dụng rộng rãi.
  4. Khả năng tiếp cận. Trong C ++, đây thường là về thuộc tính của các thành viên lớp hoặc lớp cơ sở , một lần nữa là một khái niệm khác không liên quan đến chủ đề.
  5. Toàn cầu. Trong C ++, "toàn cầu" đề cập đến một cái gì đó của không gian tên toàn cầu hoặc phạm vi không gian tên toàn cầu. Cái sau gần tương đương với phạm vi tập tin trong ngôn ngữ C. Cả trong C và C ++, liên kết không liên quan gì đến phạm vi, mặc dù phạm vi (như liên kết) cũng liên quan chặt chẽ với một định danh (trong C) hoặc tên (trong C ++) được giới thiệu bởi một số khai báo.

Các quy tắc liên kết của phạm vi không gian tên constbiến là một cái gì đó đặc biệt (và đặc biệt khác nhau để các constđối tượng khai báo trong phạm vi tập tin bằng ngôn ngữ C mà còn có khái niệm về mối liên hệ của định danh). Do ODR được thi hành bởi C ++, điều quan trọng là không giữ nhiều hơn một định nghĩa về cùng một biến hoặc hàm xảy ra trong toàn bộ chương trình ngoại trừ các inlinehàm . Nếu không có quy tắc đặc biệt nào như vậy const, một khai báo constbiến đơn giản nhất với bộ khởi tạo (ví dụ = xxx) trong một tiêu đề hoặc tệp nguồn (thường là "tệp tiêu đề") được bao gồm bởi nhiều đơn vị dịch (hoặc được bao gồm bởi một đơn vị dịch nhiều lần, mặc dù hiếm khi) trong một chương trình sẽ vi phạm ODR, điều này sử dụngconst biến như thay thế một số macro giống như đối tượng không thể.


3
Câu trả lời này nghe có vẻ rất thành thạo và có thể rất chính xác (tôi không thể đánh giá điều đó) nhưng rất có thể nó không dễ hiểu như mong muốn của nhiều người tìm kiếm câu hỏi này ở đây thay vì trực tiếp đọc thông số ngôn ngữ. Ít nhất là cho nhu cầu của tôi, tôi sẽ gắn bó với câu trả lời được chấp nhận nhưng vẫn cảm ơn bạn vì đã đưa ra một cái nhìn sâu sắc nhỏ về thông số ngôn ngữ. 👍🏻
wedi

8

Tôi nghĩ rằng Liên kết bên trong và bên ngoài trong C ++ đưa ra một lời giải thích rõ ràng và súc tích:

Một đơn vị dịch thuật đề cập đến một tệp thực hiện (.c / .cpp) và tất cả các tệp tiêu đề (.h / .hpp) mà nó bao gồm. Nếu một đối tượng hoặc chức năng bên trong một đơn vị dịch thuật có liên kết bên trong, thì biểu tượng cụ thể đó chỉ hiển thị cho trình liên kết trong đơn vị dịch thuật đó. Nếu một đối tượng hoặc chức năng có liên kết bên ngoài, trình liên kết cũng có thể nhìn thấy nó khi xử lý các đơn vị dịch thuật khác. Từ khóa tĩnh, khi được sử dụng trong không gian tên toàn cầu, buộc một biểu tượng phải có liên kết bên trong. Từ khóa extern dẫn đến một biểu tượng có liên kết bên ngoài.

Trình biên dịch mặc định liên kết các ký hiệu sao cho:

Các biến toàn cục không const có liên kết ngoài theo mặc định Các
biến toàn cục Const có liên kết bên trong theo mặc định
Các hàm có liên kết ngoài theo mặc định


6

Liên kết xác định xem các định danh có tên giống nhau tham chiếu đến cùng một đối tượng, chức năng hoặc thực thể khác, ngay cả khi các định danh đó xuất hiện trong các đơn vị dịch thuật khác nhau. Sự liên kết của một định danh phụ thuộc vào cách nó được khai báo. Có ba loại liên kết:

  1. Liên kết nội bộ : các định danh chỉ có thể được nhìn thấy trong một đơn vị dịch thuật.
  2. Liên kết ngoài : các định danh có thể được nhìn thấy (và được tham chiếu) trong các đơn vị dịch thuật khác.
  3. Không có liên kết : các định danh chỉ có thể được nhìn thấy trong phạm vi mà chúng được xác định. Liên kết không ảnh hưởng đến phạm vi

Chỉ C ++ : Bạn cũng có thể có liên kết giữa các đoạn mã C ++ và không C ++, được gọi là liên kết ngôn ngữ .

Nguồn: Liên kết chương trình IBM


5

Về cơ bản

  • extern linkage biến có thể nhìn thấy trong tất cả các tập tin
  • internal linkage biến được hiển thị trong tập tin duy nhất.

Giải thích: các biến const liên kết nội bộ theo mặc định trừ khi được khai báo là extern

  1. theo mặc định, biến toàn cục là external linkage
  2. nhưng, constbiến toàn cầu làinternal linkage
  3. thêm, extern constbiến toàn cục làexternal linkage

Một tài liệu khá hay về liên kết trong C ++

http://www.goldsborough.me/c/c++/linker/2016/03/30/19-34-25-iternal_and_external_linkage_in_c++/


1

Trong C ++

Bất kỳ biến nào trong phạm vi tệp và không được lồng trong một lớp hoặc hàm, có thể nhìn thấy trong tất cả các đơn vị dịch trong một chương trình. Điều này được gọi là liên kết ngoài vì tại thời điểm liên kết, tên được hiển thị cho trình liên kết ở mọi nơi, bên ngoài đơn vị dịch thuật đó.

Biến toàn cầu và các hàm thông thường có liên kết bên ngoài.

Đối tượng tĩnh hoặc tên hàm ở phạm vi tệp là cục bộ cho đơn vị dịch. Điều đó được gọi là Liên kết nội bộ

Liên kết chỉ đề cập đến các yếu tố có địa chỉ tại thời gian liên kết / tải; do đó, khai báo lớp và biến cục bộ không có liên kết.


const vars toàn cầu có liên kết nội bộ.
Blood-HaZaRd
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.