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.
上一篇: “长”保证至少有32位?