Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's is the idea behind C99 inline?

I am confused about inline in C99.

Here is what I want:

  1. I want my function get inlined everywhere, not just limited in one translation unit (or one compilation unit, a .c file).
  2. I want the address of the function consistent. If I save the address of the function in a function pointer, I want the function callable from the pointer, and I don't want duplication of the same function in different translation units (basically, I mean no static inline).

C++ inline does exactly this.

But (and please correct me if I am wrong) in C99 there is no way to get this behavior.

I could have use static inline, but it leads to duplication (the address of the same function in different translation unit is not the same). I don't want this duplication.

So, here are my questions:

  1. What is idea behind inline in C99?
  2. What benefits does this design give over C++'s approach?

References:

  1. Here's a link that speaks highly of C99 inline, but I don't understand why. Is this “only in exactly one compilation unit” restriction really that nice?
    http://gustedt.wordpress.com/2010/11/29/myth-and-reality-about-inline-in-c99/
  2. Here's the Rationale for C99 inline. I've read it, but I don't understand it.
    Is "inline" without "static" or "extern" ever useful in C99?
  3. A nice post, provides strategies for using inline functions.
    http://www.greenend.org.uk/rjk/tech/inline.html

Answers Summary

How to get C++ inline behavior in C99 (Yes we can)

head.h

#ifndef __HEAD_H__
#define __HEAD_H__

inline int my_max(int x, int y) {
    return (x>y) ? (x) : (y);
}

void call_and_print_addr();

#endif

src.c

#include "head.h"
#include <stdio.h>

// This is necessary! And it should occurs and only occurs in one [.c] file
extern inline int my_max(int x, int y); 

void call_and_print_addr() {
    printf("%d %u\n", my_max(10, 100), (unsigned int)my_max);
}

main.c

#include <stdio.h>
#include "head.h"

int main() {
    printf("%d %u\n", my_max(10, 100), (unsigned int)my_max);
    call_and_print_addr();

    return 0;
}

Compile it with: gcc -O3 main.c src.c -std=c99
Check the assembly with: gcc -O3 -S main.c src.c -std=c99, You'll find that my_max is inlined in both call_and_print_addr() and main().

Actually, this is exactly the same instructions given by ref 1 and ref 3. And what's wrong with me?

I used a too old version of GCC (3.4.5) to experiment, it give me “multiple definition of my_max” error message, and this is the real reason why I am so confused. Shame.

Difference between C99 and C++ inline

Actually you can compile the example above by g++: g++ main.c src.c

extern inline int my_max(int x, int y);

is redundant in C++, but necessary in C99.

So what does it do in C99?

Again, use gcc -O3 -S main.c src.c -std=c99, you'll find something like this in src.s:

_my_max:
    movl    4(%esp), %eax
    movl    8(%esp), %edx
    cmpl    %eax, %edx
    cmovge  %edx, %eax
    ret
    .section .rdata,"dr"

If you cut extern inline int my_max(int x, int y); and paste it into main.c, you'll find these assembly code in main.s.

So, by extern inline, you tell the compiler where the true function my_max(), which you can call it by its address, will be defined and compiled.

Now look back in C++, we can't specify it. We will never know where my_max() will be, and this is the “vague linkage” by @Potatoswatter.

As is said by @Adriano, most of the time, we don't care about this detail, but C99 really removes the ambiguity.

like image 462
zsh Avatar asked Apr 24 '14 15:04

zsh


People also ask

What's the point of inline function?

An inline function is one for which the compiler copies the code from the function definition directly into the code of the calling function rather than creating a separate set of instructions in memory. This eliminates call-linkage overhead and can expose significant optimization opportunities.

What's an inline function in C?

Inline Function are those function whose definitions are small and be substituted at the place where its function call is happened. Function substitution is totally compiler choice.

What does static inline mean in C?

Static inline functions are simple. Either a function defined with the inline function specifier is inlined at a reference, or a call is made to the actual function. The compiler can choose which to do at each reference. The compiler decides if it is profitable to inline at -xO3 and above.

What is inlining in programming?

Inlining is the process of replacing a subroutine or function call at the call site with the body of the subroutine or function being called. This eliminates call-linkage overhead and can expose significant optimization opportunities.


2 Answers

To get C++-like behavior, you need to give each TU with potentially-inlined calls an inline definition, and give one TU an externally-visible definition. This is exactly what is illustrated by Example 1 in the relevant section (Function specifiers) of the C standard. (In that example, external visibility is retroactively applied to an inline definition by declaring the function extern afterward: this declaration could be done in the .c file after the definition in the .h file, which turns usual usage on its head.)

If inlining could be accomplished literally everywhere, you wouldn't need the extern function. Non-inlined calls are used, however, in contexts such as recursion and referencing the function address. You may get "always inline" semantics, in a sense, by omitting the extern parts, however this can arbitrarily fail for any simple function call because the standard does not demand that a call be inlined just because there is no alternative. (This is the subject of the linked question.)

C++ handles this with the implementation concept of "vague linkage"; this isn't specified in the standard but it is very real, and tricky, inside the compiler. C compilers are supposed to be easier to write than C++; I believe this accounts for the difference between the languages.

like image 167
Potatoswatter Avatar answered Nov 16 '22 04:11

Potatoswatter


I want my function get inlined everywhere, not just limited in one translation unit(or one compile unit, a [.c] file).

With inline you politely ask your compiler to inline your function (if it has time and mood). It's unrelated to one compilation unit, at best it may even get inlined in every single call site and it won't have a body anywhere (and its code will be duplicated everywhere). It's purpose of inlining, speed in favor of size.

I want the address of the function consistent. If I save the address of the function in a function pointer, I want the function callable from the pointer, and I don't want duplication of the same function in different translation unit. (Basically, I mean no 'static inline')

Again you can't. If function is inlined then there is not any function pointer to it. Of course compiler will need a compilation unit where function will stay (because, well yes, you may need a function pointer or sometimes it may decide to do not inline that function in a specific call site).

From your description it seems that static inline is good. IMO it's not, a function body (when used, see above paragraph) in each compilation unit will lead to code duplication (and problem in comparison of function pointers because each compilation unit will have its own version of your function). It's here that C99 did something pretty good: you declare exactly one place to put function body (when and if required). Compiler won't do it for you (if you ever care about it) and there is nothing left to implementor.

What is idea behind inline in C99?

Pick a good thing (inline functions) but remove ambiguity (each C++ compiler did his own job about where function body has to stay).

What benefits does this design give over C++'s approach?

Honestly I can't see such big problem (even article you linked is pretty vague about this benefit). In a modern compiler you won't see any issue and you will never care about that. Why it's good what C did? IMO because it removed an ambiguity even if - frankly speaking - I'd prefer my compiler does that for me when I don't care about it (99.999%, I suppose).

That said, but I may be wrong, C and C++ have different targets. If you're using C (not C++ without classes and few C++ features) then probably you want to address this kind of details because they matters in your context so C and C++ had to diverge about that. There is not a better design: just different decision for a different audience.

like image 22
Adriano Repetti Avatar answered Nov 16 '22 04:11

Adriano Repetti