C + + function declaration and definition

Source: Internet
Author: User

compiler compiles a program, it compiles only one source file and generates the corresponding intermediate file (for the VC, the. obj file), and then the connector unifies all intermediate files to form an executable file. The problem is that when the compiler compiles the A.cpp file, it discovers the definition statement and defines the variables A and B, but when compiling b.cpp, it discovers the code of A and B, such as a++, and the compiler will error. Why? If the error is not reported, because the a.cpp has already been defined, then compile the b.cpp and then compile a.cpp will be how? If the compilation order of the source files is specific, it will greatly reduce the flexibility of the compilation, so C + + also stipulates that: Compile a.cpp when all the things defined (variables, functions, etc.) in compiling b.cpp will all not count, and did not compile a.cpp the same. So what do b.cpp do with the variables defined in A.cpp? For this reason, C + + proposes the concept of declaration.
So the variable declares long A; it tells the compiler that it already has a variable named a, whose type is long, and that its corresponding address is unknown, but you can make a mark, that is, in subsequent code where all the variables are used to mark, to tell the connector when connected, First in all the intermediate files to find if there is a variable called a, its address is how many, and then modify all the marked place, the address of a corresponding to put in. This enables the file to use a variable defined in another file.
so declare long A; to tell the compiler that it already has a variable a, so do not give an error when using a in subsequent code to say that a is undefined. The same is true of functions, but one problem is that function declarations and function definitions are easy to distinguish, because the function definition is followed by a compound statement, but the variable definition and the variable declaration are exactly the same, so how will the compiler recognize the variable definition and the variable declaration? When the compiler encounters a long, the uniform considers it to be a variable definition, in order to be able to identify the variable declaration, you can use the modifier extern as proposed by C + +. The
modifier is used in a declaration or definition statement to decorate this declaration or definition to provide certain information to the compiler, which is always preceded or followed by a declaration or definition statement, such as:

extern long A, *PA, &ra;

Three variables A, PA, and RA are declared (not defined) above. Because extern represents an external meaning, it is thought to tell the compiler that there are three external variables, a, PA, and RA, which are considered declaration statements, so no memory will be allocated above. Similarly, for a function, it is the same:

extern void ABC (long); or extern long AB (short b);

The above extern is equivalent to not writing because the compiler is based on the last ";" It can be judged that the above is a function declaration, and the "external" information provided is meaningless to the function, and the compiler will ignore it. extern actually also specifies the decoration of the identifier that is subsequently decorated, which should actually be extern "C" or extern "C + +", respectively, representing the identifier of the Declaration in accordance with the C language style and the C + + language style.
C + + is a strongly typed language, that is, its strict type matching principle, in order to realize the function overload function mentioned earlier. That is, several functions with the same name can be overloaded because they do not actually have the same name and are modified by their respective parameter types and number. such as void ABC (), *abc (Long), ABC (long, short), in VC, their respective names will be changed to "[email protected] @YAXXZ", "[Email protected]@[email Protec Ted] "," [email protected]@[email protected] ". While extern long A, *pa, the names of the three variables declared by the &ra; also change accordingly, respectively "[Email Protected]@3ja", "[Email Protected]@3paja", "[email Protected]@3aaja ". Above is called the C + + language style of the identifier decoration (different compiler decorated format may be different), and C-style identifier decoration is simply to precede the identifier with "_" (different compiler C-style decoration must be the same). such as: extern "C" long A, *PA, &ra; will become _a, _PA, _ra. And the above extern "C" void ABC (), *abc (Long), ABC (long, short); error, because using C-style, all just underlined in front of the function name, will produce 3 identical symbols (symbol), errors.
Why can't I have the same symbol? Why change identifiers? Not only because of the previous function overloads. Unlike symbols and identifiers, symbols can consist of any character, which is a means of communication between the compiler and the connector, and identifiers are simply a means of identification provided at the C + + language level. The reason for changing identifiers instead of directly using identifiers as symbols is that there is some information that needs to be passed between the compiler itself and the connector, which requires symbols to be identified, because it is possible for a user to write identifiers that conflict with the same symbols that are used internally by the compiler. So you have to modify the programmer-defined identifiers and then use them as symbols. Since the symbol is any character, why does the compiler not let its internal symbols use the identifier can not use the characters, such as the previous VC used "?", then not OK? Some c/C + + compilers and connectors communicate with symbols that do not have any character, and must be an identifier, so the preceding language style is unified with the prefix "_" to differentiate between the programmer-defined symbols and the inside of the compiler. Which means "?" can be used above. As the symbol is VC only so, perhaps other compilers do not support, but other compilers must support the "_" prefix identifier. This can be used in conjunction with multi-party code to implement code reuse on a larger scale, which is explained in detail in C + + zero-based (18).
When writing extern void ABC (long), is the extern "C" or extern "C + +"? In VC, if the previous code is in the source file with the extension. cpp to indicate that it is a C + + source code, it will be interpreted as the latter. If it is. C, it will be interpreted as the former. However, you can change the default settings above by modifying the project options in the VC. and extern long A; it is the same as above.
So the following:

extern "C + +" void abc (), *abc (Long), ABC (long, short);
int main () {ABC ();}

The first sentence above tells the compiler that the subsequent code may need to use this three function, called the compiler do not error. Assuming that the above program is placed in a a.cpp under a VC project, there will be no error in compiling a.cpp. But when the connection, the compiler will say the symbol "[email protected] @YAXXZ" did not find, because the project contains only one file, the connection is only connected to the corresponding a.obj and other necessary library files (subsequent articles will be explained). The connector finds the symbol "[email protected] @YAXXZ" in all object files (a.obj) and library files it can connect to, but none of them have been found. In other words, the main function uses the function void ABC () defined outside the a.cpp, but does not find the definition of the function. It should be noted that if you write int main () {void (*PA) = ABC;} will still be an error, because ABC is equivalent to an address, and here it is required to calculate the value of this address (even if the PA is not used), so the same error.
In order to eliminate the above error, the function void ABC () should be defined, either in A.cpp, after the main function, or a. cpp file can be regenerated, added to the project, and the function ABC is defined in that. cpp file. So here's what you can do:

extern "C + +" void abc (), *abc (Long), ABC (long, short);
int main () {ABC ();} void abc () {}

If you think you already know the difference between a statement and a definition and are clear about the meaning of the statement, then I bet there is a 50% possibility that you do not really understand the meaning of the statement, and here, for space limitations, will explain the true meaning of the statement in C + + from scratch (10), if you are a person with some C/ It would be a surprise to have a 50% chance of the sample given.

Meaning of the Declaration

The meaning of a declaration must be reconsidered in the light of the new definition syntax of the definition rule for member functions. Note that before the definition of a function is placed in front of the main function definition, it is no longer necessary to declare that function, and if you define a variable, you do not have to declare that variable. This means that the definition statement has the function of declaring it, but the definition statement of the member function above does not have the function of declaring, the following is to understand the true meaning of the declaration.
A declaration is a statement that requires the compiler to produce a mapped element. The so-called mapping element, which is the variable and function described earlier, has only 3 columns (or 3 fields): The type bar, the name bar, and the Address bar (the column of the member variable type puts the offset value). That is, whenever the compiler sees the declaration statement, it generates a mapping element and leaves the corresponding address bar empty, leaving some information to tell the connector this. obj file (the file that is generated after the compiler compiles the source file, for which the VC is an. obj file) needs some symbols, finds the symbols and then modifies and perfects the. obj file, and finally connects.
Recall the meaning of the previously mentioned symbol, which is a string that is used for communication between the compiler and the connector. Note that the symbol does not have a type, because the connector is only responsible for locating the symbol and perfecting it (because some of the mapping elements have the address bar or empty) intermediate files (for the VC is the. obj file), without parsing, there is no type.
The definition is to require the compiler to populate the address bar where the preceding declaration is not written. In other words, the corresponding address of a variable is only known when it is defined. So the actual allocation of memory on the stack is done by the definition of the variable, so the declared variable does not allocate memory. However, it is important to note that the definition is the address required to generate the mapping element, so the definition shows which mapping element it generates, and if there is no mapping element in the compiler's mapping table (that is, the variable table, function table, etc.) that was used internally by the compiler to record the mapping element. The compiler will make an error if the declaration of the corresponding element has not been present.
But the previous only write a variable or function definition statement, it still normal and no error AH? Actually, it's very simple to just look at declarations and definitions as a statement, but it's different from the information provided to the compiler. such as: void ABC (float), and void ABC (float) {}, the compiler treats them the same way. The former gives the type and type name of the function, so the compiler simply fills in the first name and type of the mapping element in two columns. The compiler was unable to fill in the address bar because it was only followed by a ";" and the code for this function mapping was not given. The latter, given the function name, the type of the map and the code (empty compound statement), so the compiler got all the information to fill in and then the three columns of information are filled in, the result shows that the definition statement completed the function of the Declaration.
For variables, such as long A;. As above, the type and name are given here, so the compiler fills in two columns of type and name. But the variable corresponds to the first address of a block of memory on the stack, the first address can not be displayed from the code (the preceding function by writing a compound statement after the function declaration to show the corresponding function corresponding to the address of the code), and must be obtained by the compiler internal calculation, Therefore, the above-mentioned writing is defined as the variable, and the declaration of the variable needs to be preceded by extern. That is, the above will cause the compiler to perform internal computations and then draw the corresponding address and fill in all the information of the mapped element.
It seems to be a trick, because of the appearance of custom types. Consider the definition of member variables, such as:

struct ABC {long A, b; double C;};

The above gives the type--long ABC::, Long ABC:: and double abc::; gives names--abc::a, Abc::b, and ABC::C; addresses (that is, offsets)--0, 4, and 8 are given, because they are structured custom types, Therefore, it is possible to derive the offset of each member variable from this statement. The above three information, that is, you can fill in all the information of the mapping element, so the above can be counted as definition statements. For member functions, as follows:

struct ABC {void AB (float);};

The above gives the type--void (ABC::) (float); gives the name--abc::ab. However, since the address is not given, it is not possible to fill in all the information of the mapping element, so the above is the declaration of the member function Abc::ab. According to the previous statement, you can just give the address, without having to define or declare it, so you can:

struct ABC {void AB (float) {}};

Given the type and name above, the address is given, so all the information about the mapped element will be fully populated and is defined. The above usage has its particularity, which is explained later. Note that if you then write the ABC::AB definition statement at this point, the following will be the error:

struct ABC {void AB (float) {}};
void Abc::ab (float) {}

The reason for this error is very simple, because the latter is just a definition, it only provides a message for the address of the ABC::AB, but the address bar in the mapping element is already filled in, so the compiler will say duplicate definitions. Looking at the definition of the member function separately, it gives the type void (ABC::) (float), gives the name Abc::ab, and gives the address, but why does it only give the address this information? First, the name Abc::ab is not in accordance with the rules of the identifier, and the type modifier ABC:: Must pass the type definition "{}" to be able to add, which has been explained many times before. So the information given above is: An address is given, which is the address of a MAP element of type void (ABC::) (float) with the name Abc::ab. As a result, the compiler looks for such mapping elements, if any, then fill in the corresponding address bar, otherwise the error, that is, only write a void Abc::ab (float) {} is wrong, before it must first declare the corresponding mapping element through the type definition "{}". That's what we said earlier. The definition only fills the address bar and does not generate a mapping element.


Role of the Declaration

The role of definition is obvious, meaningful mapping (name-to-address) is what it does, but what is the use of a declaration? It just generates the type to the name, why does it have to be typed to the name? It just tells the compiler not to emit an error saying that a variable or function is undefined? Anything has its meaning, let's look at the code below.

extern "C" long ABC (long A, long B);
void Main () {Long c = ABC (10, 20);}

Assuming the above code is written in A.cpp, compile the generated file a.obj, no problem. However, following the previous instructions, the connection will be incorrect because the symbol _ABC cannot be found. Because the name _abc the corresponding address bar is still empty. Then in the VC for A.cpp in the project to add a new source file B.cpp, the following writing code.

extern "C" float ABC (float a) {return A;}

Compile and connect, now there is no problem, but I believe you have seen the problem-the declaration of function ABC and the definition of the type mismatch, but the connection is successful?
Note the above about the connection, no type, just sign. The above with the extern "C" so that the a.obj requirements _abc symbols, and b.cpp provide _abc symbols, the remaining is just the b.obj of the connector to _abc the corresponding address to improve a.obj, the last connection a.obj and a.obj.
So what happens above, because of the need to consider the implementation details of the function, which is explained in "C + + from scratch (15)", and here is just one thing to note: The compiler can still generate code to implement function-function calls, even without an address. This is because the declaration must also give the type and name, because the type tells the compiler, when an operator involves a mapping element, how to generate code to implement the function of this operator. That is, the number multiplication of two char types and the two long numeric multiplication compile the generated code, which differs from the long ABC (long), the function call code and the Void ABC (float). That is, differences in the number types that the operators act on will cause the compiler to generate different code.
So why would you put ABC's definition in B.cpp? Because the compilation between the source files is independent, if placed in the A.cpp, the compiler will find that there is already such a mapping element, but the type does not match, will be an error. And put in the b.cpp, so that by the connector to improve the a.obj, to the time there will be no type of existence, just sign. Continue below.

struct ABC {long A, b; void AB (Long tem1, long tem2); void ABCD ();};
void Main () {ABC A; A.ab (10, 20);}

By the above, although the definition of abc::ab is not given here, it can still be compiled successfully without any problems. Still assume that the above code is in A.cpp, and then add B.cpp, in which the following code is written.

struct ABC {float B, A; void AB (Long tem1, long tem2); Long ABCD (float);};
void Abc::ab (Long tem1, long tem2) {a = Tem1; b = tem2;}

The function abc::ab is defined here, note that, as previously stated, because the function definition here is just a definition, you must write the type definition "{}" in front of it to have the compiler generate the mapping element. However, it is important to note that the position of the member variable is changed, so that B is mapped 0 and a is 4, and the type A, B is replaced by float, and the definition in A.cpp is quite different. But without any problems, the compilation connection was successful, A.ab (10,20), a.a 0x41a00000,a.b as 0x41200000 after execution, and * (float*) &a.a to 20,* (flaot*) &A.B was 10.
Why? Because the compiler follows type matching only in the source file that is currently being compiled, and when another source file is compiled, the mapping elements generated by compiling other source files are all invalid. The Declaration therefore binds the type and the name, and the name represents the number of the address type of the type to which it is associated, and subsequent compilation of all operators of that number in the code will be affected by the type of the number. That is, the declaration tells the compiler how to generate code, which is not just a syntax to describe a variable or function statement, it is indispensable.
It should also be noted that the declarations of the ABC::ABCD member functions in the above two files are different, and the entire project (that is, a.cpp and b.cpp) does not have a ABC::ABCD definition, but still compiles the connection successfully, because the declaration does not tell the compiler what is already in it, but how to generate the code.


Header file

As explained above, if you have a custom type ABC that you want to use in A.cpp, B.cpp, and c.cpp, you must redefine the custom type with the type definition "{}" before using ABC in A.cpp, B.cpp, and C.cpp, respectively. If you accidentally write a different definition in a.cpp and b.cpp as above, you will have an error that is difficult to find. To do this, C + + provides a precompiled directive to help.
A precompiled instruction is an instruction that executes before compiling, which is interpreted by the precompiled compiler. The precompiled compiler is another program, and in general, the compiler vendor merges it into the C + + compiler and provides only one program. This describes the include directive in the precompiled Directive--#include, in the form of # include < file name >. It should be noted that precompiled directives must have a single row, while the < file name > is a file name enclosed in double or angle brackets, such as: #include "abc.c", #include "C:\abc.dsw", or # include. It is very simple, that is, the file name written in quotation marks or angle brackets in ANSI format or MBCS format (about the two formats can refer to "C + + from scratch (v)") interpretation, and the content is unchanged to the location of # include, such as the following is the contents of the file ABC.

struct ABC {long A, b; void AB (Long tem1, long tem2);};

Then the previous a.cpp can be changed to:

#include "abc"
void Main () {ABC A; A.ab (10, 20);}

Instead, B.cpp can read:

#include "abc"
void Abc::ab (Long tem1, long tem2) {a = Tem1; b = tem2;}

At this point, it is not possible to write the definition of the custom type ABC in B.cpp incorrectly and result in the error (A.A is 0x41a00000,a.b to 0x41200000), then A.ab (10, 20), and A.A to 10,a.b 20 after execution.
Note that this uses double quotation marks to enclose the file name, which means that when enclosing only a file name or relative path without giving the full path, such as ABC above, first search the directory where the source file is compiled at this time, and then search for compiler-customized include directories (such as: C:\Program files\ Microsoft Visual Studio. NET 2003\vc7\include, etc.), usually contains the compiler's own SDK header file (about the SDK, will be in "C + + zero-start (18)"), if not found, the error (Note that The general compiler provides some options so that in addition to the above directories, you can also search for the specified directory, different compiler settings in different ways, not in this table.
If you enclose it in angle brackets, it means searching the directory where the compiler customized the containing directory, and then the source file. Why should it be different? It is only to prevent the file name from starting to conflict with the names of the files in the compiler's containing directory, because the successor directory will no longer be searched once the file is found.
Therefore, in general C + + code, if you want to use a custom type, the definition of that custom type is installed in two files, for the above structure ABC, you should generate two files, ABC.h and ABC.cpp, respectively, where ABC.h is referred to as the header file, and ABC.cpp is called the source file. In the header file is a declaration, and the source file is defined, then the content of ABC.h is the same as the previous ABC, and ABC.CPP content is the same as b.cpp. Then, whenever the structure ABC is to be used in a source file in the project, it contains ABC.h at the beginning of that source file, which is equivalent to bringing all relevant declarations of structure ABC into the compilation of that file, such as the preceding a.cpp by including ABC in the beginning to declare the structure ABC.
Why do I have to generate a ABC.cpp? If the ABC::AB definition statement is also placed in the ABC.h, then a.cpp to use abc,c.cpp also use ABC, so a.cpp contains ABC.h, because of the definition of abc::ab inside, generate a symbol [email protected]@@[ e-Mail protected] (for VC), the same c.cpp compilation to generate this symbol, and then when connected, due to the presence of two identical symbols, the connector can not determine which one to use, error. Therefore, a ABC.cpp is defined specifically to place the definition of the function abc::ab in Abc.obj, so that only one symbol is generated and the connection is no longer an error.
Notice the above struct ABC {void AB (float) {}};. If you put this in ABC.h, because the definition of the function Abc::ab has been given in the type definition, then two identical symbols will appear, and then the connection fails. In order to avoid this problem, C + + stipulates that the function defined in the type definition is directly defined as the inline function, for the sake of space, the following article describes.

C + + function declaration and definition

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.