我知道一般操作是 static inline 加函数定义放到头文件里。
first.c
inline void functionName(int first, int secend) {/****/ }
second.c
extern void functionName(int first, int secend);
int main()
{
functionName(1, 2);
return 0;
}
上面两个文件这么写,在 vs2019 上会报错。但是我看一些博客上说,是可以让 inline 修饰非 static 的全局函数。
所以我这么用是错的呗?
1
wudicgi 2022-01-14 01:14:42 +08:00
> 我知道一般操作是 static inline 加函数定义放到头文件里。
那为什么不按这个来呢 如果一定要把函数代码写在 .c 里,就去掉 first.c 里函数定义的 inline 修饰符好了, 让编译器决定什么时候内联,什么时候调用 又要内联,又要在其他 .c 里调用,这让编译器很难办啊 |
2
Buges 2022-01-14 01:21:28 +08:00 via Android 1
既要 inline ,又要 extern ,你这是为难编译器吧。
|
3
wevsty 2022-01-14 02:02:57 +08:00
inline 编译器要求随时能找到代码,不然怎么给你内联进去。
|
4
catror 2022-01-14 02:22:45 +08:00 via Android
inline 函数展开后符号都没了,extern 自然就没意义了。
|
5
dangyuluo 2022-01-14 04:23:39 +08:00
什么时候一般操作就是 static inline 函数放在头文件里了。。
|
6
codehz 2022-01-14 08:12:27 +08:00 via Android
C++的 inline 语义并不是内联函数(内联是副作用)
主要作用是允许同符号在多个翻译单元中多次出现(然后假设每次都一致),同时 inline 也要求必须在同翻译单元找到定义(除非没用到) extern inline 实际上按标准只能做同一个翻译单元里的前向声明。 而在不同翻译单元混用 extern (无 inline )和 inline 实际上属于未定义行为,因为不符合前面所说的定义一致原则 |
7
GeruzoniAnsasu 2022-01-14 08:17:53 +08:00
首先…………………………
extern 是「外部声明」,它的作用是告诉链接器 (!!) 注意不是编译器,到其它文件去找这个符号。如果 extern 直接放在定义的地方,则是告诉链接器「我这个符号应该让所有地方都能看得到」。 但你这里 extern 只是一个声明。 第二件事,inline 的意义在于告诉编译器 (!!) 注意是编译器,我这个符号的定义可能会重复出现很多次(因为会被 include 到不同文件里去),你就直接把找到的定义照抄放到调用点就行,不用暴露,不会有文件要链接我。 所以你正在试图一边让链接器去找某个符号,一边让编译器不要暴露这个符号以供链接…… 「 inline 的非 static 全局函数」是这样的: a.h inline void g_f(){} a.c #include "a.h" void a(){g_f();} b.c #include "a.h" void b(){g_f();} main.c extern void a(); extern void b(); int main() {a();b();} |
8
iceheart 2022-01-14 08:24:34 +08:00 via Android
声明与定义不一致。
访问 extern 只要一个声明,访问 inline 需要在同一个编译单元内。 |
9
Jabin 2022-01-14 08:50:50 +08:00 via Android
加了 inline ,编译器也不一定按 inline 函数处理
|
10
nightwitch 2022-01-14 09:18:57 +08:00
static inline 其实就是等于 static ,含义是被 static 修饰的函数 /变量在每个编译单元内部可见,如果在头文件里的话每个编译单元都会复制一份。
inline 现在的含义是允许一个函数 /变量在多个编译单元重复定义,在链接的时候会随机挑选一个符号链上。 这两个关键词出现在头文件的时候,含义是截然不同的。 |
11
lovelylain 2022-01-14 09:19:35 +08:00 via Android
static 作用是把符号限制在本编译单元,这样同名函数多个编译单元定义也不会报错; inline 作用是建议编译器内联展开,但是编译器也可能不内联,如果你想函数体
|
12
lovelylain 2022-01-14 09:29:33 +08:00 via Android
static 作用是把符号限制在本编译单元,这样同名函数多处定义也不会报错; inline 作用是建议编译器内联展开,但是编译器也可能不内联,如果内联了,当然不存在符号问题,没内联的话,相当于会在.o 里生成一个弱符号函数,链接时同名的只会取一份。所以按需
|
13
lovelylain 2022-01-14 09:41:15 +08:00 via Android
static 作用是把符号限制在本编译单元,这样同名函数多处定义也不会报错; inline 作用是建议编译器内联展开,但是编译器也可能不内联,如果内联了,当然不存在符号问题,没内联的话,相当于会在.o 里生成一个弱符号函数,链接时同名的只会取一份。所以按需选择吧:
如果你定义在.c 文件里面,建议 static inline ,因为如果其他.c 有跟你同名但不同实现的 inline ,是有风险的; 如果你定义在.h 里,建议 inline ,在保证只有这个.h 里 inline 定义该函数的前提下,可以使没 inline 时共享同一份实现,减小文件体积,当然你怕出错用 static inline 也行; 还有个 extern inline ,会在.o 里生成一个强符号,也有用处,但也容易出错,几乎没人用。 |
14
agagega 2022-01-14 09:52:13 +08:00
你这两个地方声明都不一样啊。不过 C 标准里是有 extern inline 这种东西的
|
15
edimetia3d 2022-01-14 12:52:03 +08:00
我其实并不记得所有的语言规范细节了,我只记得 C 和 C++不一致, 不同编译器可能不一致,不同语言版本可能不一致.
我脑海里记下的结论为: To live simpler, always use `static inline` 针对你这个问题, C++ 要求 inline 的函数在每个编译单元都是 inline 且可见的. |
16
amiwrong123 OP @GeruzoniAnsasu #7
没想到你这个居然编译通过了,那感觉你这个 inline 就好像起到了 static 的作用呢??因为两处的 g_f(){}的符号应该是一样的应该出产生冲突的,但是却没有。 然后我根据你的这个程序,写了下面这个: first.cpp ```cpp #include <iostream> using namespace std; inline void g_f() { cout << "first" << endl; } void a() { cout << "a" << endl; g_f(); } ``` second.cpp ```cpp #include <iostream> using namespace std; inline void g_f() { cout << "second" << endl; } extern void a(); void b() { cout << "b" << endl; g_f(); } int main() { a(); b(); return 0; } ``` 然后打印出来这个,这个怎么解释呢?😂 a second b second |
17
amiwrong123 OP @codehz #6
老哥你看一下我上面那个程序,我这个程序应该就是你说的:允许同符号在多个翻译单元中多次出现 g_f 函数在两个翻译单元都出现了。 本来我以为会打印出来 a first b second 但是却不是。 我不知道该怎么解释我的程序结果了 |
18
amiwrong123 OP @jobmailcn #13
老哥你看一下我上面那个程序,我这个程序应该就是你说的:如果内联了,当然不存在符号问题。 但是还是有点不明白,调用 a()的时候,不应该用的是 first.cpp 里的函数定义吗,怎么还是用的 second.cpp 里的函数定义。 |
19
amiwrong123 OP first.cpp
```cpp #include <iostream> using namespace std; inline void g_f() { cout << "first" << endl; } void a() { cout << "a" << endl; g_f(); } extern void b(); int main() { a(); b(); return 0; } ``` second.cpp ```cpp #include <iostream> using namespace std; inline void g_f() { cout << "second" << endl; } void b() { cout << "b" << endl; g_f(); } ``` 我把 main 函数放到 first.cpp 里,也是一样。都是打印 second 。不过是在 VS2019 里。 然后我用 Ubuntu 下的 gcc 又试了一下,main 函数分别 first 里或 second 里,却是都打印的是 first 了。看来是一个随机的情况,也就是楼上说的“inline 现在的含义是允许一个函数 /变量在多个编译单元重复定义,在链接的时候会随机挑选一个符号链上。” |
20
codehz 2022-01-15 10:00:45 +08:00 via Android
@amiwrong123 注意括号 然后假设每次都一致,这是前提条件。。。
来看标准吧,c++20 标准 6.3 节 13 小节 There can be more than one definition of a ... -inline function or variable ([dcl.inline]), ... 这是允许 inline 函数或变量有多个定义(即使同一个 TU )再看需要满足的要求 (13.8) Each such definition shall consist of the same sequence of tokens, where the definition of a closure type is considered to consist of the sequence of tokens of the corresponding lambda-expression. 每个定义都需要有相同的 token 序列 (13.9) In each such definition, corresponding names, looked up according to [basic.lookup], shall refer to the same entity, after overload resolution ([over.match]) and after matching of partial template specialization ([temp.over]), except that a name can refer to ...... same entity in all definitions of D. 每个定义所引用的符号在重载解析,模板偏特化之后除了某些特殊情况外都必须解析到相同的实体上 最后 If these definitions do not satisfy these requirements, then the program is ill-formed; a diagnostic is required only if the entity is attached to a named module and a prior definition is reachable at the point where a later definition occurs 如果这些条件没有满足要求,那么程序是不合法的,而也只有特殊情况(同一个模块且在不一致定义要在同一个单元)才要求编译器给出诊断(报告错误) 所以一个不要求诊断的错误的代码(甚至不属于 UB )给出任何行为都是符合预期的(链接期随意选择是结果的一部分而不是原因,写程序不是自然科学,不要用实验来推测原因) |