我知道答案,但我们可以有一些有趣的分析。我们会学会玩得开心的!
我用gcc的4.1.2来做那些测试。
首先,这段代码是不标准的,因为内联函数在不同的翻译单元中会有不同的定义。我知道这个。但让我们分析一下发生了什么,并给出了答案,我将提出三个问题。我们将从中学到:)
我将保持文件简单(例如,没有#ifndef警卫)。
假设我有这些文件:
增量.h
inline int increment()
{
static int value = 0;
return ++value;
}递减.h
int decrement();decrement.cpp
inline int increment()
{
static int value = 0;
return --value; // Attention to this
}
int decrement()
{
return increment();
}main.cpp
#include <iostream>
#include "increment.h"
#include "decrement.h"
using namespace std;
int main()
{
cout << increment() << endl;
cout << increment() << endl;
cout << decrement() << endl;
}如果我用这个Makefile编译它们:
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如果我从Makefile中移除-O2标志:
CFLAGS=-I.产出如下:
1
2
3如果我还更改了main.o和obment.o的顺序(将其保留在没有-O2标志的情况下,就像刚才做的那样):
$(CC) -lstdc++ decrement.o main.o -o crazy结果是:
-1
-2
-3这是怎么回事?为什么-O2标志和对象文件链接的顺序会这样改变输出?
发布于 2013-12-05 19:57:31
这里有两个翻译单位: main.cpp和decrement.cpp
让我们替换增量.h和递减.h的#include指令,以查看main.cpp和decrement.cpp在预处理器传递后的样子(我不会替换其他包含):
decrement.cpp
inline int increment()
{
static int value = 0;
return --value; // Attention to this
}
int decrement()
{
return increment();
}main.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;
}对于decrement.cpp,它将类似于链接器的符号导出:
(inline) int increment::value = 0;
inline int increment(); // exported only if -O2 flag is NOT specified, with -O2 it is inlined
int decrement();在main.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::增量值= 0”由链接器定义,这些转换单元将变成:
decrement.cpp
int decrement()
{
return --increment::value;
}main.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标志,编译器不会内联这些函数,而是为它们创建一个主体(因此调试将更容易,因为调试器将有一个为该函数放置断点的唯一位置)。因此,将为“内联int增量()”创建两个主体。一个在decrement.cpp (递减.o)中,它将减少值,另一个在main.cpp (main.o)中,它将增加值。
在链接时,如果没有内联"int增量()“,则多个正文定义将导致链接器错误,因为函数的主体只应该定义一次。但是,由于它是内联的,所以链接器假设“内联int增量()”的所有体在所有翻译单元中都是相同的,因为标准规定所有内联函数必须在任何地方都有相同的主体,链接器只是选择其中的第一个。
如果您链接:
$(CC) -lstdc++ main.o decrement.o -o crazy第一个主体是main.o中的主体,它增加值。所以你得到了:
1
2
3但如果你链接到:
$(CC) -lstdc++ decrement.o main.o -o crazy链接器从减量中选择身体,这实际上降低了值。所以你得到了:
-1
-2
-3问:如果一个主体(main.o)将值初始化为0,而另一个主体将值初始化为,例如,value = 3,会发生什么情况?你认为如何?
https://stackoverflow.com/questions/20409413
复制相似问题