地址操作符在嵌入式环境中返回无效地址

Addressof operator returns invalid address in embedded environment

本文关键字:地址 返回 无效 嵌入式 操作符 环境      更新时间:2023-10-16

在交叉编译的嵌入式linux环境中调试段错误时,我将问题隔离为memset调用。

进一步的调查表明,有些奇怪的事情正在发生。我尝试用以下代码在目标上运行一个测试应用程序:

string st;
st = "teststring";
std::cout << "address: " << &st << std::endl;
std::cout << "sizeof: " << sizeof(st) << std::endl;
std::cout << "value (before): " << st << std::endl;
memset(&st,0,sizeof(st));
std::cout << "value (after): " << st << std::endl;

应用程序退出,在memset行上出现段错误。输出为:

address: 0xbed7fb4c
sizeof: 4
value (before): teststring
Segmentation fault (core dumped)
Application finished with exit code 139.

在桌面环境中编译和运行的相同代码产生以下输出:

address: 0x7ffdc172f7a0
sizeof: 32
value (before): teststring
value (after):

为什么相同的代码在桌面和嵌入式系统上的表现不同?

都是不使用Qt组件的Qt应用程序。编译器是用于桌面的GCC和用于嵌入式系统的builroot GCC。

Memset本身不是这里的问题(正如不同的sizeof结果所表明的那样)。下面的代码也会在嵌入式系统上产生段错误:

string st;
st = "teststring";
char* p = (char*)&st;
for (size_t i = 0; i != sizeof(st); ++i) {
        p[i] = 0;
}

std::memset要求您传递给它的对象是可复制的。std::string是不可复制的,所以它是未定义的行为调用memset

如果您想清除string的内容,那么您应该在实例上调用clear

你的第二个例子也是非法的,因为你违反了严格的混叠规则。std::string不是c字串。不能将字符串对象的地址作为底层c-string中的第一个字符。众所周知,字符串的大小首先存储在类中,您将用垃圾覆盖它。

您正在尝试执行非法操作。

要在一个对象上使用std::memset,它必须是一个聚合。std::string不是其中之一。如果你想用0填充它,使用std::fill。如果你只想让它为空,使用clear()

如果你真的,真的想访问字符串作为字符数组,你可以说

char* p = &st[0];
for (size_t i = 0; i != st.size(); ++i) {
        p[i] = 0;
}

&st[0]给出第一个字符的地址,字符串保证数据的连续内存布局。

为了补充给出的答案,如果您想确保不创建这样的程序(例如调用memset),请使用类型特征和std::is_trivially_copyyable

#include <type_traits>
#include <string>
int main()
{
   static_assert(std::is_trivially_copyable<std::string>(), 
                 "Sorry, you're memset is not going to work");
}

现在程序将无法编译,更不用说运行了,因为给定的类型std::is_trivially_copyable无法通过static_assert。

与此比较:

#include <type_traits>
struct foo
{
    char x[10];
};
int main()
{
   static_assert(std::is_trivially_copyable<foo>(), 
                 "Sorry, you're memset is not going to work");
}

编译时没有错误,因为foo是可复制的。type_traits在这里是你的朋友。