C built-in functions and "undefined external" error

I'm trying to replace some macro routines with built-in functions, so the compiler can optimize them, so the debugger can go into them, etc. If I define them as regular functions, it works:

void do_something(void) { blah; } void main(void) { do_something(); } 

but if I define them as inline:

 inline void do_something(void) { blah; } void main(void) { do_something(); } 

he says: "Error: Undefined external." What does it mean? Taking a punch in the dark, I tried

 static inline void do_something(void) { blah; } void main(void) { do_something(); } 

and more mistakes. The function definition and function call are in the same .c file.

Can someone explain why one works and the other doesn't?

(Second related question: where can I put inline functions if I want to use them in more than one .c file?)

+6
source share
3 answers

Firstly, the compiler does not always perform the built-in functions marked as inline ; for example, if you turn off all optimizations, it probably will not lead to their inclusion.

When you define an inline function

 inline void do_something(void) { blah } 

and to use this function, even in the same file, the call to this function is permitted by the linker, not the compiler, because it is implicitly "extern". But this definition alone does not provide an external definition of a function.

If you include an ad without inline

 void do_something(void); 

in the C file that the inline definition can see, the compiler will provide an external definition of the function, and the error should go away.

The reason for static inline is that it makes the function visible only inside this compilatioin module, and therefore allows the compiler to allow the function to be called (and optimize it) and emit code for the function inside this compilation unit, then the compiler should not allow it, so there is no need in the external definition.

The best place to place an inline function is in the header file and declares them static inline . This eliminates the need for an external definition, and therefore eliminates the linker problem. However, this causes the compiler to emit code for the function in each compilation unit that uses it, so this can lead to bloat code. But since the function is built-in, it is probably small, so this is usually not a problem.

Another option is to define it as extern inline in the header, and in a single C file and extern declaration without an inline modifier.

The gcc manual explains it this way:

By declaring an inline function, you can redirect GCC to calls to this function faster. One way GCC can achieve this is to integrate this function code into the code for its subscribers. This makes execution faster by eliminating utility function calls; in addition, if any of the actual values ​​of the argument is constant, their known values ​​can be simplified during compilation, all the built-in function code must be included. The effect on code size is less predictable; the object code may be larger or smaller with an attachment function, as the case may be. You can also directly GCC to try to integrate all the "simple" functions into their callers with the -finline-functions option.

GCC implements three different semantics of function declarations in a queue. One of them is available with -std=gnu89 or -fgnu89-inline or when the gnu_inline attribute gnu_inline present in all inline ads, the other when -std=c99 , -std=c1x , -std=gnu99 or -std=gnu1x (without -fgnu89-inline ), and the third is used when compiling C ++.

To declare an inline function, use the inline in your declaration, for example:

  static inline int inc (int *a) { return (*a)++; } 

If you are writing a header file that should be included in ISO C90 programs, write __inline__ instead of inline .

Three types of inlining behave similarly in two important cases: when the inline used for a static function, for example, the example above, and when the function is first declared without using inline , and then defined using inline , for example:

  extern int inc (int *a); inline int inc (int *a) { return (*a)++; } 

In both of these general cases, the program behaves as if you had not used the inline , except for its speed.

When a function is both built-in and static , if all calls to the function are integrated into the caller, and the address of the function is never used, then the function of the native assembler code will never be referenced. In this case, GCC does not actually output assembly code for the function unless you specify the -fkeep-inline-functions option. Some calls cannot be integrated for various reasons (in particular, calls preceding a function definition cannot be integrated, and there cannot be recursive calls within the definition). If there is a non-integrated call, then the function is compiled for assembler code, as usual. This function should also be compiled, as usual, if the program refers to its address, because it cannot be nested.

Note that certain definitions in a function definition may make this unsuitable for inline replacement. Among these usages: using varargs, using alloca, using variable-sized data types, using computed goto, using non-local goto, and nested functions. Using -Winline will warn if the function marked inline cannot be replaced and will indicate the reason for the failure.

In accordance with the requirements of ISO C ++, GCC considers member functions defined within the body of a class, which should be marked as inline even if it is not explicitly declared with the inline . You can undo this with -fno-default-inline .

GCC does not perform any functions unless optimized, unless you specify the always_inline attribute for the function, for example:

  /* Prototype. */ inline void foo (const char) __attribute__((always_inline)); 

The rest of this section is for embedding the GNU C90.

When the built-in function is not static , then the compiler should assume that there may be calls from other source files; since a global symbol can be defined only once in any program, the function should not be defined in other source files, therefore, calls to them cannot be integrated. Therefore, the inline function is not static always composed by itself in normal mode.

If both inline and extern are specified in the function definition, then the definition is used only for nesting. In no case is a function compiled on its own, even if you reference its address explicitly. Such an address becomes an external link, as if you only declared a function and did not define it.

This combination of inline and extern has almost a macro effect. The way to use it is to put the function definition in the header file with these keywords, and put another copy of the definition (lack of inline and extern ) in the library file. Defining a header file will cause most calls to the function to be inlined. If any use of the function remains, they will refer to one copy in the library.

+9
source

For inline functions for working with C99 (they appeared only in this language) you would need to give a definition in the header file

 inline void do_something(void) { blah } 

and in one compilation unit (aka.c) you put some kind of "instance"

 void do_something(void); 

without inline .

+1
source

You must put them in the header file if you want to use them from multiple files.

And for the linker error: the default declaration for the function implies that it is "extern", but since it is built-in, the linker can find the character stub generated by the compiler, hence the error.

0
source

Source: https://habr.com/ru/post/909100/


All Articles