问题描述
为了性能优化,我想使用字符串的引用而不是它的值。根据编译选项的不同,我得到了不同的结果。这种行为对我来说有点不清楚,我不知道导致这种差异的实际 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 ∈
}
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
。
由于我使用选项 -O2
和 -fPIC
获得了想要的结果,但是如果同时使用这两个标志会产生不同的行为,因此我不清楚这些标志的确切行为。>
我尝试过 gcc4.8 和 gcc8.3。
解决方法
t1
和 t2
都是悬空指针,它们指向一个已经被销毁的临时 std::string
。临时 std::string
是在每次调用 test2("text")
期间根据字符串文字构造的,并且一直存在到完整表达式(;
)的结尾。
它们的确切值取决于编译器如何(重新)使用特定优化级别的堆栈空间。
我必须向选项 -O2 -fPIC
添加哪个选项才能使指针再次不同?
代码显示 undefined behavior,因为它是 illegal to compare invalid pointer values。不要这样做。
如果我们忽略比较部分,那么我们最终会得到这个版本:
#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*
作为输入参数,然后在调用 test2("text")
时不会创建临时文件。但同样,"text"
的两个实例是否指向同一位置是 implementation-defined。尽管 GCC 确实合并了相同的字符串文字,因此至少在 GCC 中您应该观察您所追求的行为。