Как я могу преобразовать 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
остается в области и не изменяется дальше
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()]
Только для указателя nonconst
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 в другом месте строки не изменяет string`` size ()
; string
s разрешено содержать любое количество NUL - они не получают специальной обработки по std :: string
(то же самое в C ++ 03).
В C ++ 03 все было значительно сложнее (различия клавиш выделены ):
x.data ()
const char *
во внутренний буфер строки , который не требовался Стандартом для завершения с NUL (т.е. может быть ['h', 'e', 'l', 'l', 'o']
, за которыми следуют не инициализированные или мусорные значения, при этом случайные доступа к ним имеют неопределенное поведение ).x [0]
через x [x.size () - 1]
& x [0]
f (const char * p, size_t n) {if (n == 0) return ; ...what... }
Вы не должны вызывать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 символ безопасен для чтения.Каким бы способом вы ни получили указатель, вы не должны получать доступ к памяти дальше от указателя, чем символы, гарантированные в описаниях выше. Попытки сделать это имеют неопределенное поведение с очень реальной вероятностью сбоев приложений и результатов мусора даже для чтения, а также оптовых данных, повреждения стека и / или уязвимости безопасности для записей.
Если вы вызываете какую-либо функцию-член 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, ...)
и функции shared-with-C, такие какstrchr ()
иprintf ()
.
Учитывая, что гарантии C ++ 03 .c_str ()
относительно возвращаемого буфера являются супер-набором .data ()
, вы всегда можете безопасно использовать .c_str ()
, но люди иногда этого не делают, потому что: ,
.data(
сообщает другим программистам, читающим исходный код, что данные не ASCIIZ (скорее, ты и № 39;повторно использовать строку для хранения блока данных (что иногда 'даже действительно текстовый) или что ты 'повторно передать его другой функции, которая обрабатывает его как блок &"бинарный &" данные. Это может быть решающим фактором в обеспечении того, чтобы изменения кода других программистов продолжали правильно обрабатывать данные.строковой
реализации потребуется дополнительное распределение памяти и / или копирование данных, чтобы подготовить завершенный буфер NULКак еще один намек, если функция 's параметры требуют (Const
) char *
но не 'Я настаиваю на получении x.size(
, функция вероятно нуждается во входе ASCIIZ, так .c_str(
это хороший выбор (функция должна знать, где текст как-то заканчивается, так что если это 's не отдельный параметр, это может быть только соглашение, такое как префикс длины или дозор или некоторая фиксированная ожидаемая длина).
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'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 == "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'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 *
и как сделать копию текста независимой от оригинальной строки
, но что вы можете сделать с ней? Случайное смещение примеров...
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
, на которую указал этот указатель, покинул область действияИспользуйте метод .c_str ()
для const char *
.
Вы можете использовать & mystring [0]
, чтобы получить указатель char *
, но есть пара gotcha: вы не обязательно получите нулевую конечную строку, и вы не сможете изменить размер строки. Вы особенно должны быть осторожны, чтобы не добавлять символы за конец строки, иначе вы получите переполнение буфера (и вероятный сбой).
Не было никакой гарантии, что все символы будут частью одного и того же непрерывного буфера до C ++ 11, но на практике все известные реализации std :: string
все равно работали таким образом; см. Des «& s [0]» указывает на смежные символы в std :: string?.
Обратите внимание, что многие функции string
будут перераспределять внутренний буфер и аннулировать любые указатели, которые вы могли сохранить. Лучше всего использовать их немедленно, а затем выбросить.
C ++ 17 (предстоящий стандарт) изменяет краткий обзор шаблона basic_string
, добавляя не-конструктивную перегрузку data ()
:
charT * data () нет, кроме;
Возвращает: указатель p такой, что p + i == & operator [] (i) для каждого i в [0, size ()].
& Лт; hr >
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();
CharT const *
из std :: basic_string < CharT >
std::string str = { "..." };
str.c_str();
CharT *
из std :: basic_string < CharT >
Начиная с C ++ 11, стандарт говорит:
- Объекты, похожие на символы в объекте
basic_string
, должны храниться непрерывно. То есть для любого объектаbasic_string
sтождество
& (s.begin () + n) == & s.begin () + nдолжно содержать все значения
nтак что
0 < = n < s.size () `.
& Лт; hr >
const_reference оператор [] (size_type pos) const;
< br / >reference оператор [] (size_type pos);
Возвращает:
* (начало () + поз)
ifpos < size ()
, в противном случае ссылка на объект типаCharT
со значениемCharT ()
; ссылочное значение не должно быть изменено.
& Лт; hr >
const charT * c_str () const nob, кроме;
< br / >const charT * data () const none, кроме;
Возвращает: указатель 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
не копируется) существующей строки.
Просто посмотрите это:
string str1("stackoverflow");
const char * str2 = str1.c_str();
Однако обратите внимание, что это вернет const char *
. Для char *
используйте strcpy
, чтобы скопировать его в другой массив char
.
char* result = strcpy((char*)malloc(str.length()+1), str.c_str());