为什么使用内联函数的程序根据链接顺序和参数具有不同的行为
Why program using inline functions have different behaviors according to link order and parameters?
我知道这个问题的答案,但我们可以有一些有趣的分析。我们将学会玩得开心!
我已经使用 gcc 4.1.2 进行这些测试。
首先,此代码不是标准的,因为内联函数在不同的翻译单元中具有不同的定义。我知道那件事。但是,让我们分析一下正在发生的事情,并回答我将提出的三个问题。我们将从中学习:)
我会保持文件简单(例如没有 #ifndef 守卫(。
假设我有这些文件:
增量.h:
inline int increment()
{
static int value = 0;
return ++value;
}
递减.h:
int decrement();
递减.cpp:
inline int increment()
{
static int value = 0;
return --value; // Attention to this
}
int decrement()
{
return increment();
}
主.cpp:
#include <iostream>
#include "increment.h"
#include "decrement.h"
using namespace std;
int main()
{
cout << increment() << endl;
cout << increment() << endl;
cout << decrement() << endl;
}
如果我用这个制作文件编译它们:
CC=gcc
CFLAGS=-I. -O2
crazy: main.o decrement.o
$(CC) -lstdc++ main.o decrement.o -o crazy
main.o: main.cpp increment.h decrement.h
$(CC) $(CFLAGS) -c main.cpp -o main.o
decrement.o: decrement.cpp decrement.h
$(CC) $(CFLAGS) -c decrement.cpp -o decrement.o
clean:
rm -f *.o *.~ crazy
输出为:
1
2
1
如果我从生成文件中删除 -O2 标志:
CFLAGS=-I.
输出为:
1
2
3
如果我还更改了main.o和decrement.o的顺序(像我刚才所做的那样不带-O2标志(:
$(CC) -lstdc++ decrement.o main.o -o crazy
结果是:
-1
-2
-3
这是怎么回事?为什么 -O2 标志和目标文件链接的顺序会以这种方式更改输出?
这里有两个翻译单元:main.cpp和decment.cpp
让我们替换 increment.h 和 decrement.h 的 #include 指令,看看预处理器传递后 main.cpp 和 decrement.cpp 是什么样子的(我不会替换其他包含(:
递减.cpp:
inline int increment()
{
static int value = 0;
return --value; // Attention to this
}
int decrement()
{
return increment();
}
主.cpp:
#include <iostream>
inline int increment()
{
static int value = 0;
return ++value;
}
int decrement();
using namespace std;
int main()
{
cout << increment() << endl;
cout << increment() << endl;
cout << decrement() << endl;
}
在递减的情况下.cpp它会导出类似于链接器的符号:
(inline) int increment::value = 0;
inline int increment(); // exported only if -O2 flag is NOT specified, with -O2 it is inlined
int decrement();
在主.cpp的情况下,它导出:
int (inline) increment::value = 0;
inline int increment(); // exported only if -O2 flag is NOT specified, with -O2 it is inlined
int main();
如果增量实际上是内联的(-O2 标志集(,链接器会在某处定义"int::increment value = 0",这些转换单元将变为:
递减.cpp
int decrement()
{
return --increment::value;
}
主.cpp
#include <iostream>
int decrement();
using namespace std;
int main()
{
cout << ++increment::value << endl;
cout << ++increment::value << endl;
cout << decrement() << endl;
}
所以我们得到这个行为:
1
2
1
但是如果我们删除 -O2 标志,编译器不会内联函数,而是为它们创建一个主体(因此调试起来会更容易,因为调试器将有一个独特的位置来放置该函数的断点(。因此,将为"inline int increment(("创建两个主体。一个递减.cpp (decrement.o(,这将递减值,另一个在 main.cpp (main.o( 中,这将递增值。
在链接时,如果"int increment(("不是内联的,则多个主体定义将导致链接器错误,因为函数的主体只应定义一次。但是由于它是内联的,链接器假设"inline int increment(("的所有主体在所有翻译单元中都是相同的,因为标准规定所有内联函数必须在任何地方都具有相同的主体,链接器只是选择其中的第一个。
如果您链接:
$(CC) -lstdc++ main.o decrement.o -o crazy
第一个主体是 main.o 中的主体,它增加值。所以你得到:
1
2
3
但是,如果您链接:
$(CC) -lstdc++ decrement.o main.o -o crazy
链接器从 decrement.o 中选取您的主体,这实际上递减了值。所以你得到:
-1
-2
-3
问题:如果一个主体 (main.o( 将值初始化为 0,而另一个主体 (decrement.o( 将其初始化为例如值 = 3,会发生什么情况?你觉得怎么样?
- 函数调用中参数的顺序重要吗
- C++部分概念 id:显式模板规范顺序/第一个参数的特殊状态的原因是什么?
- lambda 作为接受其他参数的参数的初始化顺序
- 如何实现对参数顺序不可知的std::same_as的广义形式(即对于两个以上的类型参数)
- 约束模板参数顺序的更简单方法
- 正确对齐内存模板,参数顺序不变
- 具有不同参数顺序的聚合初始化
- glDrawArrays的意外行为-参数顺序错误会产生所需的图像
- 函数的参数顺序
- 推导的模板参数顺序
- 对"pthread_key_create"的未定义引用;参数顺序不能解决问题
- 参数顺序评估
- Printf参数顺序被忽略
- 通过交换参数顺序来重载函数
- 可变模板参数顺序,必须总是最右边的参数
- 如何反转可变参数模板函数的参数顺序
- Lua元事件参数顺序
- 'stringstream'参数顺序问题
- 宏参数顺序很重要
- sequence before:不同函数的参数顺序