我正在阅读linux内核,我发现了很多像这样的宏:
#define INIT_LIST_HEAD(ptr) do { \
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
Run Code Online (Sandbox Code Playgroud)
为什么他们使用这个而不是简单地在{}中定义它?
当我尝试构建此代码时
inline void f() {}
int main()
{
f();
}
Run Code Online (Sandbox Code Playgroud)
使用命令行
gcc -std=c99 -o a a.c
Run Code Online (Sandbox Code Playgroud)
我收到链接器错误(未定义引用f).如果我使用static inline或extern inline代替just inline,或者如果我使用-O(因此函数实际内联),则错误消失.
这种行为似乎在C99标准第6.7.4(6)段中定义:
如果转换单元中函数的所有文件范围声明都包含
inline函数说明符extern,则该转换单元中的定义是内联定义.内联定义不提供函数的外部定义,也不禁止另一个转换单元中的外部定义.内联定义提供了外部定义的替代,翻译器可以使用该定义在同一翻译单元中实现对该功能的任何调用.未指定对函数的调用是使用内联定义还是使用外部定义.
如果我理解了这一切,inline那么如果还有一个具有相同名称的外部函数,那么具有上例中定义的函数的编译单元只会一致地编译,而且我永远不知道我自己的函数或外部函数是否被调用.
这种行为不是完全愚蠢吗?在inline没有static或extern在C99中定义函数是否有用?我错过了什么吗?
当然我错过了什么,行为并不愚蠢.:)
正如Nemo所解释的那样,我们的想法是定义函数
inline void f() {}
Run Code Online (Sandbox Code Playgroud)
在头文件中只有一个声明
extern inline void f();
Run Code Online (Sandbox Code Playgroud)
在相应的.c文件中.只有extern声明才会触发生成外部可见的二进制代码.确实没有inline在.c文件中使用 - 它只在标题中有用.
正如Jonathan的回答中引用的C99委员会的基本原理所阐述的那样,inline所有关于编译器优化都需要在调用的站点上看到函数的定义.这只能通过将标题放在标题中来实现,当然,标题中的定义在编译器每次看到它时都不能发出代码.但由于编译器不是强制实际内联函数,因此必须在某处存在外部定义.
我理解"内联"本身就是对编译器的建议,并且在它的结构中它可能会或可能不会内联函数,它也会产生可链接的目标代码.
我认为"静态内联"执行相同的操作(可能内联也可能不内联),但在内联时不会产生可链接的目标代码(因为没有其他模块可以链接到它).
"extern inline"在哪里适合图片?
假设我想用内联函数替换预处理器宏,并要求此函数被内联(例如,因为它使用__FILE__和__LINE__宏,这些宏应该为调用者而不是这个调用函数解析).也就是说,如果函数没有内联,我想看到编译器或链接器错误."extern inline"这样做吗?(我假设,如果没有,除了坚持使用宏之外,没有办法实现这种行为.)
C++和C之间是否存在差异?
不同编译器供应商和版本之间是否存在差异?
pg474,KNKing
"C99中的一般规则是,如果特定文件中函数的所有顶级声明都包含内联而非外部,那么该文件中函数的定义是内联的."
"如果函数在程序中的任何地方使用(包括包含其内联声明的文件),那么函数的外部声明将需要由其他文件提供.当调用该函数时,编译器可以选择执行一个普通的调用(使用函数的外部定义)或执行内联扩展(使用函数的内联定义).无法确定编译器将做出哪个选择,因此两个定义保持一致至关重要."
"具有静态存储持续时间的变量是具有外部链接的内联函数的特殊问题"
但我认为你无法通过外部链接调用函数!编译器会给出一个错误:
第473页
"所以试图从另一个文件中调用平均值将被视为错误"
"因此,C99对具有外部链接的内联函数施加了以下限制(但不对具有内部链接的内联函数):该函数可能未定义可修改的静态变量.该函数可能不包含对具有内部链接的变量的引用."
为什么??如果函数是内联函数和外部函数,那么即使它确实声明了一个静态int i; 由于函数无法链接到你无法调用它,但是不会在内联函数堆栈框架外创建静态变量 - 所以你应该能够链接到它?内联函数有堆栈框架吗?这里发生了什么??