C 和内联函数中的独立目标代码

stand alone object code in C and inline functions

当我读到 Inline Functions In Cinline 函数时,我看到了这条线:

Sometimes it is necessary for the compiler to emit a stand-alone copy of the object code for a function even though it is an inline function - for instance if it is necessary to take the address of the function, or if it can't be inlined in some particular context, or (perhaps) if optimization has been turned off. (And of course, if you use a compiler that doesn't understand inline, you'll need a stand-alone copy of the object code so that all the calls actually work at all.)

我完全不知道它想说什么,有人可以专门解释一下什么是独立目标代码吗?

如您所知,"inline" 函数被翻译成 "right there." 的机器指令,每次出现新的 "call" 函数时,这些指令都会逐字重复不同的地方——函数实际上不是 "called."(内联函数非常像汇编程序 "macro.")

但是,如果您要求(比如)"the address of" 函数,编译器必须生成它的非内联副本才能为您提供 one"place where it is."

"Object code"一般是指编译器的输出交给链接器,作为生成机器码前的中间步骤。

文中所说的是,如果您出于某种原因获取函数的地址,例如使用指向它的函数指针,则无法内联该函数。因为内联函数没有可以通过函数指针调用的地址。内联函数只是与调用代码链接在一起,实际上没有进行任何函数调用。

这里有一个例子:

#include <stdio.h>
#include <stdlib.h>

extern inline __attribute__((always_inline)) int mul16(int x) {
    return x * 16; }

extern inline __attribute__((always_inline)) int mul3(int x) {
    return x * 3; }

int main() {

    for(int i = 0; i < 10; i ++)
    {
        int (*ptr)(int) = rand() & 1 ? mul16 : mul3;
        printf("Mul2 = %d", mul16(i));
        printf(", ptr(i) = %d\n", ptr(i));
    } 
}

https://godbolt.org/z/wDpF4j

mul16 作为单独的对象存在,也内联在同一代码中。

mul16:   <----- object
        mov     eax, edi
        sal     eax, 4
        ret
mul3:
        lea     eax, [rdi+rdi*2]
        ret
.LC0:
        .string "Mul2 = %d"
.LC1:
        .string ", ptr(i) = %d\n"
main:
        push    r12
        push    rbp
        push    rbx
        mov     ebx, 0
        mov     r12d, OFFSET FLAT:mul16
.L5:
        call    rand
        test    al, 1
        mov     ebp, OFFSET FLAT:mul3
        cmovne  rbp, r12
        mov     esi, ebx
        sal     esi, 4            <-------------- inlined version
        mov     edi, OFFSET FLAT:.LC0
        mov     eax, 0
        call    printf
        mov     edi, ebx
        call    rbp
        mov     esi, eax
        mov     edi, OFFSET FLAT:.LC1
        mov     eax, 0
        call    printf
        add     ebx, 1
        cmp     ebx, 10
        jne     .L5
        mov     eax, 0
        pop     rbx
        pop     rbp
        pop     r12
        ret