9

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.

Community
  • 1
  • 1
zsh
  • 91
  • 3
  • 1
    You cannot get "inlined everywhere," for example what happens if the function recurses or, as you mention, it's called through a pointer? – Potatoswatter Apr 24 '14 at 15:17
  • 7
    This question could be improved by removing the ranty parts. – Simon Bergot Apr 24 '14 at 15:18
  • 3
    "duplication really makes me uncomfortable". Then why you're using inlining? With such suggestion to your compiler you make it free (and happy) to produce a lot of duplicated code... – Adriano Repetti Apr 24 '14 at 15:19
  • To reduce code duplication, you might want to look into Link Time Code Generation/Link Time Optimization – z̫͋ Apr 24 '14 at 15:29
  • 1
    @Simon Sorry, I learn English as a second language. I didn't try to be angry, but maybe somehow it looks offensive. And Oliver Matthews, thanks for editing the post. – zsh Apr 24 '14 at 15:42
  • By saying "duplication" I mean same function, different address. It's bad. @Adriano – zsh Apr 24 '14 at 15:50
  • @Potatoswatter, I mean "not limited in one translation unit". – zsh Apr 24 '14 at 15:51
  • It is impossible to have a function inlined everywhere and at the same time to have a unique address. the purpose of inlining is to reduce calling overhead by copying the function body into the calling code. – Alexander Oh Apr 24 '14 at 16:04
  • 1
    @Alex "It is impossible to have a function inlined everywhere and at the same time to have a unique address.". The C++ standard explicitly requires that an `inline` function has a unique address, and implementations have no problem complying. (A *function* is not inlined, a *function call* is; a function call, inlined or otherwise, has no address, but a function has). – n. m. could be an AI Apr 24 '14 at 16:09
  • @n.m. compiler will inline function call with...function itself (inline function). An inline function has a pointer only in its "extern". When inlined a **function won't just be naked but also optimized in the caller context**. – Adriano Repetti Apr 24 '14 at 16:12
  • @n.m. the standard mandates that retrieving the address of an inline function yields a unique result. If the compiler inlines the function body into the call sites in inevitable to copy the function body to a different address. So even with property 1. and property 2. you still get duplication. the only thing it prevents is having a linker symbol pointing to two different addresses. – Alexander Oh Apr 24 '14 at 16:51
  • @Alex "If the compiler inlines the function body into the call sites in inevitable to copy the function body to a different address.". This has absolutely nothing to do with the *meaning* of any standards-conforming program. It is an implementation detail that affects only performance, but not semantics. – n. m. could be an AI Apr 24 '14 at 17:25

2 Answers2

6

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.

Community
  • 1
  • 1
Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • No, you need exactly one `extern` declaration for the program, not one in each TU. – n. m. could be an AI Apr 24 '14 at 16:04
  • @n.m. Thanks, I misunderstood that point. I do think such a declaration is good expository style though. – Potatoswatter Apr 24 '14 at 16:09
  • If you put multiple `extern` declarations in different compilation units for the same `inline` function, you'll likely get linker errors (duplicate symbols). – Chris Dodd Apr 24 '14 at 16:14
  • @ChrisDodd That would happen for *definitions*. The term *`extern` declaration*, by default, means non-definition. – Potatoswatter Apr 24 '14 at 16:17
  • 1
    @Potatoswatter: Not if the function is defined as `inline` in the compilation unit. An `extern` decalration for a funciton with a `inline` definition in the same compilation unit means "compile the function in this compilation unit AND make it externally visible as a symbol to other compilation units" – Chris Dodd Apr 24 '14 at 16:24
  • 1
    @ChrisDodd Ah, now I see, the extern declaration after inline definition retroactively causes the preceding definition to be forwarded along to the linker. Sorry, I'm processing the C99 spec ab initio because I'm not really a C programmer :P . – Potatoswatter Apr 24 '14 at 16:33
  • If the Standard were to forbid code from taking the address of a function declared `inline`, in what cases would a compiler need an exported definition for it? Even if a compiler can't inline-insert the code for such a function, it should still be able to generate a static function and call that; many build systems might even be able to merge duplicate functions at link time. – supercat Aug 17 '15 at 21:09
3

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.

Adriano Repetti
  • 65,416
  • 20
  • 137
  • 208
  • 2
    _politely ask your compiler to inline your function (if it has time and mood)_ I will never be able to think of my compiler the same way again. – ryyker Apr 24 '14 at 15:56
  • 1
    "If function is inlined then there is not any function pointer to it". A function is not inlined, a function call is. Of course there are no pointers to function calls, inlined or otherwise. An `inline` function has an address. – n. m. could be an AI Apr 24 '14 at 16:06
  • 2
    @n.m. well a function call is inlined (with function body) even if not formally correct "inlined function" is widely used to express that concept. An inline function has an address (in C99) on its extern declaration but it's not what has been inlined (does it matter? probably only if you inject code). – Adriano Repetti Apr 24 '14 at 16:09