首页 > 解决方案 > gcc 中的 -O2 和 -fPIC 选项

问题描述

为了性能优化,我想利用字符串的引用而不是它的值。根据编译选项,我得到不同的结果。这种行为对我来说有点不清楚,我不知道gcc导致这种差异的实际标志。

我的代码是

#include <string>
#include <iostream>

const std::string* test2(const std::string& in) {
   // Here I want to make use of the pointer &in
   // ...
   // it's returned only for demonstration purposes...
   return &in;
}

int main() {
   const std::string* t1 = test2("text");
   const std::string* t2 = test2("text");
   // only for demonstration, the cout is printed....
   std::cout<<"References are: "<<(t1==t2?"equivalent. ":"different. ")<<t1<<"\t"<<t2<<std::endl;
   return 0;
}

共有三个编译选项:

gcc main.cc -o main -lstdc++ -O0 -fPIC && ./main 
gcc main.cc -o main -lstdc++ -O2 -fno-PIC && ./main 
gcc main.cc -o main -lstdc++ -O2 -fPIC && ./main 

前两个产生等效的结果 ( References are: different.),因此指针不同,但第三个产生等效的指针 ( References are: equivalent.)。为什么会发生这种情况,我必须将哪个选项添加到选项-O2 -fPIC中以使指针再次变得不同?由于此代码嵌入到更大的框架中,因此我无法删除选项-O2-fPIC.

由于我使用 option-O2和获得了所需的结果-fPIC,但是如果两个标志一起使用会出现不同的行为,因此我不清楚这些标志的确切行为。

我尝试使用 gcc4.8 和 gcc8.3。

标签: c++gcccompiler-flags

解决方案


两者t1t2都是悬空指针,它们指向一个std::string已经被销毁的临时对象。临时std::string是在每次调用期间从字符串文字构造的,test2("text")并且一直存在到完整表达式(the ;)的结尾。

它们的确切值取决于编译器如何(重新)在特定优化级别使用堆栈空间。

我必须将哪个选项添加到选项-O2 -fPIC中以使指针再次变得不同?

该代码表现出未定义的行为,因为比较无效指针值是非法的。根本不要这样做。

如果我们忽略比较部分,那么我们最终会得到这个版本:

#include <string>
#include <iostream>

void test2(const std::string& in) {
   std::cout << "Address of in: " << (void*)&in << std::endl;
}

int main() {
   test2("text");
   test2("text");
}

现在这段代码从 UB 中解放出来了,它将打印相同的地址或不同的地址,这取决于编译器如何在函数调用之间重用堆栈空间。没有办法控制这一点,但这没问题,因为一开始就跟踪临时人员的地址是一个坏主意。

您可以尝试使用const char*作为输入参数,然后在 call 中不会创建临时参数test2("text")。但是在这里,两个实例是否"text"指向同一个位置是实现定义的。尽管 GCC 确实合并了相同的字符串文字,但至少在 GCC 中你应该观察你所追求的行为。


推荐阅读