Как преобразовать std::string
в char*
или const char*
?
Если вы просто хотите передать 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
удалит память для вас при выходе из области действия:
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::string str;
std::vector<char> writable(str.begin(), str.end());
writable.push_back('\0');
// get the char* using &writable[0] or &*writable.begin()
Учитывая, что...
std::string x = "hello";
Как получить указатель на символ, который действителен, пока x
остается в области видимости и не изменяется далее
С++ 11 упрощает работу; следующие все предоставляют доступ к одному и тому же внутреннему буферу строки:
const char* p_c_str = x.c_str();
const char* p_data = x.data();
const char* p_x0 = &x[0];
char* p_x0_rw = &x[0]; // compiles iff x is not const...
Все перечисленные выше указатели будут содержать одно и то же значение - адрес первого символа в буфере. Даже пустая строка имеет "первый символ в буфере", потому что С++ 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()]
Только для указателя не const
из &x[0]
:
p_x0_rw[n] = c; // valid for n <= x.size() - 1
// i.e. don't overwrite the implementation maintained NUL
Запись NUL в другом месте строки не изменяет string
size()
; string
разрешено содержать любое количество NUL - им не задано специальное обращение std::string
(то же самое в С++ 03).
В С++ 03 все было значительно сложнее (ключевые отличия выделены):
x.data()
const char*
в внутренний буфер строки, который не требуется Стандарту для завершения с помощью NUL (т.е. может быть ['h', 'e', 'l', 'l', 'o']
, за которым следуют неинициализированные или мусорные значения, при случайном доступе к ним с поведением undefined).
x.size()
символы безопасны для чтения, т.е. x[0]
через x[x.size() - 1]
&x[0]
f(const char* p, size_t n) { if (n == 0) return; ...whatever... }
вы не должны вызывать f(&x[0], x.size());
, когда x.empty()
- просто используйте f(x.data(), ...)
.x.data()
, но:
const
x
это дает указатель не const
char*
; вы можете перезаписать строковое содержимое x.c_str()
const char*
в ASCIIZ (NUL-завершенное) представление значения (т.е. ['h', 'e', 'l', 'l', 'o', '\ 0']).x.data()
и &x[0]
x.size()
+ 1 символ безопасен для чтения.Каким бы способом вы не указали указатель, вы не должны получать доступ к памяти дальше от указателя, чем символы, гарантированные в приведенных выше описаниях. Попытки сделать это имеют поведение undefined с очень реальной вероятностью сбоев приложений и результатов мусора даже для чтения, а также оптовых данных, повреждения стека и/или уязвимостей безопасности для записи.
Если вы вызываете некоторую функцию-член string
, которая модифицирует string
или резервирует дополнительную емкость, любые значения указателя, которые были возвращены заранее любым из вышеперечисленных методов, недействительны. Вы можете снова использовать эти методы, чтобы получить другой указатель. (Правила те же, что и для итераторов, в string
s).
См. также Как получить указатель на символ, даже после того, как x
покидает область видимости или изменяется далее ниже.
Из С++ 11 используйте .c_str()
для данных ASCIIZ и .data()
для "двоичных" данных (поясняется ниже).
В С++ 03 используйте .c_str()
, если не определено, что .data()
является адекватным и предпочитает .data()
над &x[0]
, поскольку он безопасен для пустых строк....
... попытайтесь понять программу достаточно, чтобы использовать data()
, когда это необходимо, или вы, вероятно, допустите другие ошибки...
Символ ASCII NUL '\ 0', гарантированный .c_str()
, используется многими функциями в качестве контрольного значения, обозначающего конец релевантных и безопасных для доступа данных. Это относится как к С++ - только к функциям, например say fstream::fstream(const char* filename, ...)
, так и к функциям shared-with-C, таким как strchr()
и printf()
.
Учитывая, что С++ 03 .c_str()
гарантирует, что возвращенный буфер является супер-множеством .data()
, вы всегда можете безопасно использовать .c_str()
, но люди иногда не потому, что:
.data()
общается с другими программистами, читающими исходный код, что данные не являются ASCIIZ (скорее, вы используете строку для хранения блока данных (который иногда даже не является действительно текстовым)), или что вы передаете его другой функции, которая рассматривает ее как блок "двоичных" данных. Это может быть важной идеей в обеспечении того, чтобы изменения кода других программистов продолжали правильно обрабатывать данные.string
потребуется выполнить дополнительное выделение памяти и/или копирование данных, чтобы подготовить буфер с нулевым завершениемВ качестве еще одного намека, если параметры функции требуют (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
, но что вы можете с ним сделать? Случайное смазывание примеров...
string
, как в 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 и native-компилятор), могут передавать данные как ASCIIZ, чтобы избежать конфликтов.Используйте метод .c_str()
для const char *
.
Вы можете использовать &mystring[0]
, чтобы получить указатель char *
, но есть пара gotcha: вы не обязательно получите строку с нулевым завершением, и вы не сможете изменить размер строки. Вы особенно должны быть осторожны, чтобы не добавлять символы в конец строки, иначе вы получите переполнение буфера (и вероятный сбой).
Не было никакой гарантии, что все символы будут частью одного и того же непрерывного буфера до С++ 11, но на практике все известные реализации std::string
работали так или иначе; см. Указывает ли "& s [0]" на непрерывные символы в std::string?.
Обратите внимание, что многие функции-члены string
будут перераспределять внутренний буфер и аннулировать любые указатели, которые вы могли бы сохранить. Лучше всего использовать их немедленно, а затем отказаться.
С++ 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();
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()]
.
Существуют разборчивые возможные способы получения указателя неконтинентального символа.
std::string foo{"text"};
auto p = &*foo.begin();
Pro
против
'\0'
не должен изменяться/не обязательно являться частью неконстантной памяти.std::vector<CharT>
std::string foo{"text"};
std::vector<char> fcv(foo.data(), foo.data()+foo.size()+1u);
auto p = fcv.data();
Pro
против
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
против
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
против
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
Я работаю с 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
не копируется) существующей строки.
c_str()
используемый std
является сокращением для "C-string", а не "const string", а str()
всегда возвращает std::basic_string
, а не char*
(например, std::stringstream::str()
)
char* result = strcpy((char*)malloc(str.length()+1), str.c_str());
Просто ознакомьтесь с этим:
string str1("stackoverflow");
const char * str2 = str1.c_str();
Однако обратите внимание, что это вернет a const char *
. Для a char *
используйте strcpy
, чтобы скопировать его в другой массив char
.
Попробуйте это
std::string s(reinterpret_cast<const char *>(Data), Size);