Как преобразовать std::string в const char * или char *?
Как преобразовать std::string
в char*
или const char*
?
Ответы
Ответ 1
Если вы просто хотите передать std::string
функцию, которая нуждается в const char*
, вы можете использовать
std::string str;
const char * c = str.c_str();
Если вы хотите получить записываемую копию, например char *
, вы можете сделать это с помощью этого:
std::string str;
char * writable = new char[str.size() + 1];
std::copy(str.begin(), str.end(), writable);
writable[str.size()] = '\0'; // don't forget the terminating 0
// don't forget to free the string after finished using it
delete[] writable;
Изменить. Обратите внимание, что вышеописанное не является безопасным для исключений. Если что-то между вызовом new
и вызовом delete
вызывает утечку памяти, так как ничего не будет вызывать delete
для вас автоматически. Существует два способа решить эту проблему.
boost::scoped_array
boost::scoped_array
удалит память для вас при выходе из области действия:
std::string str;
boost::scoped_array<char> writable(new char[str.size() + 1]);
std::copy(str.begin(), str.end(), writable.get());
writable[str.size()] = '\0'; // don't forget the terminating 0
// get the char* using writable.get()
// memory is automatically freed if the smart pointer goes
// out of scope
std::vector
Это стандартный способ (не требует внешней библиотеки). Вы используете std::vector
, который полностью управляет памятью для вас.
std::string str;
std::vector<char> writable(str.begin(), str.end());
writable.push_back('\0');
// get the char* using &writable[0] or &*writable.begin()
Ответ 2
Дано сказать...
std::string x = "hello";
Получение 'char *' или 'const char *' из 'строки'
Как получить символьный указатель, который действителен, пока x
остается в области видимости и больше не изменяется
C++ 11 упрощает вещи; следующие все дают доступ к одному и тому же внутреннему строковому буферу:
const char* p_c_str = x.c_str();
const char* p_data = x.data();
char* p_writable_data = x.data(); // for non-const x from C++17
const char* p_x0 = &x[0];
char* p_x0_rw = &x[0]; // compiles iff x is not const...
Все вышеперечисленные указатели будут содержать одно и то же значение - адрес первого символа в буфере. Даже пустая строка имеет "первый символ в буфере", потому что C++ 11 гарантирует всегда сохранять дополнительный символ-терминатор NUL/0 после явно назначенного содержимого строки (например, std::string("this\0that", 9)
будет иметь буфер, содержащий "this\0that\0"
).
Учитывая любой из вышеперечисленных указателей:
char c = p[n]; // valid for n <= x.size()
// i.e. you can safely read the NUL at p[x.size()]
Только для non- const
указатель p_writable_data
и с &x[0]
:
p_writable_data[n] = c;
p_x0_rw[n] = c; // valid for n <= x.size() - 1
// i.e. don't overwrite the implementation maintained NUL
Запись NUL в другом месте строки не меняет size()
string
size()
; string
может содержать любое количество NUL - для них нет специальной обработки с помощью std::string
(то же самое в C++ 03).
В C++ 03 все было значительно сложнее (выделены ключевые различия):
-
x.data()
- возвращает
const char*
во строковый внутренний буфер, который не требовался стандартом для завершения с NUL (т.е. может быть ['h', 'e', 'l', 'l', 'o']
последующим неинициализированным или значения мусора, со случайным доступом к ним, имеющим неопределенное поведение). -
x.size()
безопасны для чтения, т.е. с x[0]
по x[x.size() - 1]
- для пустых строк вам гарантирован некоторый указатель non- NULL, к которому можно безопасно добавить 0 (ура!), но вы не должны разыменовывать этот указатель.
-
&x[0]
- для пустых строк это имеет неопределенное поведение (21.3.4)
- например, заданный
f(const char* p, size_t n) { if (n == 0) return;...whatever... }
f(const char* p, size_t n) { if (n == 0) return;...whatever... }
вы не должны вызывать f(&x[0], x.size());
когда x.empty()
- просто используйте f(x.data(),...)
.
- в противном случае, согласно
x.data()
но: - для non-
const
x
это дает указатель non- const
char*
; Вы можете перезаписать содержимое строки
-
x.c_str()
- возвращает
const char*
в представление значения ASCIIZ (завершенное NUL) (т.е. ['h', 'e', 'l', 'l', 'o', '\ 0']). - хотя немногие, если какие-либо реализации решили сделать это, стандарт C++ 03 был сформулирован так, чтобы позволить строковой реализации свободно создавать отдельный NUL-концевой буфер на лету из потенциально концевого NUL-буфера non-, "выставляемого"
x.data()
и &x[0]
-
x.size()
+ 1 символов безопасны для чтения. - гарантировано безопасно даже для пустых строк (['\ 0']).
Последствия доступа к внешним правовым индексам
Какой бы способ вы ни указали, вы не должны получать доступ к памяти дальше от указателя, чем символы, гарантированно присутствующие в описаниях выше. Попытки сделать это имеют неопределенное поведение, с очень реальной вероятностью сбоев приложения и результатов мусора даже для операций чтения, а также оптовых данных, повреждения стека и/или уязвимостей безопасности для записей.
Когда эти указатели становятся недействительными?
Если вы вызываете какую-либо string
функцию-член, которая изменяет string
или резервирует дополнительную емкость, любые значения указателя, возвращенные заранее любым из вышеуказанных методов, становятся недействительными. Вы можете использовать эти методы снова, чтобы получить другой указатель. (Правила те же, что и для итераторов в string
s).
См. Также Как получить символьный указатель действительным даже после того, как x
выходит из области видимости или изменяется далее ниже....
Итак, что лучше использовать?
Начиная с C++ 11, используйте .c_str()
для данных ASCIIZ и .data()
для "двоичных" данных (поясняется ниже).
В C++ 03 используйте .c_str()
если не уверены, что .data()
является адекватным, и предпочитайте .data()
&x[0]
поскольку это безопасно для пустых строк....
... попытайтесь понять программу настолько, чтобы при необходимости использовать data()
, иначе вы, вероятно, допустите другие ошибки...
Символ ASCII NUL '\ 0', гарантируемый .c_str()
, используется многими функциями в качестве значения часового, обозначающего конец соответствующих и безопасных для доступа данных. Это относится как к C++ -только функциям, таким как, скажем, fstream::fstream(const char* filename,...)
и к функциям с strchr()
как strchr()
и printf()
.
Учитывая, что C++ 03 .c_str()
гарантирует, что возвращаемый буфер является супер-набором .data()
, вы всегда можете безопасно использовать .c_str()
, но иногда люди этого не делают, потому что:
- Использование
.data()
сообщает другим программистам, читающим исходный код, что данные не являются ASCIIZ (скорее, вы используете строку для хранения блока данных (который иногда даже не является текстовым)), или что вы ' пересылая его в другую функцию, которая обрабатывает его как блок "двоичных" данных. Это может быть критически важным для обеспечения того, чтобы изменения кода других программистов продолжали правильно обрабатывать данные. - Только C++ 03: есть небольшая вероятность того, что вашей
string
реализации потребуется дополнительное выделение памяти и/или копирование данных, чтобы подготовить NUL-завершенный буфер
В качестве еще одной подсказки, если для параметров функции требуется (const
) char*
но не настаивают на получении x.size()
, функция, вероятно, нуждается в вводе ASCIIZ, поэтому .c_str()
- хороший выбор (функция требует знать, где текст каким-то образом заканчивается, поэтому, если он не является отдельным параметром, это может быть только соглашение, такое как префикс длины или часовой или некоторая фиксированная ожидаемая длина).
Как получить символьный указатель действительным даже после того, как x
выходит из области видимости или изменяется дальше
Вам нужно будет скопировать содержимое string
x
в новую область памяти вне x
. Этот внешний буфер может находиться во многих местах, таких как другая string
или переменная массива символов, он может иметь или не иметь время жизни, отличное от x
из-за того, что находится в другой области видимости (например, пространство имен, глобальное, статическое, куча, общая память, отображенная память файл).
Чтобы скопировать текст из std::string x
в независимый массив символов:
// USING ANOTHER STRING - AUTO MEMORY MANAGEMENT, EXCEPTION SAFE
std::string old_x = x;
// - old_x will not be affected by subsequent modifications to x...
// - you can use '&old_x[0]' to get a writable char* to old_x textual content
// - you can use resize() to reduce/expand the string
// - resizing isn't possible from within a function passed only the char* address
std::string old_x = x.c_str(); // old_x will terminate early if x embeds NUL
// Copies ASCIIZ data but could be less efficient as it needs to scan memory to
// find the NUL terminator indicating string length before allocating that amount
// of memory to copy into, or more efficient if it ends up allocating/copying a
// lot less content.
// Example, x == "ab\0cd" -> old_x == "ab".
// USING A VECTOR OF CHAR - AUTO, EXCEPTION SAFE, HINTS AT BINARY CONTENT, GUARANTEED CONTIGUOUS EVEN IN C++03
std::vector<char> old_x(x.data(), x.data() + x.size()); // without the NUL
std::vector<char> old_x(x.c_str(), x.c_str() + x.size() + 1); // with the NUL
// USING STACK WHERE MAXIMUM SIZE OF x IS KNOWN TO BE COMPILE-TIME CONSTANT "N"
// (a bit dangerous, as "known" things are sometimes wrong and often become wrong)
char y[N + 1];
strcpy(y, x.c_str());
// USING STACK WHERE UNEXPECTEDLY LONG x IS TRUNCATED (e.g. Hello\0->Hel\0)
char y[N + 1];
strncpy(y, x.c_str(), N); // copy at most N, zero-padding if shorter
y[N] = '\0'; // ensure NUL terminated
// USING THE STACK TO HANDLE x OF UNKNOWN (BUT SANE) LENGTH
char* y = alloca(x.size() + 1);
strcpy(y, x.c_str());
// USING THE STACK TO HANDLE x OF UNKNOWN LENGTH (NON-STANDARD GCC EXTENSION)
char y[x.size() + 1];
strcpy(y, x.c_str());
// USING new/delete HEAP MEMORY, MANUAL DEALLOC, NO INHERENT EXCEPTION SAFETY
char* y = new char[x.size() + 1];
strcpy(y, x.c_str());
// or as a one-liner: char* y = strcpy(new char[x.size() + 1], x.c_str());
// use y...
delete[] y; // make sure no break, return, throw or branching bypasses this
// USING new/delete HEAP MEMORY, SMART POINTER DEALLOCATION, EXCEPTION SAFE
// see boost shared_array usage in Johannes Schaub answer
// USING malloc/free HEAP MEMORY, MANUAL DEALLOC, NO INHERENT EXCEPTION SAFETY
char* y = strdup(x.c_str());
// use y...
free(y);
Другие причины, чтобы хотеть char*
или const char*
сгенерированный из string
Таким образом, выше вы видели, как получить (const
) char*
, и как сделать копию текста независимо от исходной string
, но то, что вы можете делать с ним? Случайная выборка примеров...
- дать коду "C" доступ к тексту
string
C++, как в printf("x is '%s'", x.c_str());
- скопируйте
x
текст в буфер, указанный вызывающей функцией (например, strncpy(callers_buffer, callers_buffer_size, x.c_str())
) или энергозависимую память, используемую для ввода-вывода устройства (например, for (const char* p = x.c_str(); *p; ++p) *p_device = *p;
) - добавить
x
текст к массиву символов, уже содержащему некоторый текст ASCIIZ (например, strcat(other_buffer, x.c_str())
) - будьте осторожны, чтобы не переполнить буфер (во многих ситуациях вам может понадобиться использовать strncat
) - вернуть
const char*
или char*
из функции (возможно, по историческим причинам - клиент использует ваш существующий API) или для совместимости с C вы не хотите возвращать std::string
, но хотите скопировать ваши string
данные куда-нибудь для звонящий) - будьте осторожны, чтобы не вернуть указатель, который может быть разыменован вызывающей стороной после локальной
string
переменной, на которую указатель указал, оставил область видимости - некоторые проекты с общими объектами, скомпилированными/связанными для разных реализаций
std::string
(например, STLport и compiler-native), могут передавать данные как ASCIIZ, чтобы избежать конфликтов
Ответ 3
Используйте метод .c_str()
для const char *
.
Вы можете использовать &mystring[0]
, чтобы получить указатель char *
, но есть пара gotcha: вы не обязательно получите строку с нулевым завершением, и вы не сможете изменить размер строки. Вы особенно должны быть осторожны, чтобы не добавлять символы в конец строки, иначе вы получите переполнение буфера (и вероятный сбой).
Не было никакой гарантии, что все символы будут частью одного и того же непрерывного буфера до С++ 11, но на практике все известные реализации std::string
работали так или иначе; см. Указывает ли "& s [0]" на непрерывные символы в std::string?.
Обратите внимание, что многие функции-члены string
будут перераспределять внутренний буфер и аннулировать любые указатели, которые вы могли бы сохранить. Лучше всего использовать их немедленно, а затем отказаться.
Ответ 4
С++ 17
С++ 17 (следующий стандарт) изменяет краткое описание шаблона basic_string
, добавляя неконстантную перегрузку data()
:
charT* data() noexcept;
Возвращает: указатель p такой, что p + я == & оператор для каждого я из [0, size()].
CharT const *
from std::basic_string<CharT>
std::string const cstr = { "..." };
char const * p = cstr.data(); // or .c_str()
CharT *
от std::basic_string<CharT>
std::string str = { "..." };
char * p = str.data();
С++ 11
CharT const *
от std::basic_string<CharT>
std::string str = { "..." };
str.c_str();
CharT *
от std::basic_string<CharT>
Начиная с С++ 11, стандарт говорит:
- Объекты char в объекте
basic_string
должны храниться смежно. То есть для любого объекта basic_string
s
идентификатор &*(s.begin() + n) == &*s.begin() + n
будет выполняться для всех значений n
таких, что 0 <= n < s.size()
.
-
const_reference operator[](size_type pos) const;
reference operator[](size_type pos);
Возвращает: *(begin() + pos)
если pos < size()
, в противном случае ссылка на объект типа CharT
со значением CharT()
; ссылочное значение не должно быть изменено.
-
const charT* c_str() const noexcept;
const charT* data() const noexcept;
Возвращает: указатель p такой, что p + i == &operator[](i)
для каждого i
в [0,size()]
.
Существуют разборчивые возможные способы получения указателя неконтинентального символа.
1. Используйте непрерывное хранилище С++ 11
std::string foo{"text"};
auto p = &*foo.begin();
Pro
- Простой и короткий
- Быстрый (только метод без копирования)
против
- Финал
'\0'
не должен изменяться/не обязательно являться частью неконстантной памяти.
2. Используйте std::vector<CharT>
std::string foo{"text"};
std::vector<char> fcv(foo.data(), foo.data()+foo.size()+1u);
auto p = fcv.data();
Pro
- Простой
- Автоматическая обработка памяти
- Динамический
против
- Требуется копирование строки
3. Используйте std::array<CharT, N>
, если n
является константой времени компиляции (и достаточно малой)
std::string foo{"text"};
std::array<char, 5u> fca;
std::copy(foo.data(), foo.data()+foo.size()+1u, fca.begin());
Pro
- Простой
- Управление памятью стека
против
- Static
- Требуется копирование строки
4. Распределение сырой памяти с автоматическим удалением хранилища
std::string foo{ "text" };
auto p = std::make_unique<char[]>(foo.size()+1u);
std::copy(foo.data(), foo.data() + foo.size() + 1u, &p[0]);
Pro
- Малая занимаемая площадь памяти
- Автоматическое удаление
- Простой
против
- Требуется копирование строки
- Статический (динамическое использование требует гораздо большего количества кода)
- Меньше функций, чем вектор или массив
5. Распределение сырой памяти с ручным управлением
std::string foo{ "text" };
char * p = nullptr;
try
{
p = new char[foo.size() + 1u];
std::copy(foo.data(), foo.data() + foo.size() + 1u, p);
// handle stuff with p
delete[] p;
}
catch (...)
{
if (p) { delete[] p; }
throw;
}
Pro
- Максимальное 'управление'
Con
- Требуется копирование строки
- Максимальная ответственность/восприимчивость к ошибкам
- Комплекс
Ответ 5
Я работаю с API с большим количеством функций, получая в качестве ввода char*
.
Я создал небольшой класс для решения этой проблемы, я применил идиом RAII.
class DeepString
{
DeepString(const DeepString& other);
DeepString& operator=(const DeepString& other);
char* internal_;
public:
explicit DeepString( const string& toCopy):
internal_(new char[toCopy.size()+1])
{
strcpy(internal_,toCopy.c_str());
}
~DeepString() { delete[] internal_; }
char* str() const { return internal_; }
const char* c_str() const { return internal_; }
};
И вы можете использовать его как:
void aFunctionAPI(char* input);
// other stuff
aFunctionAPI("Foo"); //this call is not safe. if the function modified the
//literal string the program will crash
std::string myFoo("Foo");
aFunctionAPI(myFoo.c_str()); //this is not compiling
aFunctionAPI(const_cast<char*>(myFoo.c_str())); //this is not safe std::string
//implement reference counting and
//it may change the value of other
//strings as well.
DeepString myDeepFoo(myFoo);
aFunctionAPI(myFoo.str()); //this is fine
Я назвал класс DeepString
, потому что он создает глубокую и уникальную копию (DeepString
не копируется) существующей строки.
Ответ 6
Просто ознакомьтесь с этим:
string str1("stackoverflow");
const char * str2 = str1.c_str();
Однако обратите внимание, что это вернет a const char *
. Для a char *
используйте strcpy
, чтобы скопировать его в другой массив char
.
Ответ 7
char* result = strcpy((char*)malloc(str.length()+1), str.c_str());
Ответ 8
Попробуйте это
std::string s(reinterpret_cast<const char *>(Data), Size);