2015-04-23 5 views
-1

Я работаю над переносом некоторых в linux, и я обнаружил довольно интересную ошибку в коде, который, по-видимому, работает на окнах, но не на Linux. Класс с некоторыми членами строки инициализировался через memset(this), который, по-видимому, работает на окнах, но генерирует ошибку сегментации в linux в дескрипторе строк.Вы можете `memset()` над строкой в ​​окнах?

Да, я знаю, используя memset(), потому что это ужасная практика, и я ее исправляю.

SSCCE:

#include <iostream> 
#include <cstring> 

int main() 
{ 
    std::string tmp; 
    std::cout << "String instantiated" << std::endl; 
    memset(&tmp, 0, sizeof(tmp)); 
    std::cout << "String memset" << std::endl; 

    return 0; 
} 

Это прекрасно работает на окнах, но строка деструкторов на ошибку сегментации Linux.

Составители:

  • MSVC++ 2013 (Microsoft (R) C/C++ оптимизирующий компилятор версии 18.00.31101 для x64)
  • г ++ (Ubuntu 4.8.2-19ubuntu1) 4.8.2

Я понимаю, что это была (и есть) ужасная практика в любом случае, но как она когда-либо работала в первую очередь?

+0

Что на земле было намерение здесь? Может быть, осталось, когда 'tmp' был массивом' char'? –

+1

Ошибка, деталь реализации и ужас - почему вы спрашиваете (оставьте ее в покое)? –

+0

@ ДжозефМансфилд - Нет ** ПРОСМОТР ** идея. Кодовая база ~ 50KLOC без комментирования C++. Мое предположение состоит в том, что у класса первоначально были только члены типа C (где это использование memset является функциональным, если это ужасно), а член строки был добавлен позже, не глядя на конструктор, и он как-то работает в любом случае. –

ответ

4

Если вы хотите углубиться в детали реализации, MSVC и Clang (с LIBC++) использовать string с оптимизацией короткой строки, которая выглядит примерно так:

class string { 
    size_t length; 
    char* ptr; 
    char short_buf[N]; 
}; 

Так что, если это memset к 0, его деструктора будет считать, что длина равна нулю и, вероятно, ничего не сделает, и даже если она попытается выполнить delete[] ptr, она не будет разбита, потому что delete отлично работает с нулевыми указателями.

GCC, на противоположном, до самого последнего времени используется совсем другое string impirmentation, в котором используется копия на запись и подсчет ссылок. Таким образом, его внутренняя структура намного сложнее, и неудивительно, что она падает после memset.

+0

На самом деле это вопрос, о котором я спрашивал. В принципе, неопределенное поведение, подобное этому, представляет собой интересное окно в внутренности компилятора/stdlib. –

0

Вы фактически получаете segfault, когда программа выходит (при возврате 0), если вы проверяете с помощью отладчика, потому что программа пытается освободить std :: string tmp, который, по его мнению, находится в стеке, а затем сбой, потому что он не находит допустимую строку std :: в этом месте.

Edit:

Он может работать на одной ОС, а не иначе, потому что это не указано в стандарте C++, а также какой-либо конкретной реализации deallocating зЬй :: строка может отличаться, пока они соответствуют стандарт. Так получилось, что реализация, используемая вами в Linux, не справляется с этим, но это не нужно, потому что для этого не нужно использовать mem memset. Если вы хотите использовать memset для строки, используйте строку стиля C.

+0

Я знаю, почему я получаю sefgault на linux, почему это * не * segfault на windows. –

+1

@FakeName Повторить другого комментатора: это неопределенное поведение, все может случиться. Какую часть вы не понимаете? – deviantfan

+0

@deviantfan - Итак, что такое компилятор * на самом деле * делает в разделе «undefined»? Я знаю, что вы не должны (и не можете) * зависеть * от неопределенного поведения, но он делает что-то *, что это такое? –

2

Вы спросили:

как она когда-либо работать в первую очередь?

Рассмотрим реализацию, как показано ниже:

class string 
{ 
    public: 

     string() : size_(0), data_(nullptr) {} 

     string(char const* s) : size_(strlen(s)), data_(new char(size_+1)) 
     { 
     strcpy(data_, s); 
     } 

     ~string() 
     { 
     if (data_) 
     { 
      delete [] data_; 
     } 
     } 

    private: 
     size_t size_; 
     char* data_; 
}; 

Учитывая такое внедрение и если nullptr представлен 0 (который является наиболее распространенным представлением),

string s1; 
memset(&s1, 0, sizeof(s1)); // Has no impact on s1 

string s2("This is a test"); 
memset(&s2, 0, sizeof(s2)); // Makes s2 the same as a default 
          // constructed string with memory leak 
          // as a side effect. Still, it is 
          // not going to cause segementation 
          // fault. 
Смежные вопросы