Addressof operator returns invalid address in embedded environment

When debugging a segfault error in a cross compiled embedded linux environment, I isolated the problem to be a call of memset.

Further investigation showed, that there is something strange going on. I tried to run a test application on the target with the following code:

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;

The application exits with a segfault on the memset line. The output is:

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

The same code compiled and run on the desktop environment produces the following output:

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

Why does the identical code behave differently on the desktop and on the embedded system?

Both are Qt applications without using QT components. The compiler is GCC for the desktop and buildroot gcc for the embedded system.

Memset itself is not the problem here (as indicated by the different sizeof result). The following code also produces a segfault on the embedded system:

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

std::memset requires the the object you pass to it be trivially-copyable. std::string is not trivially-copyable so it is undefined behavior to call memset on it.

If you want to clear the contents of the string then you should call clear on the instance.

Your second example is also illegal as you are breaking the strict aliasing rules. a std::string is not a c-string. You cannot treat the address of the string object as the first character in the underlying c-string. For all you know the size of the string is stored first in the class and you are overwrite that with garbage.


You're trying to perform illegal operations.

To use std::memset on an object, it must be an aggregate. std::string isn't one. If you want to fill it with zeros, use std::fill . If you just want to make it empty, use clear() .

If you really, really want to access the string as a character array, you can say

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

&st[0] yields address of the first character and string guarantees contiguous memory layout of the data.


To add to the answers given, if you want to make sure you don't create programs that do things like this (like calling memset ) use type traits and 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");
}

Now the program will not compile, let alone run, due to the type given std::is_trivially_copyable failing the static_assert.

Compare to this:

#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");
}

This compiles with no errors, since foo is trivially copyable. The type_traits are your friend here.

链接地址: http://www.djcxy.com/p/40380.html

上一篇: “长”保证至少有32位?

下一篇: Addressof运算符在嵌入式环境中返回无效地址