Extern c что это
Перейти к содержимому

Extern c что это

  • автор:

Extern – C and C++ Extern Keyword Function Tutorial

Farhan Hasin Chowdhury

Farhan Hasin Chowdhury

Extern – C and C++ Extern Keyword Function Tutorial

The extern keyword in C and C++ extends the visibility of variables and functions across multiple source files.

In the case of functions, the extern keyword is used implicitly. But with variables, you have to use the keyword explicitly.

I believe a simple code example can explain things better in some cases than a wall of text. So I’ll quickly setup a simple C++ program for demonstration.

If you have GCC installed on your system you may follow along. Otherwise, I’ll include outputs from each code snippet with them for you to read through.

extern with Functions

In the example, I have two C++ files named main.cpp and math.cpp and a header file named math.h . Code for the math.h file is as follows:

As you can see, the header file contains the declaration for a simple function called sum that takes two integers as parameters. The code for the math.cpp file is as follows:

This file contains the definition for the previously declared sum function and it returns the sum of the given parameters as an integer.

Finally, the code for the main.cpp file is as follows:

This file includes the math.h header file containing the declaration for the sum function. Then inside the main function, the std::cout << sum(10, 8) << std::endl; statement calls the sum functions by passing 10 and 8 as the two parameters and prints out whatever the returned value is.

Now if you try to compile this program you’ll see it compiles without any problem and upon executing the resultant binary file, you’ll see following output in the console:

This works (even though the definition of the sum function is in a separate file than main.cpp ) because all the functions in C/C++ are declared as extern . This means they can be invoked from any source file in the whole program.

You can declare the function as extern int sum(int a, int b) instead but this will only cause redundancy.

extern with Variables

Although the extern keyword is applied implicitly to all the functions in a C/C++ program, the variables behave a bit differently.

Before I dive into the usage of extern with variables, I would like to clarify the difference between declaring a variable and defining it.

Declaring a variable simply declares the existence of the variable to the program. It tells the compiler that a variable of a certain type exists somewhere in the code. You declare a float variable as follows:

At this point, the variable doesn’t have any memory allocated to it. The compiler only knows that a float variable named pi exists somewhere in the code.

Defining the variable, on the other hand, means declaring the existence of the variable, as well as allocating the necessary memory for it. You define a variable as follows:

You can declare a variable as many times as you want, but you can define a variable only once. This is because you can not allocate memory to the same variable multiple times.

Now, I’ll modify the math.h header file created in the previous section to contain the declaration for the pi variable as follows:

As you can see, the variable has been declared as an extern in the header file, which means this should be accessible anywhere in the program. Next, I’ll update the main.cpp file as follows:

I’ve added a new std::cout statement to print out the value of the pi variable. If you try to compile this program at this point, the compilation process will fail.

This happens because, declaring the variable has let the compiler know that this variable exists somewhere in the program – but in reality it doesn’t. It has no memory allocation at all.

To get out of this problem, I’ll define the pi variable inside the math.cpp file as follows:

The compilation process finishes without any issues, and if I execute the resultant binary, I’ll see the following output in my console:

Since the pi variable has been declared as an extern and has been defined within the math.cpp file, the main.cpp file is able to access the value of pi without any problem at all.

You can define the variable anywhere in the program but I chose the math.cpp file for definition to prove the point that this extern variable indeed is available to all the other source files as well.

Conclusion

Even though it’s not used that often, the extern keyword in C/C++ is undoubtedly one of the most important concept to understand. I hope you’ve understood how the keyword works at a basic level from this short article.

As you continue to use the keyword in your programs, you’ll definitely come across problems and situations that are outside the scope of this article. Feel free to reach out to me in Twitter and LinkedIn if you think I can be of help. Otherwise, Stack Overflow is always there to help.

Also, if you’re native Bengali speaker, checkout freeCodeCamp’s Bengali Publication and YouTube Channel. Till the next one, stay safe and keep learning.

What is the effect of extern "C" in C++?

What exactly does putting extern «C» into C++ code do?

Ciro Santilli OurBigBook.com's user avatar

18 Answers 18

extern "C" makes a function-name in C++ have C linkage (compiler does not mangle the name) so that client C code can link to (use) your function using a C compatible header file that contains just the declaration of your function. Your function definition is contained in a binary format (that was compiled by your C++ compiler) that the client C linker will then link to using the C name.

Since C++ has overloading of function names and C does not, the C++ compiler cannot just use the function name as a unique id to link to, so it mangles the name by adding information about the arguments. A C compiler does not need to mangle the name since you can not overload function names in C. When you state that a function has extern "C" linkage in C++, the C++ compiler does not add argument/parameter type information to the name used for linkage.

Just so you know, you can specify extern "C" linkage to each individual declaration/definition explicitly or use a block to group a sequence of declarations/definitions to have a certain linkage:

If you care about the technicalities, they are listed in section 7.5 of the C++03 standard, here is a brief summary (with emphasis on extern "C" ):

What is the Extern Keyword in C?

The extern keyword in C is used to declare a variable as a global variable such that a variable declared in another scope of the same file or another file can be accessed from anywhere in the program. The variables or functions declared with the extern keyword in C will be linked externally(external linkage).

Declaration

It is important to note that the C extern keyword is used to only declare a variable and not define a variable.

The C extern keyword is used to declare global variables mostly in the header files that will be used in other files of a long program.

Declaring a variable implies that the variable is introduced to the compiler along with the data type of the variable. Defining a variable implies that the storage required for the variable is allocated by the compiler A variable definition can also be considered as a declaration, but not in all cases.

Syntax

The extern keyword is used before the data type of a variable or the return data type of a function. The syntax of using the extern keyword in C is,

Uses of C extern keyword

The best use case of the C extern keyword is to declare functions or variables as global and to use them in different scopes from where they are defined.

Examples

The following example illustrates how the extern keyword is used to declare a global variable,

In the above example, the line extern int var is used to declare the variable var . At this time, no space is allocated by the compiler for the variable, var . At the line, int var=10 , the variable is defined and declared again, but the compiler knows that the variable var has already been declared by using the extern keyword, therefore the compiler only defines the predeclared var variable with the value of 10. Now, the variable is a global variable and can be used from any part of the program.

Let us consider another example containing two files that have the definition and declaration of a variable using the extern keyword,

Let the file, variables.c has a variable called extern_var that has been defined,

To use the variable extern_var present in variables.c in main.c , we can declare the variable using the extern keyword,

The output of the above code is,

The "#include" syntax is used to link global data from one file to another.

Instead of following this syntax, we can remove the #include "variables.c" line from the code and link the files manually during compiling by using the following command,

What is C Extern Variable?

Introduction

As we have seen in the above section, the C extern variable is a global variable created with the extern keyword. The data type of the variable can't be changed after declaring the variable.

Uses of C Extern Variable

The main use cases of the extern variable are,

  • Access a variable defined in another file.
  • Create a global common variable that can be used in all files that are linked together.

Examples

The following example illustrates how the C extern keyword is used,

Let us consider a program in which there are multiple functions in different files that modify a common variable, The globals.h file is the header file and can have all the global variables,

The file named changer.c defines the extern variable and has functions to update the global variable,

The main file, main.c can also access the global variable and modify it,

Note: Only the main.c file has to be compiled to run the entire program.

The output of the above code will be,

In the above example, the variable common_variable is made declared as an extern variable and is made accessible from both files.

It is interesting to note that, the function increment() is used in the main.c file even though the function has not been declared using the C extern keyword. We will look at the reason for this in the function section of the article.

What is C Extern Function?

Introduction

A C extern function is a global function that can be declared in a file and called from another file.

Syntax

The syntax to declare a C extern function is,

We have seen in the last section about the function, void increment()<> which was called from another file even though the function has not been declared using the extern keyword, this is because, functions are always handled as global by the compiler, so there is no need to use the C extern keyword before a function. The compiler will intercept the increment() function as,

Uses of C Extern Function

The C extern function has the following use cases,

  • The function can be called from any linked part of the program.
  • Creating libraries in C which has many functions that can be used by importing the necessary libraries.

Examples

The following example contains a file with C extern function definition and the function is called in another file.

The file, functions.c has the function declaration,

The main.c file from which the function is called is,

We will get the same results even if we remove the extern keyword from the functions defined in functions.c .

The output of the above program is,

Q: How to resolve the undefined reference to error in programs using an extern and what is the cause for the error?

A: The undefined reference to error occurs when the variable is declared using an extern but not defined later or the file in which the definition of the variable is present is not included in the program.

To resolve the above error, check the #include "file_name" section of the code, and if the code is linked manually check if you have included the extern keyword for variables.

Урок №49. Глобальные переменные

Мы уже знаем, что переменные, объявленные внутри блока, называются локальными. Они имеют локальную область видимости (используются только внутри блока, в котором объявлены) и автоматическую продолжительность жизни (создаются в точке определения и уничтожаются в конце блока).

Глобальными называются переменные, которые объявлены вне блока. Они имеют статическую продолжительность жизни, т.е. создаются при запуске программы и уничтожаются при её завершении. Глобальные переменные имеют глобальную область видимости (или «файловую область видимости»), т.е. их можно использовать в любом месте файла, после их объявления.

Определение глобальных переменных

Обычно глобальные переменные объявляют в верхней части кода, ниже директив #include, но выше любого другого кода. Например:

Подобно тому, как переменные во внутреннем блоке скрывают переменные с теми же именами во внешнем блоке, локальные переменные скрывают глобальные переменные с одинаковыми именами внутри блока, в котором они определены. Однако с помощью оператора разрешения области видимости ( :: ), компилятору можно сообщить, какую версию переменной вы хотите использовать: глобальную или локальную. Например:

Результат выполнения программы:

Global value: 3
Local value: 9

Использовать одинаковые имена для локальных и глобальных переменных — это прямой путь к проблемам и ошибкам, поэтому подобное делать не рекомендуется. Многие разработчики добавляют к глобальным переменным префикс g_ («g» от англ. «global»). Таким образом, можно убить сразу двух зайцев: определить глобальные переменные и избежать конфликтов имен с локальными переменными.

Ключевые слова static и extern

В дополнение к области видимости и продолжительности жизни, переменные имеют еще одно свойство — связь. Связь переменной определяет, относятся ли несколько упоминаний одного идентификатора к одной и той же переменной или нет.

Переменная без связей — это переменная с локальной областью видимости, которая относится только к блоку, в котором она определена. Это обычные локальные переменные. Две переменные с одинаковыми именами, но определенные в разных функциях, не имеют никакой связи — каждая из них считается независимой единицей.

Переменная, имеющая внутренние связи, называется внутренней переменной (или «статической переменной»). Она может использоваться в любом месте файла, в котором определена, но не относится к чему-либо вне этого файла.

Переменная, имеющая внешние связи, называется внешней переменной. Она может использоваться как в файле, в котором определена, так и в других файлах.

Если вы хотите сделать глобальную переменную внутренней (которую можно использовать только внутри одного файла) — используйте ключевое слово static:

What is the Extern Keyword in C?

The extern keyword in C is used to declare a variable as a global variable such that a variable declared in another scope of the same file or another file can be accessed from anywhere in the program. The variables or functions declared with the extern keyword in C will be linked externally(external linkage).

Declaration

It is important to note that the C extern keyword is used to only declare a variable and not define a variable.

The C extern keyword is used to declare global variables mostly in the header files that will be used in other files of a long program.

Declaring a variable implies that the variable is introduced to the compiler along with the data type of the variable. Defining a variable implies that the storage required for the variable is allocated by the compiler A variable definition can also be considered as a declaration, but not in all cases.

Syntax

The extern keyword is used before the data type of a variable or the return data type of a function. The syntax of using the extern keyword in C is,

Uses of C extern keyword

The best use case of the C extern keyword is to declare functions or variables as global and to use them in different scopes from where they are defined.

Examples

The following example illustrates how the extern keyword is used to declare a global variable,

In the above example, the line extern int var is used to declare the variable var . At this time, no space is allocated by the compiler for the variable, var . At the line, int var=10 , the variable is defined and declared again, but the compiler knows that the variable var has already been declared by using the extern keyword, therefore the compiler only defines the predeclared var variable with the value of 10. Now, the variable is a global variable and can be used from any part of the program.

Let us consider another example containing two files that have the definition and declaration of a variable using the extern keyword,

Let the file, variables.c has a variable called extern_var that has been defined,

To use the variable extern_var present in variables.c in main.c , we can declare the variable using the extern keyword,

The output of the above code is,

The "#include" syntax is used to link global data from one file to another.

Instead of following this syntax, we can remove the #include "variables.c" line from the code and link the files manually during compiling by using the following command,

What is C Extern Variable?

Introduction

As we have seen in the above section, the C extern variable is a global variable created with the extern keyword. The data type of the variable can't be changed after declaring the variable.

Uses of C Extern Variable

The main use cases of the extern variable are,

  • Access a variable defined in another file.
  • Create a global common variable that can be used in all files that are linked together.

Examples

The following example illustrates how the C extern keyword is used,

Let us consider a program in which there are multiple functions in different files that modify a common variable, The globals.h file is the header file and can have all the global variables,

The file named changer.c defines the extern variable and has functions to update the global variable,

The main file, main.c can also access the global variable and modify it,

Note: Only the main.c file has to be compiled to run the entire program.

The output of the above code will be,

In the above example, the variable common_variable is made declared as an extern variable and is made accessible from both files.

It is interesting to note that, the function increment() is used in the main.c file even though the function has not been declared using the C extern keyword. We will look at the reason for this in the function section of the article.

What is C Extern Function?

Introduction

A C extern function is a global function that can be declared in a file and called from another file.

Syntax

The syntax to declare a C extern function is,

We have seen in the last section about the function, void increment()<> which was called from another file even though the function has not been declared using the extern keyword, this is because, functions are always handled as global by the compiler, so there is no need to use the C extern keyword before a function. The compiler will intercept the increment() function as,

Uses of C Extern Function

The C extern function has the following use cases,

  • The function can be called from any linked part of the program.
  • Creating libraries in C which has many functions that can be used by importing the necessary libraries.

Examples

The following example contains a file with C extern function definition and the function is called in another file.

The file, functions.c has the function declaration,

The main.c file from which the function is called is,

We will get the same results even if we remove the extern keyword from the functions defined in functions.c .

The output of the above program is,

Q: How to resolve the undefined reference to error in programs using an extern and what is the cause for the error?

A: The undefined reference to error occurs when the variable is declared using an extern but not defined later or the file in which the definition of the variable is present is not included in the program.

To resolve the above error, check the #include "file_name" section of the code, and if the code is linked manually check if you have included the extern keyword for variables.

What is the effect of extern "C" in C++?

What exactly does putting extern «C» into C++ code do?

Ciro Santilli OurBigBook.com's user avatar

18 Answers 18

extern "C" makes a function-name in C++ have C linkage (compiler does not mangle the name) so that client C code can link to (use) your function using a C compatible header file that contains just the declaration of your function. Your function definition is contained in a binary format (that was compiled by your C++ compiler) that the client C linker will then link to using the C name.

Since C++ has overloading of function names and C does not, the C++ compiler cannot just use the function name as a unique id to link to, so it mangles the name by adding information about the arguments. A C compiler does not need to mangle the name since you can not overload function names in C. When you state that a function has extern "C" linkage in C++, the C++ compiler does not add argument/parameter type information to the name used for linkage.

Just so you know, you can specify extern "C" linkage to each individual declaration/definition explicitly or use a block to group a sequence of declarations/definitions to have a certain linkage:

If you care about the technicalities, they are listed in section 7.5 of the C++03 standard, here is a brief summary (with emphasis on extern "C" ):

Extern – C and C++ Extern Keyword Function Tutorial

Farhan Hasin Chowdhury

Farhan Hasin Chowdhury

Extern – C and C++ Extern Keyword Function Tutorial

The extern keyword in C and C++ extends the visibility of variables and functions across multiple source files.

In the case of functions, the extern keyword is used implicitly. But with variables, you have to use the keyword explicitly.

I believe a simple code example can explain things better in some cases than a wall of text. So I’ll quickly setup a simple C++ program for demonstration.

If you have GCC installed on your system you may follow along. Otherwise, I’ll include outputs from each code snippet with them for you to read through.

extern with Functions

In the example, I have two C++ files named main.cpp and math.cpp and a header file named math.h . Code for the math.h file is as follows:

As you can see, the header file contains the declaration for a simple function called sum that takes two integers as parameters. The code for the math.cpp file is as follows:

This file contains the definition for the previously declared sum function and it returns the sum of the given parameters as an integer.

Finally, the code for the main.cpp file is as follows:

This file includes the math.h header file containing the declaration for the sum function. Then inside the main function, the std::cout << sum(10, 8) << std::endl; statement calls the sum functions by passing 10 and 8 as the two parameters and prints out whatever the returned value is.

Now if you try to compile this program you’ll see it compiles without any problem and upon executing the resultant binary file, you’ll see following output in the console:

This works (even though the definition of the sum function is in a separate file than main.cpp ) because all the functions in C/C++ are declared as extern . This means they can be invoked from any source file in the whole program.

You can declare the function as extern int sum(int a, int b) instead but this will only cause redundancy.

extern with Variables

Although the extern keyword is applied implicitly to all the functions in a C/C++ program, the variables behave a bit differently.

Before I dive into the usage of extern with variables, I would like to clarify the difference between declaring a variable and defining it.

Declaring a variable simply declares the existence of the variable to the program. It tells the compiler that a variable of a certain type exists somewhere in the code. You declare a float variable as follows:

At this point, the variable doesn’t have any memory allocated to it. The compiler only knows that a float variable named pi exists somewhere in the code.

Defining the variable, on the other hand, means declaring the existence of the variable, as well as allocating the necessary memory for it. You define a variable as follows:

You can declare a variable as many times as you want, but you can define a variable only once. This is because you can not allocate memory to the same variable multiple times.

Now, I’ll modify the math.h header file created in the previous section to contain the declaration for the pi variable as follows:

As you can see, the variable has been declared as an extern in the header file, which means this should be accessible anywhere in the program. Next, I’ll update the main.cpp file as follows:

I’ve added a new std::cout statement to print out the value of the pi variable. If you try to compile this program at this point, the compilation process will fail.

This happens because, declaring the variable has let the compiler know that this variable exists somewhere in the program – but in reality it doesn’t. It has no memory allocation at all.

To get out of this problem, I’ll define the pi variable inside the math.cpp file as follows:

The compilation process finishes without any issues, and if I execute the resultant binary, I’ll see the following output in my console:

Since the pi variable has been declared as an extern and has been defined within the math.cpp file, the main.cpp file is able to access the value of pi without any problem at all.

You can define the variable anywhere in the program but I chose the math.cpp file for definition to prove the point that this extern variable indeed is available to all the other source files as well.

Conclusion

Even though it’s not used that often, the extern keyword in C/C++ is undoubtedly one of the most important concept to understand. I hope you’ve understood how the keyword works at a basic level from this short article.

As you continue to use the keyword in your programs, you’ll definitely come across problems and situations that are outside the scope of this article. Feel free to reach out to me in Twitter and LinkedIn if you think I can be of help. Otherwise, Stack Overflow is always there to help.

Also, if you’re native Bengali speaker, checkout freeCodeCamp’s Bengali Publication and YouTube Channel. Till the next one, stay safe and keep learning.

Внутренняя и внешняя линковка в C++

Представляем вам перевод интересной статьи, который подготовили для вас рамках курса «Разработчик C++». Надеемся, что она будет полезна и интересна для вас, как и нашим слушателям.

Сталкивались ли вы когда-нибудь с терминами внутренняя и внешняя связь? Хотите узнать, для чего используется ключевое слово extern, или как объявление чего-то static влияет на глобальную область? Тогда эта статья для вас.

В двух словах

В единицу трансляции включены файл реализации (.c/.cpp) и все его заголовочные файлы (.h/.hpp). Если внутри единицы трансляции у объекта или функции есть внутреннее связывание, то этот символ виден компоновщику только внутри этой единицы трансляции. Если же у объекта или функции есть внешнее связывание, то компоновщик сможет видеть его при обработке других единиц трансляции. Использование ключевого слова static в глобальном пространстве имен дает символу внутреннее связывание. Ключевое слово extern дает внешнее связывание.
Компилятор по умолчанию дает символам следующие связывания:

  • Non-const глобальные переменные — внешнее связывание;
  • Const глобальные переменные — внутреннее связывание;
  • Функции — внешнее связывание.

Поговорим сначала о двух простых концепциях, необходимых для обсуждения связывания.

  • Разница между объявлением и определением;
  • Единицы трансляции.

Объявление VS. Определение

Кратко обсудим разницу между объявлением и определением символа: объявление (или декларация) говорит компилятору о существовании конкретного символа, и позволяет обращение к этому символу в случаях не требующих точного адреса памяти или хранилища символа. Определение говорит компилятору, что содержится в теле функции или сколько памяти нужно выделить переменной.

В некоторых ситуациях компилятору недостаточно объявления, например, когда элемент данных класса имеет тип ссылки или значения (то есть не ссылка, и не указатель). В то же время, разрешен указатель на объявленный (но неопределенный) тип, так как ему нужен фиксированный объем памяти (например, 8 байт в 64-битных системах), не зависящий от типа, на который указывает. Чтобы получить значение по этому указателю, потребуется определение. Также для объявления функции нужно объявить (но не определить) все параметры (не важно взятые ли по значению, ссылке или указателю) и возвращаемый тип. Определение типа возвращаемого значения и параметров необходимо только для определения функции.

Разница между определением и объявлением функции весьма очевидна.

С переменными все немного иначе. Объявление и определение обычно не разделяются. Главное, что это:

Не только объявляет x , но и определяет его. Происходит это благодаря вызову дефолтного конструктора int. (В C++ в отличие от Java, конструктор простых типов (таких как int) по умолчанию не инициализирует значение в 0. В примере выше х будет иметь равен любому мусору, лежащему в адресе памяти, выделенном компилятором).

Но вы можете явно разделить объявление переменной и ее определение при помощи ключевого слова extern .

Однако, при инициализации и добавлении extern к объявлению, выражение превращается в определение и ключевое слово extern становится бесполезным.

Предварительное Объявление

В C++ существует концепция предварительного объявления символа. Это значит, что мы объявляем тип и имя символа для использования в ситуациях, не требующих его определения. Так нам не понадобится включать полное определение символа (обычно — заголовочный файл) без явной необходимости. Тем самым, мы снижаем зависимость от файла, содержащего определение. Главное преимущество — при изменении файла с определением, файл, где мы предварительно объявляем этот символ, не потребует повторной компиляции (а значит, и все прочие файлы его включающие).

Предположим, у нас есть объявление функции (называемое прототипом) для f, принимающее объект типа Class по значению:

Сразу включить определение Class — наивно. Но так как мы пока только объявили f , достаточно предоставить компилятору объявление Class . Таким образом, компилятор сможет узнать функцию по ее прототипу, а мы сможем избавиться от зависимости file.hpp от файла, содержащего определение Class , скажем class.hpp:

Допустим, file.hpp содержится в 100 других файлах. И, допустим, мы меняем определение Class в class.hpp. Если вы добавим class.hpp в file.hpp, file.hpp и все 100 содержащих его файла будут должны перекомпилироваться. Благодаря предварительному объявления Class единственными файлами, требующими повторной компиляции, будут class.hpp и file.hpp (если считать, что f определен там).

Частота использования

Важное отличие объявления от определения состоит в том, что символ может быть объявлен много раз, но определен только однажды. Так вы можете предварительно объявить функцию или класс сколько угодно раз, но определение может быть только одно. Это называется Правилом Одного Определения. В C++ работает следующее:

А это не работает:

Единицы трансляции

Программисты обычно работают с заголовочными файлами и файлами реализации. Но не компиляторы — они работают с единицами трансляции (translation units, кратко — TU), которые иногда называют единицами компиляции. Определение такой единицы довольно простое — любой файл, переданный компилятору, после его предварительной обработки. Если быть точным, это файл, получаемый в результате работы препроцессора расширяющего макрос, включающего исходный код, который зависит от #ifdef и #ifndef выражений, и копипасты всех файлов #include .

Есть следующие файлы:

Препроцессор выдаст следующую единицу трансляции, которая затем передается компилятору:

Обсудив основы, можно приступить к связям. В целом, связь — это видимость символов для компоновщика при обработке файлов. Связь может быть либо внешней, либо внутренней.

Внешняя связь

Когда символ (переменная или функция) обладает внешней связью, он становится видимым компоновщикам из других файлов, то есть “глобально” видимым, доступным всем единицами трансляции. Это значит, что вы должны определить такой символ в конкретном месте одной единицы трансляции, обычно в файле реализации (.c/.cpp), так чтобы у него было только одно видимое определение. Если вы попытаетесь одновременно с объявлением символа выполнить его определение, или поместить определение в файл к объявлению, то рискуете разозлить компоновщик. Попытка добавить файл больше чем в один файл реализации, ведет к добавлению определения больше чем в одну единицу трансляции — ваш компоновщик будет плакать.

Ключевое слово extern в C и C++ (явно) объявляет, что у символа есть внешняя связь.

Оба символа имеют внешнюю связь. Выше отмечалось, что const глобальные переменные по умолчанию имеют внутреннее связывание, non-const глобальные переменные — внешнее. Это значит, что int x; — то же самое, что и extern int x;, верно? Не совсем. int x; на самом деле аналогичен extern int x<>; (используя синтаксис универсальной/скобочной инициализации, для избежания самого неприятного синтаксического анализа (the most vexing parse)), так как int x; не только объявляет, но и определяет x. Следовательно, не добавить extern к int x; глобально настолько же плохо, как определить переменную при объявлении ее extern:

Плохой Пример

Давайте объявим функцию f с внешней связью в file.hpp и там же определим ее:

Обратите внимание, что добавлять здесь extern не нужно, так как все функции явно extern. Разделения объявления и определения тоже не потребуется. Поэтому давайте просто перепишем это следующим образом:

Такой код можно было бы написать до прочтения этой статьи, либо после ее чтения под воздействием алкоголя или тяжелых веществ (например, булочек с корицей).

Давайте посмотрим, почему так делать не стоит. Теперь у нас есть два файла реализации: a.cpp и b.cpp, оба включены в file.hpp:

Теперь пусть поработает компилятор и сгенерирует две единицы трансляции для двух файлов реализации выше (помните что #include буквально означает копировать/вставить):

На этом этапе вмешивается компоновщик (связывание происходит после компиляции). Компоновщик берет символ f и ищет определение. Сегодня ему повезло, он находит аж два! Одно в единице трансляции A, другое в B. Компоновщик замирает от счастья и говорит вам примерно следующее:

Компоновщик находит два определения для одного символа f . Поскольку у f есть внешнее связывание, он виден компоновщику при обработке и A, и B. Очевидно, это нарушает Правило Одного Определения и вызывает ошибку. Точнее это вызывает ошибку повторяющегося символа (duplicate symbol error), которую вы будете получать не реже, чем ошибку неопределенного символа (undefined symbol error), возникающую, когда вы объявили символ, но забыли определить.

Использование

Стандартным примером объявления переменных extern являются глобальные переменные. Предположим, вы работаете над самовыпекаемым тортом. Наверняка есть глобальные переменные, связанные с тортом, которые должны быть доступны в разных частях вашей программы. Допустим, тактовая частота съедобной схемы внутри вашего торта. Это значение естественно требуется в разных частях для синхронной работы всей шоколадной электроники. (Злой) C-способ объявления такой глобальной переменной имеет вид макроса:

Программист C++, испытывающий к макросам отвращение, лучше напишет настоящий код. Например такой:

(Современный программист C++ захочет использовать разделительные литералы: unsigned int clock_rate = 1’000’000;)

Внутренняя Связь

Если у символа есть внутренняя связь, то он будет виден только внутри текущей единицы трансляции. Не путайте видимость с правами доступа, например private. Видимость означает, что компоновщик сможет использовать этот символ только при обработке единицы трансляции, в которой был объявлен символ, а не позже (как в случае символов с внешней связью). На практике, это значит, что при объявлении символа с внутренней связью в заголовочном файле, каждая единица трансляции, включающая в себя этот файл, получит уникальную копию этого символа. Как если бы вы предопределили каждый такой символ в каждой единице трансляции. Для объектов это значит, что компилятор будет буквально выделять совершенно новую, уникальную копию для каждой единицы трансляции, что, очевидно, может привести к высоким расходам памяти.

Для объявления символа с внутренней связью, в C и C++ существует ключевое слово static. Такое использование отличается от применения static в классах и функциях (или, в целом, в любых блоках).

Каждая единица трансляции, включающая header.hpp получает уникальную копию переменной, в силу наличия у нее внутренней связи. Есть три единицы трансляции:

  1. file1.cpp
  2. file2.cpp
  3. main.cpp

Анонимные пространства имен

В С++ существует другой способ объявления одного и более символов с внутренней связью: анонимные пространства имен. Такое пространство гарантирует, что символы, объявленные внутри него, видны только в текущей единице трансляции. По сути, это просто способ объявить несколько символов static. Какое-то время от использования ключевого слова static в целях объявления символа с внутренней связью отказались в пользу анонимных пространств имен. Однако, им снова стали пользоваться в силу удобства объявления одной переменной или функции с внутренней связью. Есть еще несколько незначительных отличий, на которых я не буду останавливаться.

В любом случае, это:

Делает (почти) то же самое, что и:

Использование

Так в каких же случаях пользоваться внутренними связями? Использовать их для объектов — плохая идея. Расход памяти больших объектов может быть очень высок из-за копирования под каждую единицу трансляции. Но, в основном, это просто вызывает странное, непредсказуемое поведение. Представьте, что у вас есть синглтон (класс, в котором вы создаете экземпляр только одного инстанса) и неожиданно появляется несколько инстансов вашего “синглтона” (по одному на каждую единицу трансляции).

Однако, внутреннюю связь можно использовать для скрытия из глобальной области локальных хелпер-функций единицы трансляции. Допустим, есть хелпер-функция foo в file1.hpp, которую вы используете в file1.cpp. В то же время у вас есть функция foo в file2.hpp, используемая в file2.cpp. Первая и вторая foo отличаются друг от друга, но вы не можете придумать другие имена. Поэтому вы можете объявить их static. Если вы не будете добавлять и file1.hpp, и file2.hpp в одну и ту же единицу трансляции, то это скроет foo друг от друга. Если этого не сделать, то они будут неявно иметь внешнюю связь и определение первой foo столкнется с определением второй, вызвав ошибку компоновщика о нарушении правила одного определения.

Вы всегда можете оставить свои комментарии и\или вопросы тут или зайти к нам на день открытых дверей.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *