Есть ли проблема с зависшим указателем в этом коде?

#c #pointers #c-str

#c #указатели #c-str

Вопрос:

 string str;    
char *a=str.c_str();
  

Этот код работает нормально для меня, но в любом другом месте я вижу этот код вместо

 string str;
char *a=new char[str.length()];
strcpy(a,str.c_str());
  

Интересно, какой из них правильный и почему?

Комментарии:

1. Что такое str ? Если str есть std::string , ни один из них не является правильным.

2. «У меня этот код работает нормально» — попробуйте включить предупреждения компилятора!

3. Большой вопрос в том, зачем кому-то писать подобный код (второй фрагмент)? Это даже если в new[] вызове используется правильное количество символов. Это похоже на то, что автор умоляет о том, чтобы где-то произошла утечка памяти.

4. @PaulMcKenzie: Вы часто сталкиваетесь с этим при работе с API, которые принимают char* в качестве входных данных, даже если они якобы не изменяют буфер.

5. Я научился использовать std::vector<char> для этого.

Ответ №1:

Предполагая, что тип str является std::string , ни один из кодов не является правильным.

 char *a=str.c_str();
  

недопустимо, потому что c_str() вернет const char* , а удаление const без приведения (обычно const_cast ) недопустимо.

 char *a=new char[str.length()];
strcpy(a,str.c_str());
  

недопустимо, потому что str.length() не учитывается завершающий нулевой символ при выделении для использования завершающего нулевого символа strcpy() .

В коде, опубликованном здесь, нет проблемы с зависшим указателем, потому что здесь никакие указатели не признаны недействительными.

Комментарии:

1. Обратите внимание, что std::string::length не пересекает строку — стандарт требует, чтобы это было O(1). Возможно, строка содержит 0 значений в своем буфере. strcpy остановилось бы на первом 0 в str . Следовательно, поведение не обязательно не определено, в зависимости от того, как str настроено.

Ответ №2:

Два сегмента кода выполняют разные действия.

Первый присваивает значение указателя str вашей новой строке c-tpye и неявно преобразует из const char* (возвращаемый тип c_str()) в char* , что неверно. Если бы вы изменили свою новую строку, вы столкнулись бы с ошибкой. Даже если c_str() возвращено char* , изменение новой строки также внесет изменения в str .

Второй, с другой стороны, создает новую строку типа c из исходной строки, копируя ее побайтно в новую память, выделенную для вашей новой строки. Хотя строка кода, которую вы написали, неверна, поскольку она не охватывает завершающий нулевой символ строки типа c . Чтобы это исправить, выделите для этого 1 дополнительный байт:

 char *a=new char[str.length() 1];
  

После копирования данных из первой строки в вашу новую, внесение в нее изменений не приведет к изменениям в оригинале str .

Комментарии:

1. Значит ли это, что c_str() просто возвращает адрес, где хранится массив символов для класса string (и, следовательно, он должен пометить его const, чтобы мы с ним не связывались)?

2. @Saqib Точно. Вы можете использовать этот указатель для смещения массива символов, используя арифметику указателя, но вы не должны рассматривать его как примитивный тип для присваивания. Далее читайте о семантике указателя, чтобы было ясно, что вы можете и не можете с этим делать.

Ответ №3:

Возможно.

Рассмотрим это.

 char const* get_string() {
    string str{"Hello"};    
    return str.c_str();
}
  

Эта функция возвращает указатель на внутреннее значение str , которое выходит за пределы области видимости при возврате функции. У вас есть висячий указатель. Неопределенное поведение. Следите за путешествующими во времени носовыми обезьянами.

Теперь рассмотрим это.

 char const* get_string() {
    string str{"Hello"};
    char const* a = new char[str.length() 1];
    strcpy(a, str.c_str());
    return a;
}
  

Эта функция возвращает действительный указатель на действительную строку в стиле C с нулевым завершением. Нет висящего указателя. Если вы забудете delete[] это, у вас будет утечка памяти, но это не то, о чем вы спрашивали.

Разница заключается в сроке службы объекта. Помните о области видимости.