Hoe converteer je een std::string naar const char* of char*

Hoe kan ik een std::stringconverteren naar een char*of een const char*?


Antwoord 1, autoriteit 100%

Als je alleen een std::stringwilt doorgeven naar een functie die const char*nodig heeft, kun je gebruiken

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

Als je een beschrijfbare kopie wilt krijgen, zoals char *, kun je dat als volgt doen:

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;

Bewerken: merk op dat het bovenstaande niet uitzonderingsveilig is. Als er iets tussen de new-aanroep en de delete-aanroep wordt veroorzaakt, lekt u geheugen, omdat niets automatisch deletevoor u aanroept. Er zijn twee directe manieren om dit op te lossen.

boost::scoped_array

boost::scoped_arrayzal het geheugen voor je verwijderen als je buiten bereik gaat:

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

Dit is de standaardmanier (vereist geen externe bibliotheek). U gebruikt std::vector, die volledig beheert de herinnering voor jou.

std::string str;
std::vector<char> writable(str.begin(), str.end());
writable.push_back('\0');
// get the char* using &writable[0] or &*writable.begin()

Antwoord 2, autoriteit 18%

Gegeven zeg…

std::string x = "hello";

Een `char *` of `const char*` halen uit een `string`

Hoe u een tekenaanwijzer krijgt die geldig is terwijl xbinnen het bereik blijft en niet verder wordt gewijzigd

C++11vereenvoudigt dingen; de volgende geven allemaal toegang tot dezelfde interne stringbuffer:

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

Alle bovenstaande aanwijzers hebben dezelfde waarde– het adres van het eerste teken in de buffer. Zelfs een lege string heeft een “eerste teken in de buffer”, omdat C++11 garandeert dat er altijd een extra NUL/0-terminatorkarakter behouden blijft na de expliciet toegewezen stringinhoud (bijv. std::string("this\0that", 9)heeft een buffer met "this\0that\0").

Gezien een van de bovenstaande aanwijzingen:

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

Alleen voor de niet-const-aanwijzer p_writable_dataen van &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

Het schrijven van een NUL elders in de string verandert nietde string‘s size(); string‘s mogen een willekeurig aantal NUL’s bevatten – ze krijgen geen speciale behandeling door std::string(hetzelfde in C++03).

In C++03waren de zaken aanzienlijk gecompliceerder (belangrijkste verschillen gemarkeerd):

  • x.data()

    • retourneert const char*naar de interne buffer van de string die door de standaard niet werd vereist om te eindigen met een NUL(dat wil zeggen ['h', 'e', 'l', 'l', 'o']gevolgd door niet-geïnitialiseerde of afvalwaarden, met onbedoelde toegangen tot ongedefinieerd gedrag) .
      • x.size()-tekens zijn veilig te lezen, dwz x[0]tot en met x[x.size() - 1]
      • voor lege tekenreeksen bent u verzekerd van een niet-NULL-aanwijzer waaraan veilig 0 kan worden toegevoegd (hoera!), maar u moet die aanwijzer niet verbannen.
  • &x[0]

    • voor lege strings heeft dit ongedefinieerd gedrag(21.3.4)
      • bijv. gegeven f(const char* p, size_t n) { if (n == 0) return; ...whatever... }je mag f(&x[0], x.size());wanneer x.empty()– gebruik gewoon f(x.data(), ...).
    • anders, volgens x.data()maar:
      • voor niet-constxlevert dit een niet-constchar*pointer op; je kunt tekenreeksinhoud overschrijven
  • x.c_str()

    • retourneert const char*naar een ASCIIZ (NUL-terminated) representatie van de waarde (dwz [‘h’, ‘e’, ​​’l’, ‘l’, ‘o’, ‘ \0’]).
    • hoewel weinig of geen implementaties ervoor kozen om dit te doen, werd de C++03-standaard geformuleerd om de string-implementatie de vrijheid te geven om een ​​onderscheiden NUL-getermineerde buffer te creërenon-the-fly, van de mogelijk niet-NUL afgesloten buffer “blootgesteld” door x.data()en &x[0]
    • x.size()+ 1 tekens zijn veilig om te lezen.
    • gegarandeerd veilig, zelfs voor lege strings ([‘\0’]).

Gevolgen van toegang tot externe juridische indexen

Op welke manier u een aanwijzer ook krijgt, u mag het geheugen niet verder van de aanwijzer benaderen dan de tekens die gegarandeerd aanwezig zijn in de bovenstaande beschrijvingen. Pogingen om dit te doen hebben ongedefinieerd gedrag, met een zeer reële kans op applicatiecrashes en rommelresultaten, zelfs voor leesbewerkingen, en bovendien groothandelsgegevens, stapelcorruptie en/of beveiligingskwetsbaarheden voor schrijfbewerkingen.

Wanneer worden die aanwijzingen ongeldig gemaakt?

Als u een string-lidfunctie aanroept die de stringwijzigt of verdere capaciteit reserveert, worden alle pointerwaarden die vooraf door een van de bovenstaande methoden worden geretourneerd, ongeldig gemaakt. U kunt deze methoden opnieuw gebruiken om nog een aanwijzer te krijgen. (De regels zijn hetzelfde als voor iterators in strings).

Zie ook Hoe u een tekenaanwijzer geldig kunt krijgen, zelfs nadat xhet bereik heeft verlaten of verder is gewijzigdhieronder….

Dus, wat is beterom te gebruiken?

Gebruik vanuit C++11 .c_str()voor ASCIIZ-gegevens en .data()voor “binaire” gegevens (hieronder verder uitgelegd).

Gebruik in C++03 .c_str()tenzij u zeker weet dat .data()voldoende is, en geef de voorkeur aan .data()over &x[0]omdat het veilig is voor lege strings….

…probeer het programma voldoende te begrijpen om waar nodig data()te gebruiken, anders maak je waarschijnlijk andere fouten…

Het ASCII NUL-teken ‘\0’ gegarandeerd door .c_str()wordt door veel functies gebruikt als een schildwachtwaarde die het einde van relevante en veilig toegankelijke gegevens aangeeft. Dit is van toepassing op zowel C++-only functies zoals zeg fstream::fstream(const char* filename, ...)en gedeelde-met-C-functies zoals strchr(), en printf().

Gezien de .c_str()‘s garanties van C++03 over de geretourneerde buffer een superset van .data()‘s zijn, kunt u altijd veilig gebruik .c_str(), maar mensen doen dat soms niet omdat:

  • het gebruik van .data()communiceert aan andere programmeurs die de broncode lezen dat de gegevens niet ASCIIZ zijn (u gebruikt de tekenreeks eerder om een ​​gegevensblok op te slaan (wat soms niet het geval is) zelfs echt tekstueel)), of dat je het doorgeeft aan een andere functie die het behandelt als een blok “binaire” gegevens. Dit kan een cruciaal inzicht zijn om ervoor te zorgen dat de codewijzigingen van andere programmeurs de gegevens correct blijven verwerken.
  • Alleen C++03: er is een kleine kans dat uw string-implementatie wat extra geheugentoewijzing en/of gegevenskopie moet doen om de NUL-beëindigde buffer voor te bereiden

Als verdere hint, als de parameters van een functie de (const) char*vereisen, maar niet aandringen op het verkrijgen van x.size(), heeft de functie waarschijnlijkeen ASCIIZ-invoer nodig, dus .c_str()is een goede keuze (de functie moet weten waar de tekst op de een of andere manier eindigt, dus als dat niet zo is een aparte parameter het kan alleen een conventie zijn zoals een lengte-prefix of schildwacht of een vaste verwachte lengte).

Hoe een karakteraanwijzer geldig te krijgen, zelfs nadat xhet bereik heeft verlaten of verder is gewijzigd

U moet kopieerde inhoud van de stringxnaar een nieuw geheugengebied buiten x. Deze externe buffer kan zich op veel plaatsen bevinden, zoals een andere stringof karakterarrayvariabele, deze kan al dan niet een andere levensduur hebben dan xomdat hij zich in een ander bereik bevindt ( bijv. naamruimte, globaal, statisch, heap, gedeeld geheugen, geheugen toegewezen bestand).

Om de tekst van std::string xnaar een onafhankelijke tekenreeks te kopiëren:

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

Andere redenen om een ​​char*of const char*te willen genereren op basis van een string

Dus hierboven heb je gezien hoe je een (const) char*kunt krijgen, en hoe je een kopie van de tekst kunt maken die onafhankelijk is van de originele string, maar wat kun je ermee doen? Een paar willekeurige voorbeelden…

  • geef “C” code toegang tot de C++ string‘s tekst, zoals in printf("x is '%s'", x.c_str());
  • kopieer de tekst van xnaar een buffer die is opgegeven door de aanroeper van uw functie (bijv. strncpy(callers_buffer, callers_buffer_size, x.c_str())), of vluchtig geheugen dat wordt gebruikt voor apparaat I/O (bijv. for (const char* p = x.c_str(); *p; ++p) *p_device = *p;)
  • voeg de tekst van xtoe aan een tekenreeks die al wat ASCIIZ-tekst bevat (bijv. strcat(other_buffer, x.c_str())) – pas op dat u niet overloopt de buffer (in veel situaties moet u mogelijk strncatgebruiken)
  • retourneer een const char*of char*van een functie (misschien om historische redenen – de klant gebruikt uw bestaande API – of voor C-compatibiliteit die u niet wilt retourneer een std::string, maar wil wel de gegevens van je stringergens kopiëren voor de beller)
    • zorg ervoor dat u geen pointer retourneert waarnaar de beller kan verwijzen nadat een lokale string-variabele waarnaar die pointer wees, het bereik heeft verlaten
    • sommige projecten met gedeelde objecten die zijn gecompileerd/gekoppeld voor verschillende std::string-implementaties (bijv. STLport en compiler-native) kunnen gegevens doorgeven als ASCIIZ om conflicten te voorkomen

Antwoord 3, autoriteit 3%

Gebruik de .c_str()methode voor const char *.

Je kunt &mystring[0]gebruiken om een ​​char *pointer te krijgen, maar er zijn een paar problemen: je krijgt niet per se een nul die eindigt tekenreeks, en u kunt de grootte van de tekenreeks niet wijzigen. Je moet vooral oppassen dat je geen tekens toevoegt voorbij het einde van de string, anders krijg je een bufferoverschrijding (en waarschijnlijke crash).

Er was geen garantie dat alle karakters deel zouden uitmaken van dezelfde aangrenzende buffer tot C++11, maar in de praktijk werkten alle bekende implementaties van std::stringtoch op die manier; zie Wijst &s[0] naar aangrenzende tekens in een std::string?.

Houd er rekening mee dat veel stringlidfuncties de interne buffer opnieuw zullen toewijzen en eventuele verwijzingen die u hebt opgeslagen ongeldig maken. Het is het beste om ze meteen te gebruiken en vervolgens weg te gooien.


Antwoord 4, autoriteit 2%

C++17

C++17(aanstaande standaard) verandert de synopsis van de sjabloon basic_stringen voegt een niet-constante overbelasting van data()toe:

charT* data() noexcept;

Retourneert: Een pointer p zodat p + i == &operator voor elke i in [0,size()].


CharT const *van std::basic_string<CharT>

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

CharT *van std::basic_string<CharT>

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

C++11

CharT const *van std::basic_string<CharT>

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

CharT *van std::basic_string<CharT>

Vanaf C++11 zegt de standaard:

  1. De char-achtige objecten in een basic_stringobject zullen aaneengesloten worden opgeslagen. Dat wil zeggen, voor elk basic_stringobject s, de identiteit &*(s.begin() + n) == &*s.begin() + ngeldt voor alle waarden van Nzodat 0 <= n < s.size().

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

    Retourneert: *(begin() + pos)als pos < size(), anders een verwijzing naar een object van het type CharTmet waarde CharT(); de waarde waarnaar wordt verwezen mag niet worden gewijzigd.


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

    Retourneert: Een pointer p zodanig dat p + i == &operator[](i)voor elke iin [0,size()].

Er zijn verschillende manieren om een ​​non-cont character pointer te krijgen.

1. Gebruik de aaneengesloten opslag van C++11

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

Pro

  • Eenvoudig en kort
  • Snel (alleen methode zonder kopie)

Nadelen

  • Definitieve '\0'mag niet worden gewijzigd / maakt niet noodzakelijk deel uit van het niet-const-geheugen.

2. Gebruik std::vector<CharT>

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

Pro

  • Eenvoudig
  • Automatische geheugenverwerking
  • Dynamisch

Nadelen

  • Tekenreekskopie vereist

3. Gebruik std::array<CharT, N>als Nde compileertijdconstante is (en klein genoeg)

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

Pro

  • Eenvoudig
  • Verwerking stapelgeheugen

Nadelen

  • Statisch
  • Tekenreekskopie vereist

4. Onbewerkte geheugentoewijzing met automatische opslagverwijdering

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

  • Kleine geheugenvoetafdruk
  • Automatisch verwijderen
  • Eenvoudig

Nadelen

  • Tekenreekskopie vereist
  • Statisch (dynamisch gebruik vereist veel meer code)
  • Minder kenmerken dan vector of array

5. Onbewerkte geheugentoewijzing met handmatige verwerking

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

  • Maximale ‘controle’

Min

  • Tekenreekskopie vereist
  • Maximale aansprakelijkheid / vatbaarheid voor fouten
  • Complex

Antwoord 5

Zie dit eens:

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

Houd er echter rekening mee dat dit een const char *zal opleveren.

Voor een char *gebruikt u strcpyom deze naar een andere char-array te kopiëren.


Antwoord 6

Ik werk met een API met veel functies die een char*als invoer krijgen.

Ik heb een kleine klas gemaakt om dit soort problemen het hoofd te bieden en ik heb de RAIIidioom.

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

En je kunt het gebruiken als:

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

Ik heb de klasse DeepStringgenoemd omdat deze een diepe en unieke kopie maakt (de DeepStringkan niet worden gekopieerd) van een bestaande tekenreeks.


Antwoord 7

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

Antwoord 8

laten we zeggen,
string str=”stapel”;

1)string converteren naar char*

 char* s_rw=&str[0]; 

De bovenstaande char*(d.w.z. s_rw) is leesbaar en beschrijfbaar en verwijst naar de basis
adres van de string die geconverteerd moet worden naar char*

2)Tekenreeks converteren naar const char*

  const char* s_r=&str[0];

De bovenstaande const char* (d.w.z. s_r) is leesbaar maar niet beschrijfbaar en verwijst naar de
basisadres van de string.


Antwoord 9

Probeer dit

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

LEAVE A REPLY

Please enter your comment!
Please enter your name here

15 + 17 =

Other episodes