Оператор Addressof возвращает недопустимый адрес во встроенной среде

При отладке ошибки segfault в перекрестной скомпилированной встроенной среде 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;

Приложение выходит с segfault на линии 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 для рабочего стола и buildroot gcc для встроенной системы.

Сама Memset не является проблемой здесь (как указано в другом размере результата). Следующий код также создает segfault для встроенной системы:

string st;
st = "teststring";
char* p = (char*)&st;
for (size_t i = 0; i != sizeof(st); ++i) {
        p[i] = 0;
}
-1
18 окт. '16 в 13:02
источник поделиться
3 ответа

std::memset требует, чтобы объект, который вы передаете ему, был тривиально-скопирован. std::string не является тривиально-скопируемой, поэтому это неопределенное поведение для вызова memset на нем.

Если вы хотите очистить содержимое string вы должны вызвать clear в экземпляре.

Ваш второй пример также является незаконным, поскольку вы нарушаете правила строгого сглаживания. std::string не является c-строкой. Вы не можете рассматривать адрес строкового объекта как первый символ в базовой c-строке. Насколько вам известно, размер строки сохраняется сначала в классе, и вы перезаписываете это с помощью мусора.

+4
18 окт. '16 в 13:06
источник

Вы пытаетесь выполнить незаконные операции.

Чтобы использовать std::memset для объекта, он должен быть агрегатом. std::string не является одним. Если вы хотите заполнить его нулями, используйте std::fill. Если вы просто хотите сделать его пустым, используйте clear().

Если вы действительно хотите получить доступ к строке в виде массива символов, вы можете сказать:

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

&st[0] дает адрес первого символа, а строка гарантирует непрерывную структуру памяти данных.

+1
18 окт. '16 в 13:10
источник

Чтобы добавить к приведенным ответам, если вы хотите, чтобы вы не создавали программы, которые делают такие вещи (например, call 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 тривиально копируется. Тип_трейты - ваш друг здесь.

0
18 окт. '16 в 13:23
источник

Посмотрите другие вопросы по метке или Задайте вопрос