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

780

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

  • 1
    Вместо: char * writable = new char [str.size () + 1]; Вы можете использовать char writeable [str.size () + 1]; Тогда вам не нужно беспокоиться об удалении доступных для записи или обработки исключений.
  • 7
    Вы не можете использовать str.size (), если размер не известен во время компиляции, также он может переполнить ваш стек, если значение фиксированного размера огромно.
Показать ещё 5 комментариев
Теги:
string
const
char

8 ответов

939
Лучший ответ

Если вы просто хотите передать 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::vector

Это стандартный способ (не требует внешней библиотеки). Вы используете 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()
  • 38
    Просто используйте char * result = strdup (str.c_str ());
  • 60
    можно, но strdup - это не стандартная функция ac или c ++, а из posix :)
Показать ещё 30 комментариев
181

Учитывая, что...

std::string x = "hello";

Получение ` char *` или `const char *` из строки `string`

Как получить указатель на символ, который действителен, пока 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]
      • для пустых строк вам гарантируется некоторый указатель, отличный от NULL, к которому можно безопасно добавить 0 (ура!), но вы не должны разыгрывать этот указатель.
  • &x[0]

    • для пустых строк это поведение undefined (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']).
    • хотя и мало, если какие-либо реализации решили сделать это, стандарт С++ 03 был сформулирован так, чтобы позволить реализации строки освободить отличный NUL-завершенный буфер на лету, от потенциально буфер, не содержащий NUL, "выставленный" x.data() и &x[0]
    • x.size() + 1 символ безопасен для чтения.
    • гарантированный сейф даже для пустых строк (['\ 0']).

Последствия доступа к внешним юридическим показателям

Каким бы способом вы не указали указатель, вы не должны получать доступ к памяти дальше от указателя, чем символы, гарантированные в приведенных выше описаниях. Попытки сделать это имеют поведение 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 (скорее, вы используете строку для хранения блока данных (который иногда даже не является действительно текстовым)), или что вы передаете его другой функции, которая рассматривает ее как блок "двоичных" данных. Это может быть важной идеей в обеспечении того, чтобы изменения кода других программистов продолжали правильно обрабатывать данные.
  • Только С++ 03: существует небольшая вероятность того, что вашей реализации 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, но что вы можете с ним сделать? Случайное смазывание примеров...

  • предоставить код доступа "C" к тексту С++ string, как в printf("x is '%s'", x.c_str());
  • copy 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, вы не хотите возвращать std::string, но хотите скопировать ваш string данные для вызывающего абонента)
    • будьте осторожны, чтобы не вернуть указатель, который может быть разыменован вызывающей стороной после локальной переменной string, на которую указатель указал, оставил область
    • некоторые проекты с общими объектами, скомпилированными/связанными для разных реализаций std::string (например, STLport и native-компилятор), могут передавать данные как ASCIIZ, чтобы избежать конфликтов.
  • 4
    Хороший. Другая причина хотеть char * (не const) состоит в том, чтобы работать с передачей MPI. Это выглядит лучше, если вам не нужно копировать туда-сюда. Я бы лично предложил char * const getter для string. Const указатель, но редактируемая строка. Хотя, возможно, это было связано с неявным преобразованием из const char * в string ...
30

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

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

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

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

  • 1
    Вы должны отметить, что data () возвращает const char * :), что вы имеете в виду, & str [0], который возвращает непрерывную, но не обязательную строку с нулевым символом в конце.
  • 1
    @litb, аааа! Это то, что я получаю за попытку быстрого ответа. Я использовал ваше решение в прошлом, не знаю, почему это не первое, что пришло в голову. Я отредактировал свой ответ.
Показать ещё 3 комментария
18

С++ 17

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

С++ 11

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

  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. Используйте непрерывное хранилище С++ 11

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

Pro

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

против

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

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

  • Простой
  • Управление памятью стека

против

  • Static
  • Требуется копирование строки

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

  • Требуется копирование строки
  • Максимальная ответственность/восприимчивость к ошибкам
  • Комплекс
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() всегда возвращает std::basic_string , а не char* (например, std::stringstream::str() )
7
char* result = strcpy((char*)malloc(str.length()+1), str.c_str());
  • 1
    выглядит причудливо, но очень трудно понять ... Простое - лучшая ИМО
  • 4
    strcpy (), malloc (), length () и c_str () являются основными функциями, и в этом нет ничего сложного. Просто выделяю память и копирую.
Показать ещё 3 комментария
7

Просто ознакомьтесь с этим:

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

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

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

Попробуйте это

std::string s(reinterpret_cast<const char *>(Data), Size);

Ещё вопросы

Сообщество Overcoder
Наверх
Меню