#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[]
это, у вас будет утечка памяти, но это не то, о чем вы спрашивали.
Разница заключается в сроке службы объекта. Помните о области видимости.