8 ответов:
если вы просто хотите передать
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()] = ''; // don't forget the terminating 0 // don't forget to free the string after finished using it delete[] writable;Edit: обратите внимание, что выше не исключение безопасности. Если что-нибудь между
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()] = ''; // don't forget the terminating 0 // get the char* using writable.get() // memory is automatically freed if the smart pointer goes // out of scopestd:: vector
это стандартный способ (не требует никаких внешних библиотек). Вы используете
std::vector, который полностью управляет памятью для вас.std::string str; std::vector<char> writable(str.begin(), str.end()); writable.push_back(''); // get the char* using &writable[0] or &*writable.begin()
дали сказать...
std::string x = "hello";получение 'char *' или 'const char*' из 'string'
как получить указатель символа, который действителен в то время как
xостается в области действия и не изменяется далееC++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...все вышеперечисленные указатели будут содержать то же значение - адрес первого символ в буфере. Даже пустая строка имеет "первый символ в буфере", потому что C++11 гарантирует, что всегда будет содержать дополнительный символ конца NUL/0 после явно назначенного содержимого строки (например,
std::string("thisthat", 9)будет иметь буфер холдинг"thisthat").учитывая любой из вышеперечисленных указателей:
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запись нуля в другом месте строки делает не изменить
string' ssize();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; ...whatever... }вы не должны называтьf(&x[0], x.size());, когдаx.empty()- просто используйтеf(x.data(), ...).- в противном случае, согласно
x.data()но:
- для некурящих
constxэто дает неconstchar*указатель; вы можно перезаписать содержимое строки
x.c_str()
- возвращает
const char*к ASCIIZ (Nul-terminated) представлению значения (т. е. ['h', 'e', 'l', 'l', 'o', '\0']).- хотя немногие, если какие-либо реализации решили сделать это, стандарт C++03 был сформулирован так, чтобы позволить реализации строки свободу создавать distinct Nul-terminated buffer на ходу, из потенциально НЕ НУЛЕВОГО завершенного буфера "exposed" by
x.data()и&x[0]x.size()+ 1 символы безопасны для чтения.- гарантированный сейф даже для пустых строк (['\0']).
последствия доступа к внешним правовые показатели
каким бы способом вы ни получили указатель, вы не должны обращаться к памяти дальше от указателя, чем гарантированные символы присутствуют в описаниях выше. Попытки сделать это есть неопределено поведение, С очень реальной вероятностью сбоев приложений и результатов мусора даже для чтения, а также оптовых данных, повреждения стека и/или уязвимостей безопасности для записи.
когда эти указатели становятся недействительными?
если вы называете некоторые
stringфункция-член, которая модифицируетstringили резервирует дополнительную емкость, любые значения указателя, возвращенные заранее любым из вышеуказанные методы недействительным. Вы можете использовать эти методы снова, чтобы получить другой указатель. (Правила такие же, как для итераторов вstrings).см. также как получить указатель символа действительным даже после
xпокидает область действия или изменяется далее ниже....так, что лучше использовать?
из C++11, использование
.c_str()для данных ASCIIZ, и.data()для "двоичных" данных (см. далее ниже).в C++03, используйте
.c_str()если не уверен, что.data()адекватно, и предпочитают.data()over&x[0]как это безопасно для пустых строк.......постарайтесь понять программу достаточно, чтобы использовать
data()при необходимости, или вы, вероятно, сделать другие ошибки...символ ASCII NUL '\0', гарантированный
.c_str()используется многими функциями в качестве сторожевого значения, обозначающего конец релевантных и безопасных для доступа данных. Это относится к обеим c++-только функции, такие как sayfstream::fstream(const char* filename, ...)и совместно-с-C функции, такие какstrchr()иprintf().учитывая C++03's
.c_str()гарантии о возвращенном буфере-это супер-набор.data()'s, вы всегда можете смело использовать.c_str(), но люди иногда этого не делают, потому что:
- используя
.data()сообщает другим программистам, читающим исходный код, что данные не являются ASCIIZ (скорее, вы используете строку для хранения блока данных (который иногда даже не является действительно текстовым)), или что вы передаете его другой функции, которая рассматривает его как блок "двоичных" данных. Это может быть важным моментом в обеспечении того, чтобы изменения кода других программистов продолжали правильно обрабатывать данные.- только c++03: есть небольшой шанс, что ваш
stringреализация должна будет сделать некоторое дополнительное выделение памяти и / или копирование данных, чтобы подготовить буфер с нулевым завершениемкак далее подсказка, если параметры функции требуют (
const)char*но не настаивайте на том, чтобыx.size()функция наверное нужен вход ASCIIZ, так что.c_str()это хороший выбор (функция должна знать, где текст заканчивается каким-то образом, поэтому, если это не отдельный параметр, он может быть только соглашением, таким как префикс длины или Страж или некоторая фиксированная ожидаемая длина).как получить указатель символа действительным даже после
xоставляет область или изменяется дальшевам потребуется скопировать содержание
stringxв новую область памяти за пределами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's 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 == "abcd" -> 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->Hel) char y[N + 1]; strncpy(y, x.c_str(), N); // copy at most N, zero-padding if shorter y[N] = ''; // 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's 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, а как do с ним? Случайный набор примеров...
- дайте коду "C" доступ к C++
stringтекст, как вprintf("x is '%s'", x.c_str());- копировать
x's текст в буфер, указанный вызывающей функцией (например,strncpy(callers_buffer, callers_buffer_size, x.c_str())), или энергозависимая память, используемая для ввода/вывода устройства (напримерfor (const char* p = x.c_str(); *p; ++p) *p_device = *p;)- добавить
x's текст в массив символов, уже содержащий некоторый текст 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's: вы не обязательно получите нулевую завершенную строку, и вы не сможете изменить размер строки. Вы особенно должны быть осторожны, чтобы не добавлять символы после конца строки, или вы получите переполнение буфера (и вероятный сбой).не было никакой гарантии, что все символы будут частью одного и того же смежного буфера до C++11, но на практике все известные реализации
std::stringвсе равно так получилось; см. указывает ли "&s[0] " на смежные символы в строке std::?.обратите внимание, что многие
stringфункции-члены будут перераспределять внутренний буфер и аннулировать любые указатели, которые вы могли бы сохранить. Лучше всего использовать их сразу, а затем выбросить.
C++17
C++17 (предстоящий стандарт) изменяет синопсис шаблона
basic_stringдобавление неконстантной перегрузкиdata():
charT* data() noexcept;возвращает: указатель p такой, что P + i == &оператор для каждого i в [0, size ()].
CharT const *С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();C++11
CharT const *отstd::basic_string<CharT>std::string str = { "..." }; str.c_str();
CharT *Сstd::basic_string<CharT>начиная с C++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. Использование прилежащей хранение C++11
std::string foo{"text"}; auto p = &*foo.begin();Pro
- и
- быстрый (только метод без копирования)
минусы
- финал
''не подлежит изменению / не обязательно является частью неконстантной памяти.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
- простой
- стековой памяти обработка
минусы
- статический
- требуется копирование строки
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
- требуется копирование строки
- максимальная ответственность / восприимчивость для ошибки
- комплекс
Я работаю с API с большим количеством функций get в качестве входных данных a
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не копируется) существующей строки.
просто посмотрите на это:
string str1("stackoverflow"); const char * str2 = str1.c_str();однако , обратите внимание, что это вернет
const char *.Дляchar *используйтеstrcpyскопировать его в другойcharмассив.
Comments