Як перетворити std :: string в const char * або char *?


893

Як я можу конвертувати std::stringв a char*чи a const char*?


2
Замість: char * writable = new char [str.size () + 1]; Ви можете використовувати char wrtable [str.size () + 1]; Тоді вам не потрібно турбуватися про видалення файлів, що записуються, або обробку виключень.

7
Ви не можете використовувати str.size (), якщо розмір не відомий під час компіляції, також він може переповнити ваш стек, якщо значення фіксованого розміру величезне.
Паулм

1
char * результат = strcpy ((char *) malloc (str.length () + 1), str.c_str ());
cegprakash

7
@cegprakash strcpyі mallocнасправді не є способом C ++.
хлопчик

4
Ні, але це char* dest = new char[str.length() + 1]; std::copy(str.begin(), str.end(), dest)було б ідіоматичніше C ++. strcpy()і malloc()не є помилковим або проблематичним, але здається непослідовним використовувати рядок C ++ та бібліотеку C з C ++ еквівалентами в одному блоці коду.
хлопець

Відповіді:


1055

Якщо ви просто хочете передати 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 :: вектор

Це стандартний спосіб (не вимагає жодної зовнішньої бібліотеки). Ви використовуєте 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()

41
Просто використовуйте char * result = strdup (str.c_str ());
Джаспер Беккерс

63
Ви могли б, але strdup - це не ac або c ++ стандартна функція, це з posix :)
Йоханнес Шауб - litb

14
що я, мабуть, віддав перевагу, як правило, std :: vector <char> writable (str.begin (), str.end ()); writable.push_back ('\ 0'); char * c = & записаний [0];
Йоханнес Шауб - ліб

17
std :: copy - це спосіб c ++ зробити це без необхідності потрапляння на рядок вказівника. Я намагаюся уникати використання функцій C наскільки я можу.
Йоханнес Шауб - ліб

16
Станом на C ++ 17, std::string::data()тепер повертається CharT*замість а const CharT*. Можливо, було б корисно оновити цю відповідь :)
Rakete1111,

192

Дано сказати ...

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();
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()]

Тільки для не 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 в іншому рядку не змінює значення string's 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]
      • для порожніх рядків ви гарантуєте деякий не-NULL покажчик, до якого 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():
      • для не - const xце дає не const char*вказівник; ви можете перезаписати рядовий вміст
  • 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або резервує додаткову ємність, будь-які вказівні значення, повернені заздалегідь будь-яким з перерахованих вище способів, недійсні . Ви можете знову використовувати ці методи, щоб отримати інший покажчик. (Правила такі ж, як для ітераторів в strings).

Див. Також Як отримати покажчик символів дійсним навіть після виходу 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()і printf().

З огляду на .c_str()гарантії C ++ 03 щодо повернутого буфера - це супернабір .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'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*та як зробити копію тексту незалежною від оригіналу 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 та власник компілятора), можуть передавати дані як ASCIIZ, щоб уникнути конфліктів

4
Хороший. Ще одна причина, щоб хотіти char * (non const) - це працювати з широкомовними програмами MPI. Це виглядає приємніше, якщо вам не доведеться копіювати туди і назад. Я особисто запропонував би їй отримати * char * const getter. Покажчик Const, але рядок, який можна редагувати Хоча це, можливо, переплуталося з неявним перетворенням з const char * в string ...
bartgol

33

Використовуйте .c_str()метод для const char *.

Ви можете використовувати, &mystring[0]щоб отримати char *вказівник, але є декілька готчей: вам не обов'язково вийде нульовий завершений рядок, і ви не зможете змінити розмір рядка. Особливо потрібно бути обережним, щоб не додати символів до кінця рядка, інакше ви отримаєте перевищення буфера (і ймовірний збій).

Не було гарантії, що всі символи будуть частиною одного суміжного буфера до C ++ 11, але на практиці всі відомі реалізації std::stringтак чи інакше працювали; див. Чи вказує "& s [0]" на суміжні символи в рядку std ::? .

Зауважте, що багато stringфункцій-членів перерозподілять внутрішній буфер і недійсні будь-які покажчики, які ви могли зберегти. Найкраще використовувати їх негайно, а потім відкинути.


1
Ви повинні зауважити, що data () повертає const char * :), що ви маєте на увазі & str [0], який повертає суміжний, але не необхідний нульовий завершений рядок.
Йоханнес Шауб - ліб

1
@litb, Argh! Ось що я отримую за спробу швидкої відповіді. Я раніше використовував ваше рішення, не знаю, чому це було не перше, що прийшло в голову. Я відредагував свою відповідь.
Марк Викуп

2
Технічно зберігання std :: string буде суміжним лише в C ++ 0x.
MSalters

1
@MSalters, дякую - я цього не знав. Мені буде важко знайти реалізацію там, де цього не було.
Позначте викуп

2
char * результат = strcpy (malloc (str.length () + 1), str.c_str ());
cegprakash

21

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 далі стандарт говорить:

  1. Об'ємні об'єкти в basic_stringоб'єкті повинні зберігатися безперервно. Тобто, для будь-якого basic_stringоб'єкта sідентичність &*(s.begin() + n) == &*s.begin() + nмає містити всі nтакі значення 0 <= n < s.size().

  1. const_reference operator[](size_type pos) const;
    reference operator[](size_type pos);

    Повертає: *(begin() + pos)якщо pos < size(), інакше посилання на об'єкт типу CharTзі значенням CharT(); посилальне значення не може змінюватися.


  1. const charT* c_str() const noexcept;
    const charT* data() const noexcept;

    Повертає: вказівник p такий, що p + i == &operator[](i)для кожного iв [0,size()].

Існує декілька можливих способів отримати вказівник символів, які не мають const.

1. Використовуйте суміжне сховище C ++ 11

std::string foo{"text"};
auto p = &*foo.begin();

Про

  • Простий і короткий
  • Швидкий (лише метод без копії)

Мінуси

  • Фінал '\0'не повинен бути змінений / не обов'язково є частиною неконст-пам'яті.

2. Використовуйте std::vector<CharT>

std::string foo{"text"};
std::vector<char> fcv(foo.data(), foo.data()+foo.size()+1u);
auto p = fcv.data();

Про

  • Простий
  • Автоматична обробка пам'яті
  • Динамічний

Мінуси

  • Потрібна струнна копія

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());

Про

  • Простий
  • Обробка пам'яті стека

Мінуси

  • Статичний
  • Потрібна струнна копія

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]);

Про

  • Невеликий слід пам'яті
  • Автоматичне видалення
  • Простий

Мінуси

  • Потрібна струнна копія
  • Статичний (для динамічного використання потрібно набагато більше коду)
  • Менш можливостей, ніж вектор або масив

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;
}

Про

  • Максимальний "контроль"

Кон

  • Потрібна струнна копія
  • Максимальна відповідальність / сприйнятливість до помилок
  • Складні

9

Я працюю з 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не копіюється) існуючого рядка.


3
Я б уникнув цієї конвенції про іменування. c_str()як використовується, stdце абревіатура для "C-string" не "const string" і str()завжди повертає a std::basic_string, а не char*(наприклад std::stringstream::str())
bcrist

8
char* result = strcpy((char*)malloc(str.length()+1), str.c_str());

1
виглядає вигадливо, але насправді важко зрозуміти ... Простий - найкращий ІМО
Naeem A. Malik

4
strcpy (), malloc (), length () та c_str () - основні функції, і в цьому немає нічого складного. Просто виділення пам'яті та копіювання.
cegprakash

5
так, функції є основними, але ви скрутили і нахилили їх так, щоб виглядати як чаша спагетті або одне вкладиш монстра Франкенштейна :)
Naeem A. Malik

4
Так, функції є основними, але ... ви пам’ятали, коли почали займатися мовою програмування? Деякі рядки більше для пояснення, і це дійсно допоможе неофіту дізнатись, чому, наприклад, відрізняється чи краще, ніж ця відповідь :)
Hastur

2
@cegprakash: Кожен раз, коли є malloc (), також повинен бути вільний (). В іншому випадку код просочує пам'ять, і це також вирішує вашу відповідь. Розподіл пам’яті, хоча б не натякаючи на необхідне розселення, є поганою практикою для таких питань.
Стрізель

7

Просто дивіться це:

string str1("stackoverflow");
const char * str2 = str1.c_str();

Однак зауважте, що це поверне a const char *.

Для char *, strcpyскопіюйте його в інший charмасив.


23
Привіт, те, що ви опублікували, вже було сказано кілька разів, з більш детальною інформацією, в інших відповідях на 5-річне запитання. Добре відповідати на більш старі питання, але лише якщо ви додасте нову інформацію. Інакше це просто шум.
Мат

7
Особисто я ціную простоту.
TankorSmash

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.