Как преобразовать std :: string в const char * или char *?


895

Как я могу преобразовать std::stringв char*или const char*?


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

7
Вы не можете использовать str.size (), если размер не известен во время компиляции, также он может переполнить ваш стек, если значение фиксированного размера огромно.
paulm

1
char * result = strcpy ((char *) malloc (str.length () + 1), str.c_str ());
Цегпракаш

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 ++ в одном и том же блоке кода.
Бойси

Ответы:


1057

Если вы просто хотите передать 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вас автоматически. Есть два непосредственных способа решить это.

повышение :: 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::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*вместо a const CharT*. Это может быть хорошей идеей, чтобы обновить этот ответ :)
Rakete1111

192

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

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

Только для не 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]
      • для пустых строк вам гарантирован некоторый ненулевой указатель, к которому можно безопасно добавить 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 ++ - только функциям, таким как say, так fstream::fstream(const char* filename, ...)и к функциям shared-with-C, таким как 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)
  • вернуть a const char*или char*функцию (возможно, по историческим причинам - клиент использует ваш существующий API - или для совместимости с C вы не хотите возвращать a std::string, но действительно хотите скопировать свои stringданные куда-нибудь для вызывающей стороны)
    • будьте осторожны, чтобы не вернуть указатель, который может быть разыменован вызывающей стороной после того, как локальная stringпеременная, на которую указывает этот указатель, вышла из области видимости
    • некоторые проекты с общими объектами, скомпилированными / связанными для разных std::stringреализаций (например, STLport и compiler-native), могут передавать данные как ASCIIZ, чтобы избежать конфликтов

4
Хороший. Другая причина хотеть char * (не const) состоит в том, чтобы работать с широковещательной передачей MPI. Это выглядит лучше, если вам не нужно копировать туда-сюда. Я бы лично предложил get * const get для string. Const указатель, но редактируемая строка. Хотя, возможно, это не
совсем верно:

33

Используйте .c_str()метод для const char *.

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

Не было никакой гарантии, что все символы будут частью одного и того же непрерывного буфера до C ++ 11, но на практике все известные реализации std::stringработали так или иначе; см. «& s [0]» указывает на смежные символы в std :: string? ,

Обратите внимание, что многие stringфункции-члены перераспределяют внутренний буфер и делают недействительными любые указатели, которые вы могли сохранить. Лучше всего использовать их сразу, а затем отказаться.


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

1
@litb, аааа! Это то, что я получаю за попытку быстрого ответа. Я использовал ваше решение в прошлом, не знаю, почему это не первое, что пришло в голову. Я отредактировал свой ответ.
Марк Рэнсом

2
Технически, хранилище std :: string будет смежным только в C ++ 0x.
MSalters

1
@MSalters, спасибо - я этого не знал. Мне было бы трудно найти реализацию, где это было бы не так.
Марк Рэнсом

2
char * result = strcpy (malloc (str.length () + 1), str.c_str ());
Цегпракаш

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

Есть несколько возможных способов получить неконстантный символьный указатель.

1. Используйте непрерывное хранилище C ++ 11

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

профессионал

  • Простой и короткий
  • Быстро (только метод без копирования)

Cons

  • Финал '\0'не должен быть изменен / не обязательно является частью неконстантной памяти.

2. Используйте std::vector<CharT>

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

профессионал

  • просто
  • Автоматическая обработка памяти
  • динамический

Cons

  • Требуется копия строки

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

профессионал

  • просто
  • Обработка стековой памяти

Cons

  • статический
  • Требуется копия строки

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

профессионал

  • Маленький объем памяти
  • Автоматическое удаление
  • просто

Cons

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

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
выглядит причудливо, но очень трудно понять ... Простое - лучшая ИМО
Наим А. Малик

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

5
да, функции являются базовыми, но вы скрутили и согнули их, чтобы они выглядели как миска спагетти или монстр Франкенштейна с одним вкладышем :)
Наим А. Малик

4
Да, функции являются основными, но ... вы помните, когда начинали работать с языком программирования? Несколько строк, которые нужно объяснить, и это действительно поможет новичку понять, почему, например, он отличается от этого ответа или лучше :)
Hastur

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

7

Просто посмотрите на это:

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

Тем не менее, обратите внимание, что это вернет const char *.

Для char *, используйте, strcpyчтобы скопировать его в другой charмассив.


23
Привет, то, что ты написал, уже было сказано несколько раз, с более подробной информацией, в других ответах на 5-летний вопрос. Можно ответить на старые вопросы, но только если вы добавите новую информацию. В противном случае это просто шум.
Мат

7
Лично я ценю простоту.
TankorSmash

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.