首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么使用内联函数的程序根据链接顺序和参数有不同的行为?

为什么使用内联函数的程序根据链接顺序和参数有不同的行为?
EN

Stack Overflow用户
提问于 2013-12-05 19:57:31
回答 1查看 104关注 0票数 0

我知道答案,但我们可以有一些有趣的分析。我们会学会玩得开心的!

我用gcc的4.1.2来做那些测试。

首先,这段代码是不标准的,因为内联函数在不同的翻译单元中会有不同的定义。我知道这个。但让我们分析一下发生了什么,并给出了答案,我将提出三个问题。我们将从中学到:)

我将保持文件简单(例如,没有#ifndef警卫)。

假设我有这些文件:

增量.h

代码语言:javascript
复制
inline int increment()
{
    static int value = 0;
    return ++value;
}

递减.h

代码语言:javascript
复制
int decrement();

decrement.cpp

代码语言:javascript
复制
inline int increment()
{
    static int value = 0;
    return --value; // Attention to this
}

int decrement()
{
    return increment();
}

main.cpp

代码语言:javascript
复制
#include <iostream>
#include "increment.h"
#include "decrement.h"

using namespace std;

int main()
{
    cout << increment() << endl;
    cout << increment() << endl;
    cout << decrement() << endl;
}

如果我用这个Makefile编译它们:

代码语言:javascript
复制
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

产出如下:

代码语言:javascript
复制
1
2
1

如果我从Makefile中移除-O2标志:

代码语言:javascript
复制
  CFLAGS=-I.

产出如下:

代码语言:javascript
复制
1
2
3

如果我还更改了main.o和obment.o的顺序(将其保留在没有-O2标志的情况下,就像刚才做的那样):

代码语言:javascript
复制
$(CC) -lstdc++ decrement.o main.o -o crazy

结果是:

代码语言:javascript
复制
-1
-2
-3

这是怎么回事?为什么-O2标志和对象文件链接的顺序会这样改变输出?

EN

回答 1

Stack Overflow用户

发布于 2013-12-05 19:57:31

这里有两个翻译单位: main.cpp和decrement.cpp

让我们替换增量.h和递减.h的#include指令,以查看main.cpp和decrement.cpp在预处理器传递后的样子(我不会替换其他包含):

decrement.cpp

代码语言:javascript
复制
inline int increment()
{
    static int value = 0;
    return --value; // Attention to this
}

int decrement()
{
    return increment();
}

main.cpp

代码语言:javascript
复制
#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,它将类似于链接器的符号导出:

代码语言:javascript
复制
(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的例子中,它导出:

代码语言:javascript
复制
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

代码语言:javascript
复制
int decrement()
{
    return --increment::value;
}

main.cpp

代码语言:javascript
复制
#include <iostream>

int decrement();

using namespace std;

int main()
{
    cout << ++increment::value << endl;
    cout << ++increment::value << endl;
    cout << decrement() << endl;
}

所以我们得到这样的行为:

代码语言:javascript
复制
1
2
1

但是,如果我们移除-O2标志,编译器不会内联这些函数,而是为它们创建一个主体(因此调试将更容易,因为调试器将有一个为该函数放置断点的唯一位置)。因此,将为“内联int增量()”创建两个主体。一个在decrement.cpp (递减.o)中,它将减少值,另一个在main.cpp (main.o)中,它将增加值。

在链接时,如果没有内联"int增量()“,则多个正文定义将导致链接器错误,因为函数的主体只应该定义一次。但是,由于它是内联的,所以链接器假设“内联int增量()”的所有体在所有翻译单元中都是相同的,因为标准规定所有内联函数必须在任何地方都有相同的主体,链接器只是选择其中的第一个。

如果您链接:

代码语言:javascript
复制
$(CC) -lstdc++ main.o decrement.o -o crazy

第一个主体是main.o中的主体,它增加值。所以你得到了:

代码语言:javascript
复制
1
2
3

但如果你链接到:

代码语言:javascript
复制
    $(CC) -lstdc++ decrement.o main.o -o crazy

链接器从减量中选择身体,这实际上降低了值。所以你得到了:

代码语言:javascript
复制
-1
-2
-3

问:如果一个主体(main.o)将值初始化为0,而另一个主体将值初始化为,例如,value = 3,会发生什么情况?你认为如何?

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20409413

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档