Como converter um std :: string em const char * ou char *?


894

Como posso converter um std::stringpara um char*ou um const char*?


2
Em vez de: char * writable = new char [str.size () + 1]; Você pode usar char writable [str.size () + 1]; Então você não precisa se preocupar em excluir o tratamento gravável ou de exceção.

7
Você não pode usar str.size (), a menos que o tamanho seja conhecido no momento da compilação, também pode sobrecarregar sua pilha se o valor do tamanho fixo for enorme.
paulm

1
char * resultado = strcpy ((char *) malloc (str.length () + 1), str.c_str ());
Cegprakash

7
@cegprakash strcpye mallocnão são realmente a maneira C ++.
boycy 25/09/14

4
Não, mas char* dest = new char[str.length() + 1]; std::copy(str.begin(), str.end(), dest)seria C ++ mais idiomático. strcpy()e malloc()não estão errados ou problemáticos, mas parece inconsistente usar uma cadeia de caracteres C ++ e recursos da biblioteca C com equivalentes C ++ no mesmo bloco de código.
boycy

Respostas:


1057

Se você apenas deseja passar a std::stringpara uma função que precisa, const char*pode usar

std::string str;
const char * c = str.c_str();

Se você deseja obter uma cópia gravável char *, pode fazer isso com isso:

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;

Edit : Observe que o acima não é exceção segura. Se algo entre a newchamada e a deletechamada for lançada, você vazará memória, pois nada será chamado deleteautomaticamente. Existem duas maneiras imediatas de resolver isso.

boost :: scoped_array

boost::scoped_array excluirá a memória para você ao sair do escopo:

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

Essa é a maneira padrão (não requer nenhuma biblioteca externa). Você usa std::vector, que gerencia completamente a memória para você.

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
Basta usar char * result = strdup (str.c_str ());
Jasper Bekkers

63
você poderia, mas strdup não é ac ou c ++ função padrão, que é de posix :)
Johannes Schaub - litb

14
o que eu provavelmente preferiria geralmente é std :: vector <char> gravável (str.begin (), str.end ()); writable.push_back ('\ 0'); char * c = & gravável [0];
Johannes Schaub - litb 7/12/08

17
std :: copy é a maneira c ++ de fazer isso, sem a necessidade de obter o ponteiro da string. Eu tento evitar o uso de funções C, tanto quanto eu puder.
Johannes Schaub - litb

16
A partir do C ++ 17, std::string::data()agora retorna a em CharT*vez de a const CharT*. Pode ser uma boa idéia para atualizar esta resposta :)
Rakete1111

192

Dado dizer ...

std::string x = "hello";

Obtendo um `char *` ou `const char *` de uma `string`

Como obter um ponteiro de caractere válido enquanto xpermanece no escopo e não é mais modificado

C ++ 11 simplifica as coisas; todos os seguintes dão acesso ao mesmo buffer interno de cadeias de caracteres:

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

Todos os ponteiros acima conterão o mesmo valor - o endereço do primeiro caractere no buffer. Mesmo uma string vazia possui um "primeiro caractere no buffer", porque o C ++ 11 garante sempre manter um caractere terminador NUL / 0 extra após o conteúdo da string explicitamente designado (por exemplo, std::string("this\0that", 9)manterá o buffer "this\0that\0").

Dado qualquer um dos indicadores acima:

char c = p[n];   // valid for n <= x.size()
                 // i.e. you can safely read the NUL at p[x.size()]

Somente para o não constponteiro p_writable_datae de &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

Escrever um NUL em outro lugar da string não altera os string's size(); stringé permitido conter qualquer número de NULs - eles não recebem tratamento especial por std::string(o mesmo em C ++ 03).

No C ++ 03 , as coisas eram consideravelmente mais complicadas (principais diferenças destacadas ):

  • x.data()

    • retorna const char*ao buffer interno da string que não era exigido pelo Padrão para concluir com um NUL (ou seja, pode ser ['h', 'e', 'l', 'l', 'o']seguido por valores não inicializados ou de lixo, com acessos acidentais a ele com comportamento indefinido ).
      • x.size()caracteres são seguros de ler, ou seja, x[0]atravésx[x.size() - 1]
      • para cadeias vazias, você garante um ponteiro não NULL ao qual 0 pode ser adicionado com segurança (viva!), mas você não deve desreferenciar esse ponteiro.
  • &x[0]

    • para cadeias vazias, isso tem um comportamento indefinido (21.3.4)
      • por exemplo, dado que f(const char* p, size_t n) { if (n == 0) return; ...whatever... }você não deve ligar f(&x[0], x.size());quando x.empty()- apenas use f(x.data(), ...).
    • caso contrário, conforme por x.data()mas:
      • para não- const xisso produz um não- const char*ponteiro; você pode substituir o conteúdo da string
  • x.c_str()

    • retorna const char*para uma representação ASCIIZ (terminada em NUL) do valor (ou seja, ['h', 'e', ​​'l', 'l', 'o', '\ 0']).
    • embora poucas implementações tenham optado por fazê-lo, o C ++ 03 Standard foi redigido para permitir à implementação da cadeia a liberdade de criar um buffer distinto terminado por NUL em tempo real , a partir do buffer potencialmente não terminado por NUL "exposto" por x.data()e&x[0]
    • x.size() É seguro ler + 1 caracteres.
    • seguro garantido, mesmo para cadeias vazias (['\ 0']).

Consequências do acesso a índices legais externos

Qualquer que seja a maneira como você obtém um ponteiro, não deve acessar a memória mais além do ponteiro do que os caracteres garantidos presentes nas descrições acima. As tentativas de fazer isso têm um comportamento indefinido , com uma chance muito real de travamentos de aplicativos e resultados de lixo, mesmo para leituras e dados adicionais adicionais, empilham corrupção e / ou vulnerabilidades de segurança para gravações.

Quando esses ponteiros são invalidados?

Se você chamar alguma stringfunção de membro que modifique stringou reserve mais capacidade, quaisquer valores de ponteiro retornados anteriormente por qualquer um dos métodos acima serão invalidados . Você pode usar esses métodos novamente para obter outro ponteiro. (As regras são as mesmas que para os iteradores em strings).

Consulte também Como obter um ponteiro de caractere válido mesmo depois que xsai do escopo ou é modificado mais abaixo ....

Então, qual é o melhor para usar?

No C ++ 11, use .c_str()para dados ASCIIZ e .data()para dados "binários" (explicados mais abaixo).

No C ++ 03, use a .c_str()menos que .data()seja adequado e prefira .data()o &x[0]contrário, pois é seguro para cadeias de caracteres vazias ....

... tente entender o programa o suficiente para usar data()quando apropriado, ou você provavelmente cometerá outros erros ...

O caractere ASCII NUL '\ 0' garantido por .c_str()é usado por muitas funções como um valor sentinela que indica o fim dos dados relevantes e de acesso seguro. Isso se aplica às funções C ++ - somente funções como say fstream::fstream(const char* filename, ...)e funções compartilhadas com C como strchr()e printf().

Dadas as .c_str()garantias de C ++ 03 sobre o buffer retornado, são um superconjunto de .data()'s, você sempre pode usar com segurança .c_str(), mas às vezes as pessoas não o fazem porque:

  • usando .data()comunica-se com outros programadores lendo o código fonte de que os dados não são ASCIIZ (em vez disso, você está usando a string para armazenar um bloco de dados (que às vezes nem é realmente textual)) ou que está passando esses dados para outra função que a trata como um bloco de dados "binários". Isso pode ser um insight crucial para garantir que as alterações no código de outros programadores continuem a manipular os dados corretamente.
  • Somente C ++ 03: há uma pequena chance de que sua stringimplementação precise fazer uma alocação de memória extra e / ou cópia de dados para preparar o buffer terminado por NUL

Como uma dica adicional, se os parâmetros de uma função exigirem o ( const), char*mas não insistirem em obter x.size(), a função provavelmente precisará de uma entrada ASCIIZ, portanto, .c_str()é uma boa opção (a função precisa saber onde o texto termina de alguma forma, por isso, se não estiver) um parâmetro separado, ele pode ser apenas uma convenção, como um prefixo de comprimento ou sentinela ou algum comprimento esperado fixo).

Como obter um ponteiro de caractere válido mesmo depois que xsai do escopo ou é modificado ainda mais

Você precisará copiar o conteúdo da página string xpara uma nova área de memória externa x. Esse buffer externo pode estar em muitos locais, como outra stringvariável ou matriz de caracteres, pode ou não ter uma vida útil diferente da xdevida a estar em um escopo diferente (por exemplo, namespace, global, estático, heap, memória compartilhada, arquivo mapeado pela memória) .

Para copiar o texto de std::string xpara uma matriz de caracteres independente:

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

Outras razões para querer um char*ou const char*gerado a partir de umstring

Então, acima, você viu como obter um ( const) char*e como fazer uma cópia do texto independente do original string, mas o que você pode fazer com ele? Um punhado aleatório de exemplos ...

  • conceda acesso ao código "C" ao stringtexto do C ++ , como emprintf("x is '%s'", x.c_str());
  • copiar xo texto para um buffer especificado pelo responsável pela chamada da função (por exemplo strncpy(callers_buffer, callers_buffer_size, x.c_str())) ou memória volátil usada para E / S do dispositivo (por exemplo for (const char* p = x.c_str(); *p; ++p) *p_device = *p;)
  • anexar xo texto a uma matriz de caracteres que já contenha algum texto ASCIIZ (por exemplo strcat(other_buffer, x.c_str())) - tome cuidado para não exceder o buffer (em muitas situações, você pode precisar usar strncat)
  • retornar uma const char*ou char*de uma função (talvez por razões históricas - o cliente está usando sua API existente - ou para compatibilidade com C, você não deseja retornar a std::string, mas deseja copiar stringos dados de algum lugar para o chamador)
    • tenha cuidado para não retornar um ponteiro que possa ser desreferenciado pelo chamador depois que uma stringvariável local para a qual esse ponteiro apontado deixou o escopo
    • alguns projetos com objetos compartilhados compilados / vinculados para diferentes std::stringimplementações (por exemplo, STLport e nativo do compilador) podem passar dados como ASCIIZ para evitar conflitos

4
Agradável. Outro motivo para querer um caractere * (não const) é operar com transmissão MPI. Parece melhor se você não precisar copiar para frente e para trás. Eu pessoalmente teria oferecido um caractere de caractere para string. Ponteiro Const, mas sequência editável. Embora possa ter mexido com a conversão implícita de const char * para string ...
bartgol

33

Use o .c_str()método para const char *.

Você pode usar &mystring[0]para obter um char *ponteiro, mas há algumas dicas: você não obterá necessariamente uma string terminada com zero e não poderá alterar o tamanho da string. Você precisa tomar cuidado especial para não adicionar caracteres após o final da string ou obter uma saturação de buffer (e provável falha).

Não havia garantia de que todos os caracteres fizessem parte do mesmo buffer contíguo até o C ++ 11, mas na prática todas as implementações conhecidas std::stringfuncionavam dessa maneira; consulte “& s [0]” aponta para caracteres contíguos em uma std :: string? .

Observe que muitas stringfunções de membro realocam o buffer interno e invalidam os ponteiros que você pode ter salvo. Melhor usá-los imediatamente e depois descartar.


1
você deve observar que data () retorna const char * :) o que você quer dizer é & str [0], que retorna uma string terminada nula contígua, mas não necessária.
Johannes Schaub - litb 7/12/08

1
@litb, Argh! É o que recebo por tentar responder rapidamente. Eu usei sua solução no passado, não sei por que não foi a primeira coisa que me veio à mente. Eu editei minha resposta.
Mark Ransom

2
Tecnicamente, o armazenamento std :: string será contíguo apenas no C ++ 0x.
MSalters

1
@MSalters, obrigado - eu não sabia disso. Seria difícil encontrar uma implementação em que esse não fosse o caso.
Mark Ransom

2
char * resultado = strcpy (malloc (str.length () + 1), str.c_str ());
Cegprakash

21

C ++ 17

O C ++ 17 (próximo padrão) altera a sinopse do modelo, basic_stringadicionando uma sobrecarga não constante de data():

charT* data() noexcept;

Retorna: Um ponteiro p tal que p + i == & operador para cada i em [0, tamanho ()].


CharT const * de std::basic_string<CharT>

std::string const cstr = { "..." };
char const * p = cstr.data(); // or .c_str()

CharT * de std::basic_string<CharT>

std::string str = { "..." };
char * p = str.data();

C ++ 11

CharT const * de std::basic_string<CharT>

std::string str = { "..." };
str.c_str();

CharT * de std::basic_string<CharT>

A partir do C ++ 11, o padrão diz:

  1. Os objetos do tipo char em um basic_stringobjeto devem ser armazenados contiguamente. Ou seja, para qualquer basic_stringobjeto s, a identidade vale &*(s.begin() + n) == &*s.begin() + npara todos os valores de ntal modo 0 <= n < s.size().

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

    Retorna: *(begin() + pos)se pos < size(), caso contrário, uma referência a um objeto do tipo CharTcom valor CharT(); o valor referenciado não deve ser modificado.


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

    Retorna: um ponteiro p tal que p + i == &operator[](i)para cada ipol [0,size()].

Existem maneiras possíveis de separar um ponteiro de caracter não constante.

1. Use o armazenamento contíguo do C ++ 11

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

Pró

  • Simples e curto
  • Rápido (único método sem cópia envolvida)

Contras

  • Final '\0'não deve ser alterado / não necessariamente parte da memória não const.

2. Use std::vector<CharT>

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

Pró

  • Simples
  • Manuseio automático de memória
  • Dinâmico

Contras

  • Requer cópia em cadeia

3. Use std::array<CharT, N>se o Ntempo de compilação for constante (e pequeno o suficiente)

std::string foo{"text"};
std::array<char, 5u> fca;
std::copy(foo.data(), foo.data()+foo.size()+1u, fca.begin());

Pró

  • Simples
  • Manipulação de memória de pilha

Contras

  • Estático
  • Requer cópia em cadeia

4. Alocação de memória bruta com exclusão automática de armazenamento

std::string foo{ "text" };
auto p = std::make_unique<char[]>(foo.size()+1u);
std::copy(foo.data(), foo.data() + foo.size() + 1u, &p[0]);

Pró

  • Pequena presença de memória
  • Exclusão automática
  • Simples

Contras

  • Requer cópia em cadeia
  • Estático (o uso dinâmico requer muito mais código)
  • Menos recursos que vetor ou matriz

5. Alocação de memória bruta com manipulação manual

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

Pró

  • 'Controle' máximo

Vigarista

  • Requer cópia em cadeia
  • Responsabilidade máxima / suscetibilidade a erros
  • Complexo

9

Estou trabalhando com uma API com muitas funções, recebidas como entrada a char*.

Criei uma classe pequena para enfrentar esse tipo de problema, implementei o idioma 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_; }
};

E você pode usá-lo como:

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

Chamei a classe DeepStringporque ela está criando uma cópia profunda e exclusiva ( DeepStringnão é copiável) de uma string existente.


3
Eu evitaria essa convenção de nomenclatura. c_str()tal como utilizado por stduma abreviatura de "C-cadeia" "const string" não e str()sempre retorna um std::basic_string, não char*(por exemplo std::stringstream::str())
bcrist

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

1
parece chique, mas realmente difícil de entender ... Simples é o melhor IMO #
Naeem A. Malik

4
strcpy (), malloc (), length () e c_str () são funções básicas e não há nada difícil nisso. Apenas alocando memória e cópia.
Cegprakash

5
sim as funções são básicos mas você torcido e dobrado-los a olhar como tigela de espaguete ou um forro monstro de Frankenstein :)
Naeem A. Malik

4
Sim, as funções são básicas, mas ... você se lembra quando começou a lidar com uma linguagem de programação? Algumas linhas mais para explicar e ele realmente vai ajudar um neófito para saber por que, por exemplo, é diferente ou melhor do que esta resposta :)
Hastur

2
@egegakash: Sempre que houver um malloc (), também deverá haver um free (). Caso contrário, o código vazará memória e a solução em sua resposta também. Alocar memória sem ao menos sugerir a desalocação necessária é uma prática recomendada para essas perguntas.
Striezel 23/08/16

7

Basta ver isso:

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

No entanto, observe que isso retornará a const char *.

Para a char *, use strcpypara copiá-lo para outra charmatriz.


23
Olá, o que você postou já foi dito várias vezes, com mais detalhes, em outras respostas à pergunta de 5 anos. É bom responder a perguntas mais antigas, mas somente se você adicionar novas informações. Caso contrário, é apenas barulho.
Mat

7
Pessoalmente, aprecio a simplicidade.
precisa saber é o seguinte

-4

Tente isto

std::string s(reinterpret_cast<const char *>(Data), Size);
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.