Как преобразовать std :: string в const char * или char *? - PullRequest
838 голосов
/ 07 декабря 2008

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

Ответы [ 8 ]

994 голосов
/ 07 декабря 2008

Если вы просто хотите передать 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()
184 голосов
/ 11 ноября 2010

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

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

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

Какой бы способ вы ни указали, вы не должны обращаться к памяти дальше от указателя, чем символы, гарантированно присутствующие в описаниях выше. Попытки сделать это имеют неопределенное поведение , с очень реальной вероятностью сбоев приложений и результатов мусора даже для операций чтения, а также дополнительных оптовых данных, повреждения стека и / или уязвимостей безопасности для записей.

Когда эти указатели становятся недействительными?

Если вы вызываете какую-то string функцию-член, которая изменяет string или резервирует дополнительную емкость, любые значения указателя, возвращенные заранее любым из вышеперечисленных методов, аннулируются . Вы можете использовать эти методы снова, чтобы получить другой указатель. (Правила те же, что и для итераторов в string с).

См. Также Как сделать указатель на символ действительным даже после того, как 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" доступ к тексту C ++ string, как в 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 и compiler-native), могут передавать данные как ASCIIZ, чтобы избежать конфликтов
33 голосов
/ 07 декабря 2008

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

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

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

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

20 голосов
/ 12 января 2016

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

Pro

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

Против

  • Final '\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

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

Против

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

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

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

Против

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

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

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

Против

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

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 голосов
/ 29 марта 2011

Я работаю с 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 не копируется) существующей строки.

7 голосов
/ 12 июля 2014
char* result = strcpy((char*)malloc(str.length()+1), str.c_str());
7 голосов
/ 12 мая 2013

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

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

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

0 голосов
/ 17 февраля 2017

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

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