Как преобразовать std::string в const char* или char*
Как я могу преобразовать std::string
в char*
или const char*
?
4 ответ(ов)
Получение char *
или const char*
из std::string
Как получить указатель на символы, который действителен, пока x
находится в области видимости и не изменяется дальше
С C++11 все проще; следующие варианты дают доступ к одному и тому же внутреннему буферу строки:
const char* p_c_str = x.c_str();
const char* p_data = x.data();
char* p_writable_data = x.data(); // для неконстантного x с C++17
const char* p_x0 = &x[0];
char* p_x0_rw = &x[0]; // компилируется, если x не константный...
Все вышеуказанные указатели будут содержать одинаковое значение - адрес первого символа в буфере. Даже у пустой строки есть "первый символ в буфере", так как C++11 гарантирует, что всегда будет дополнительный символ НУЛЬ/0 после явно заданного содержимого строки (например, std::string("this\0that", 9)
будет иметь буфер, хранящий "this\0that\0"
).
Для любого из вышеуказанных указателей:
char c = p[n]; // действительно для n <= x.size()
// т.е. вы можете безопасно читать НУЛЬ по адресу p[x.size()]
Только для неконстантного указателя p_writable_data
и из &x[0]
:
p_writable_data[n] = c;
p_x0_rw[n] = c; // действительно для n <= x.size() - 1
// т.е. не затирайте НУЛЬ, поддерживаемый реализацией
Запись НУЛЯ в другое место строки не изменяет size()
строки; std::string
допускает наличие любого количества НУЛЕЙ - они не имеют специального обращения со стороны std::string
(то же самое в C++03).
В C++03 ситуация была значительно сложнее (ключевые различия выделены):
x.data()
- возвращает
const char*
на внутренний буфер строки, который не был обязательным по стандарту завершаться НУЛЕМ (т.е. может быть['h', 'e', 'l', 'l', 'o']
, за которым следуют неинициализированные или мусорные значения, с случайным доступом к ним, имеющим неопределенное поведение).x.size()
символов можно безопасно читать, т.е.x[0]
доx[x.size() - 1]
- для пустых строк вы гарантированно получаете некоторый ненулевой указатель, к которому можно безопасно добавить 0 (ура!), но вы не должны разыменовывать этот указатель.
- возвращает
&x[0]
- для пустых строк это имеет неопределенное поведение (21.3.4)
- например, дано
f(const char* p, size_t n) { if (n == 0) return; ...что угодно... }
вы не должны вызыватьf(&x[0], x.size());
, когдаx.empty()
- просто используйтеf(x.data(), ...)
.
- например, дано
- в остальном, как
x.data()
, но:- для неконстантного
x
это возвращает указательchar*
, который не является константным; вы можете перезаписывать содержимое строки
- для неконстантного
- для пустых строк это имеет неопределенное поведение (21.3.4)
x.c_str()
- возвращает
const char*
на ASCIIZ (NUL-терминированное) представление значения (т.е. ['h', 'e', 'l', 'l', 'o', '\0']). - хотя немногие, если вообще, реализации решали это делать, стандарт C++03 был сформулирован так, что позволял реализации строки иметь свободу создавать отдельный NUL-терминированный буфер на лету, исходя из потенциально не NUL-терминированного буфера, "выведенного" через
x.data()
и&x[0]
x.size()
+ 1 символов безопасно читать.- гарантировано безопасно даже для пустых строк (['\0']).
- возвращает
Последствия доступа за пределами законных индексов
Какой бы способ вы ни использовали для получения указателя, вы не должны обращаться к памяти дальше от указателя, чем символы, гарантированно присутствующие в описаниях выше. Попытки сделать это имеют неопределенное поведение, с очень реальной вероятностью сбоев приложения и мусорными результатами даже для чтения, а также возможным полным повреждением данных, стека и/или уязвимостями для записи.
Когда эти указатели становятся недействительными?
Если вы вызываете какую-либо функцию-члена string
, которая изменяет string
или резервирует дополнительную емкость, любые значения указателей, возвращенные ранее любым из вышеуказанных методов, становятся недействительными. Вы можете снова использовать эти методы для получения другого указателя. (Правила такие же, как для итераторов в string
).
Смотрите также Как получить указатель на символы, который будет действителен даже после выхода x
из области видимости или дальнейших модификаций ниже...
Итак, что лучше использовать?
С C++11 используйте .c_str()
для ASCIIZ данных, а .data()
для "двоичных" данных (подробности ниже).
В C++03 используйте .c_str()
, если только вы не уверены, что .data()
достаточно, и предпочитайте .data()
перед &x[0]
, так как это безопасно для пустых строк...
...постарайтесь понять программу достаточно, чтобы использовать data()
в подходящих ситуациях, иначе вы, вероятно, совершите другие ошибки...
Гарантированный символ НУЛЬ '\0', обеспечиваемый .c_str()
, используется многими функциями как значение-обозначение, определяющее конец относящихся и безопасных для доступа данных. Это относится как к функциям только C++, таким как fstream::fstream(const char* filename, ...)
, так и к функциям C, которым передается strchr()
, и printf()
.
Учитывая, что гарантии .c_str()
в C++03 по возвращаемому буферу являются супермножеством по сравнению с .data()
, вы всегда можете безопасно использовать .c_str()
, хотя иногда люди этого не делают, потому что:
- использование
.data()
сообщает другим программистам, читающим исходный код, что данные не ASCIIZ (вместо этого вы используете строку для хранения блока данных (который иногда даже не является текстовым)), или что вы передаете его другой функции, которая обрабатывает его как блок "двойных" данных. Это может быть важным моментом для обеспечения того, чтобы изменения кода других программистов продолжали обрабатывать данные должным образом. - только C++03: есть небольшая вероятность, что ваша реализация
string
потребуется выполнить дополнительные операции по выделению памяти и/или копированию данных, чтобы подготовить NUL-терминированный буфер.
Как дополнительный совет, если параметры функции требуют (const
) char*
, но не настаивают на получении x.size()
, функция вероятно нуждается в ASCIIZ-входных данных, поэтому .c_str()
является хорошим выбором (функции необходимо знать, где текст заканчивается каким-либо образом, так что если это не отдельный параметр, это может быть лишь соглашением вроде длины-префикса или значения-обозначения или некоторой фиксированной ожидаемой длины).
Как получить указатель на символ, действительный даже после выхода x
из области видимости или дальнейших модификаций
Вам нужно будет копировать содержимое строки x
в новую область памяти вне x
. Этот внешний буфер может находиться в многих местах, таких как другая строка или переменная символов, он может или не может иметь другую продолжительность жизни, чем x
, из-за нахождения в другой области видимости (например, пространство имен, глобальная, статическая, куча, общая память, память, смонтированная на файле).
Чтобы скопировать текст из std::string x
в независимый массив символов:
// ИСПОЛЬЗУЯ ДРУГУЮ СТРОКУ - АВТОМАТИЧЕСКИЙ УПРАВЛЕНИЕ ПАМЯТЬЮ, БЕЗОПАСНО ОТ ИСКЛЮЧЕНИЙ
std::string old_x = x;
// - old_x не будет затронута последующими модификациями x...
// - вы можете использовать `&old_x[0]`, чтобы получить записываемый char* на содержимое old_x
// - вы можете использовать resize(), чтобы уменьшить/увеличить строку
// - изменение размера невозможно из функции, переданной только адрес char*
std::string old_x = x.c_str(); // old_x завершится раньше, если x содержит НУЛЬ
// Копирует данные ASCIIZ, но может быть менее эффективно, так как нужно просканировать память, чтобы найти НУЛЬ, указывающий длину строки, прежде чем выделить эту величину для копирования, или более эффективно, если в конечном итоге выделяется/копируется значительно меньше контента.
// Пример, x == "ab\0cd" -> old_x == "ab".
// ИСПОЛЬЗУЯ ВЕКТОР СИМВОЛОВ - АВТО, БЕЗОПАСНО ОТ ИСКЛЮЧЕНИЙ, НАЗНАЧАЕТ ДВОЙНОЕ СОДЕРЖИМОЕ, ГАРАНТИРОВАННАЯ СМЕЩЕННОСТЬ НАМЕРА СОЗДАНО ДАЖЕ В C++03
std::vector<char> old_x(x.data(), x.data() + x.size()); // без НУЛЯ
std::vector<char> old_x(x.c_str(), x.c_str() + x.size() + 1); // с НУЛЕМ
// ИСПОЛЬЗУЯ СТЕК, ГДЕ МАКСИМАЛЬНЫЙ РАЗМЕР x ИЗВЕСТЕН КАК КОНСТАНТНОЕ "N" ВРЕМЕНИ КОМПИЛЯЦИИ
// (немного опасно, так как "известные" вещи иногда неправильны и часто становятся неправильными)
char y[N + 1];
strcpy(y, x.c_str());
// ИСПОЛЬЗУЯ СТЕК, ГДЕ НЕНАДЕЖНЫЙ X БУДЕТ УСЕЧЕН (например, Hello\0->Hel\0)
char y[N + 1];
strncpy(y, x.c_str(), N); // скопируйте максимум N, добавляя 0, если короче
y[N] = '\0'; // обязательно завершение на НУЛЕ
// ИСПОЛЬЗУЯ СТЕК, ЧТОБЫ ОБРАБОТАТЬ x НИ
Вы можете использовать следующий код для преобразования строки std::string
в массив char *
, поскольку str1.c_str()
возвращает указатель на константный массив символов. Если вам нужен изменяемый массив char
, вам нужно использовать функцию strcpy
, чтобы сделать копию строки в новый массив:
#include <iostream>
#include <cstring>
#include <string>
int main() {
std::string str1("stackoverflow");
const char * str2 = str1.c_str();
// Создаем массив char с размером на одну больше, чтобы разместить нулевой терминатор
char* charArray = new char[str1.size() + 1];
// Копируем содержимое str2 в charArray
strcpy(charArray, str2);
// Теперь вы можете использовать charArray как изменяемый массив
std::cout << charArray << std::endl;
// Не забудьте освободить выделенную память
delete[] charArray;
return 0;
}
Таким образом, вы создаете изменяемый массив символов на основе строки std::string
, и теперь вы можете вносить изменения в charArray
. Не забудьте освободить память, когда она больше не нужна, чтобы избежать утечек памяти.
В этом коде происходит следующее:
Функция
malloc
выделяет необходимое количество памяти для хранения строки.str.length() + 1
используется для учета символа окончания строки (нулевого байта). Таким образом, выделяется достаточно памяти для копирования строкиstr
.Затем
strcpy
копирует содержимое строки, на которую ссылаетсяstr.c_str()
, в только что выделенную область памяти.Наконец, указатель на только что скопированную строку сохраняется в переменной
result
.
Важно помнить, что в этом коде есть риск утечки памяти, если вы не освободите выделенную память, когда она больше не нужна. После использования переменной result
, следует вызвать free(result)
, чтобы освободить память.
Так что итоговый ответ будет следующим:
char* result = strcpy((char*)malloc(str.length()+1), str.c_str());
Это выделяет память для копирования строки и копирует ее, но не забудьте освободить память после использования!
Вопрос: Как преобразовать строку в char и const char в C++?
Ответ:
Допустим, у нас есть строка:
std::string str = "stack";
- Преобразование строки в char*
char* s_rw = &str[0];
В приведённом коде char*
(т.е. s_rw
) является как читаемым, так и записываемым указателем, указывающим на базовый адрес строки, которую нужно преобразовать в char*
. Обратите внимание, что такой способ работает, если строка была создана на основе std::string
, но если строка была создана в виде литерала, то это может привести к неопределённому поведению, так как строковые литералы обычно хранятся в области памяти, которая не подлежит изменению.
- Преобразование строки в const char*
const char* s_r = &str[0];
В этом случае const char*
(т.е. s_r
) является читаемым, но не записываемым указателем и также указывает на базовый адрес строки. Использование const char*
рекомендуется в случаях, когда необходимо убедиться, что строка не будет изменена через этот указатель.
Запомните, если реализуется работа с динамическими строками, например, с использованием char*
, лучше воспользоваться функциями стандартной библиотеки C, такими как strdup()
для создания копии строки, чтобы избежать проблем с памятью.
Как перебрать слова в строке?
Почему предпочтительнее использовать char[] вместо String для хранения паролей?
Как обрезать std::string?
`std::wstring` против `std::string`: когда использовать и в чем разница?
Как преобразовать символ в строку?