C++ 为什么允许指向内联函数的指针?
我有两个问题:C++ 为什么允许指向内联函数的指针?,c++,function,inline,C++,Function,Inline,我有两个问题: 1)为什么C++中允许使用内联函数指针? 我读到内联函数的代码只是被复制到函数调用语句中,内联函数中没有编译时内存分配。那么,既然内联函数没有固定的内存地址,为什么可以存在指向内联函数的指针呢 2)考虑下面的代码: inline void func() { int n=0; cout<<(&n); } inline void func() { int n=0; cout这个函数最初是向编译器发出的一个提示,即程序员认为这个函数是
1)为什么C++中允许使用内联函数指针? 我读到内联函数的代码只是被复制到函数调用语句中,内联函数中没有编译时内存分配。那么,既然内联函数没有固定的内存地址,为什么可以存在指向内联函数的指针呢
2)考虑下面的代码:
inline void func()
{
int n=0;
cout<<(&n);
}
inline void func()
{
int n=0;
cout这个函数最初是向编译器发出的一个提示,即程序员认为这个函数是内联的候选函数-编译器不需要遵守这一点
在现代使用中,它与内联几乎没有任何关系——现代编译器“在你背后”自由内联(或不内联)函数,这些构成了优化技术的一部分
<> >强>代码转换(包括内联)是在C++中的/<强>下完成的,这基本上意味着编译器可以按照需要来转换代码,只要执行是“好像”原始代码是按写执行的。这个规则在C++中优化了。
也就是说,一旦函数的地址被获取,它就必须存在(即地址必须有效)。这可能意味着它不再是内联的,但仍然可以是内联的(优化器将应用适当的分析)
既然内联函数没有固定的内存地址,那么为什么可以存在指向内联函数的指针呢
不,这只是一个提示,主要与链接有关,而不是实际的内联。这助长了当前的主要用法,在头文件中定义函数
它是否应该在每次调用func()
时打印不同的n
地址值
可能,n
是一个局部变量,基于函数执行时的堆栈位置。也就是说,函数inline
,它与链接相关,链接器将在翻译单元上合并函数
如中所述
…如果示例更改为static int n
,则对函数的每次调用都必须打印一个常量值(当然是在单个程序运行中)…这是真的,无论代码是否内联
这也是链接要求对局部变量n
的影响。内联函数并不总是内联的。它只是表示程序员希望内联此函数
如果使用函数的地址,则函数很可能未内联到最终可执行文件中,至少在GCC中是这样:
当函数既是内联函数又是静态函数时,如果对该函数的所有调用都集成到调用方中,并且从未使用该函数的地址,则从未引用该函数自己的汇编代码
您阅读了旧资料。现在使用内联
的主要原因是允许在头文件中使用函数体。使用带有函数的内联
关键字向链接器发出信号,表示可以组合跨翻译单元的函数的所有实例;在包含mul的头文件中使用非内联函数由于违反一个定义规则,多个单元会导致未定义的行为
C++17还添加了一些属性,这些属性与可以在头中定义变量的属性相同,并且所有定义都由链接器组合,而不是导致ODR冲突
关于“代码复制到调用函数”的内容称为内联,与inline
关键字无关。编译器将根据优化设置决定是否对非内联函数和内联函数执行此操作
1)为什么C++中允许内联函数指针?< /P>
因为内联函数和其他函数一样,指向它们是函数可以做的事情之一。内联函数在这方面并不特殊
我读到内联函数的代码只是被复制到函数调用语句中,内联函数中没有编译时内存分配
你(也许还有你读过的材料)混合了两个相关且命名相似的概念
内联函数在使用它的所有翻译单元中定义,而非内联函数仅根据一个定义规则的要求在一个翻译单元中定义。这就是函数的内联声明的含义;它放松了一个定义规则,但也给出了在所有翻译单元中定义的附加要求使用它的单位(如果odr没有放松,这是不可能的)
内联扩展(或内联)是一种优化,通过将被调用函数复制到调用方的框架中来避免函数调用。函数调用可以内联扩展,无论函数是否声明为内联。而声明为内联的函数不一定是内联扩展的
但是,函数不能在未定义的转换单元中进行内联扩展(除非链接时间优化执行扩展)。因此,内联声明允许在所有TU中定义的要求,也允许在调用函数的所有TU中定义函数,从而使函数的内联扩展成为可能。但优化无法保证
2) 它是否应该在每次调用func()时打印不同的地址n值
内联扩展确实会导致局部变量位于调用方的框架中,是的。但是,如果调用来自不同的框架,则无论扩展如何,它们的位置都会不同
通常,任何内联扩展的函数都会生成一个常规的非扩展版本。如果获取函数的地址,它将指向该非扩展函数。如果
#include <iostream>
int foo(int (*fun)(int), int x) {
return fun(x);
}
int succ(int n) {
return n+1;
}
int main() {
int c=0;
for (int i=0; i<10000; ++i) {
c += foo(succ, i);
}
std::cout << c << std::endl;
}
_Z3fooPFiiEi:
.LFB998:
.cfi_startproc
movq %rdi, %rax
movl %esi, %edi
jmp *%rax
.cfi_endproc
.LFE998:
.size _Z3fooPFiiEi, .-_Z3fooPFiiEi
.p2align 4,,15
.globl _Z4succi
.type _Z4succi, @function
_Z4succi:
.LFB999:
.cfi_startproc
leal 1(%rdi), %eax
ret
.cfi_endproc
.LFE999:
.size _Z4succi, .-_Z4succi
.section .text.startup,"ax",@progbits
.p2align 4,,15
.globl main
.type main, @function
main:
.LFB1000:
.cfi_startproc
movdqa .LC1(%rip), %xmm4
xorl %eax, %eax
pxor %xmm1, %xmm1
movdqa .LC0(%rip), %xmm0
movdqa .LC2(%rip), %xmm3
jmp .L5
.p2align 4,,10
.p2align 3
.L8:
movdqa %xmm2, %xmm0
.L5:
movdqa %xmm0, %xmm2
addl $1, %eax
paddd %xmm3, %xmm0
cmpl $2500, %eax
paddd %xmm0, %xmm1
paddd %xmm4, %xmm2
jne .L8
movdqa %xmm1, %xmm5
subq $24, %rsp
.cfi_def_cfa_offset 32
movl $_ZSt4cout, %edi
psrldq $8, %xmm5
paddd %xmm5, %xmm1
movdqa %xmm1, %xmm6
psrldq $4, %xmm6
paddd %xmm6, %xmm1
movdqa %xmm1, %xmm7
movd %xmm7, 12(%rsp)
movl 12(%rsp), %esi
call _ZNSolsEi
movq %rax, %rdi
call _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
xorl %eax, %eax
addq $24, %rsp
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE1000:
.size main, .-main
.p2align 4,,15
.type _GLOBAL__sub_I__Z3fooPFiiEi, @function
_GLOBAL__sub_I__Z3fooPFiiEi:
.LFB1007:
.cfi_startproc
subq $8, %rsp
.cfi_def_cfa_offset 16
movl $_ZStL8__ioinit, %edi
call _ZNSt8ios_base4InitC1Ev
movl $__dso_handle, %edx
movl $_ZStL8__ioinit, %esi
movl $_ZNSt8ios_base4InitD1Ev, %edi
addq $8, %rsp
.cfi_def_cfa_offset 8
jmp __cxa_atexit
.cfi_endproc
.LFE1007:
.size _GLOBAL__sub_I__Z3fooPFiiEi, .-_GLOBAL__sub_I__Z3fooPFiiEi
.section .init_array,"aw"
.align 8
.quad _GLOBAL__sub_I__Z3fooPFiiEi
.local _ZStL8__ioinit
.comm _ZStL8__ioinit,1,1