Πώς μπορώ να μετατρέψω ένα std::string
σε ένα char*
ή ένα const char*
;
Αν θέλετε απλά να περάσετε ένα 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
θα διαγράψει τη μνήμη για εσάς όταν βγείτε εκτός εμβέλειας:
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()
Δεδομένου ότι λένε...
std::string x = "hello";
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...
Όλοι οι παραπάνω δείκτες θα κρατούν την ίδια τιμή - τη διεύθυνση του πρώτου χαρακτήρα στο buffer. Ακόμα και ένα κενό αλφαριθμητικό έχει έναν "πρώτο χαρακτήρα στο buffer", επειδή η C++11 εγγυάται να κρατάει πάντα έναν επιπλέον χαρακτήρα τερματισμού NUL/0 μετά το ρητά εκχωρημένο περιεχόμενο του αλφαριθμητικού (π.χ. std::string("this\0that", 9)
θα έχει ένα buffer που θα κρατάει τον "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 σε άλλο σημείο του αλφαριθμητικού δεν αλλάζει το size()
του string
's. Τα string
's επιτρέπεται να περιέχουν οποιοδήποτε αριθμό NULs - δεν τους δίνεται ειδική μεταχείριση από το std::string
(το ίδιο και στην C++03).
Στην C++03, τα πράγματα ήταν αρκετά πιο περίπλοκα (βασικές διαφορές υπογραμμισμένες):
x.data()
const char*
στον εσωτερικό buffer της συμβολοσειράς ο οποίος δεν απαιτείτο από το Πρότυπο να καταλήγει σε NUL (δηλαδή θα μπορούσε να είναι ['h', 'e', 'l', 'l', 'o']
ακολουθούμενος από μη αρχικοποιημένες ή σκουπιδο-τιμές, με τυχαίες προσπελάσεις σε αυτές να έχουν απροσδιόριστη συμπεριφορά).x.size()
είναι ασφαλείς για ανάγνωση, δηλαδή x[0]
έως x[x.size() - 1]
.&x[0]
f(const char* p, size_t n) { if (n == 0) return; ...οτιδήποτε... }
δεν πρέπει να καλέσετε f(&x[0], x.size());
όταν x.empty()
- απλά χρησιμοποιήστε f(x.data(), ...)
.x.data()
αλλά:x.c_str()
const char*
σε μια αναπαράσταση ASCIIZ (με τερματισμό NUL) της τιμής (π.χ. ['h', 'e', 'l', 'l', 'o', '\0']).x.data()
και &x[0]
.x.size()
+ 1 χαρακτήρες είναι ασφαλές να διαβαστούν.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, ...)
όσο και για συναρτήσεις που μοιράζονται με τη C όπως η strchr()
και η printf()
.
Δεδομένου ότι οι εγγυήσεις της C++03's .c_str()
's για τον επιστρεφόμενο buffer είναι ένα υπερσύνολο των εγγυήσεων της .data()
's, μπορείτε πάντα να χρησιμοποιείτε με ασφάλεια την .c_str()
, αλλά οι άνθρωποι μερικές φορές δεν το κάνουν γιατί:
.data()
επικοινωνεί σε άλλους προγραμματιστές που διαβάζουν τον πηγαίο κώδικα ότι τα δεδομένα δεν είναι ASCIIZ (μάλλον, χρησιμοποιείτε τη συμβολοσειρά για να αποθηκεύσετε ένα μπλοκ δεδομένων (το οποίο μερικές φορές δεν είναι καν πραγματικό κείμενο)), ή ότι το περνάτε σε μια άλλη συνάρτηση που το αντιμετωπίζει ως ένα μπλοκ "δυαδικών" δεδομένων. Αυτό μπορεί να είναι μια κρίσιμη διορατικότητα για να διασφαλίσετε ότι οι αλλαγές κώδικα άλλων προγραμματιστών' συνεχίζουν να χειρίζονται σωστά τα δεδομένα.string
σας θα χρειαστεί να κάνει κάποια επιπλέον κατανομή μνήμης και/ή αντιγραφή δεδομένων για να προετοιμάσει τον buffer με τερματισμό NUL.
Ως περαιτέρω υπόδειξη, αν οι παράμετροι μιας συνάρτησης απαιτούν το (const
) char*
αλλά δεν επιμένουν να πάρουν το x.size()
, η συνάρτηση πιθανώς χρειάζεται μια είσοδο ASCIIZ, οπότε η .c_str()
είναι μια καλή επιλογή (η συνάρτηση πρέπει να ξέρει που τελειώνει το κείμενο με κάποιο τρόπο, οπότε αν δεν είναι ξεχωριστή παράμετρος μπορεί να είναι μόνο μια σύμβαση όπως ένα πρόθεμα μήκους ή ένα sentinel ή κάποιο σταθερό αναμενόμενο μήκος).
Πώς να διατηρήσετε έναν δείκτη χαρακτήρων έγκυρο ακόμα και μετά την έξοδο του x
από την εμβέλεια ή την περαιτέρω τροποποίησή τουΘα πρέπει να αντιγράψετε τα περιεχόμενα της string
x
σε μια νέα περιοχή μνήμης εκτός της x
. Αυτός ο εξωτερικός απομονωτής θα μπορούσε να βρίσκεται σε πολλά μέρη, όπως σε μια άλλη μεταβλητή string
ή πίνακα χαρακτήρων, μπορεί να έχει ή να μην έχει διαφορετική διάρκεια ζωής από το x
λόγω του ότι βρίσκεται σε διαφορετική εμβέλεια (π.χ. namespace, global, static, σωρό, κοινόχρηστη μνήμη, memory mapped file).
Για να αντιγράψετε το κείμενο από το 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
, αλλά τι μπορείτε να κάνετε με αυτό; Ένα τυχαίο πλήθος παραδειγμάτων...
string
της C++, όπως στο printf("x is '%s'", x.c_str());
x
'σε ένα buffer που καθορίζεται από τον καλούντα τη συνάρτησή σας'(π.χ. strncpy(callers_buffer, callers_buffer_size, x.c_str())
), ή σε πτητική μνήμη που χρησιμοποιείται για I/O συσκευής (π.χ. for (const char* p = x.c_str(); *p; ++p) *p_device = *p;
))x
'σε έναν πίνακα χαρακτήρων που περιέχει ήδη κάποιο κείμενο ASCIIZ (π.χ. strcat(other_buffer, x.c_str())
) - προσέξτε να μην υπερβείτε τον buffer (σε πολλές περιπτώσεις μπορεί να χρειαστεί να χρησιμοποιήσετε το strncat
).const char*
ή char*
από μια συνάρτηση (ίσως για ιστορικούς λόγους - ο πελάτης χρησιμοποιεί το υπάρχον API σας - ή για συμβατότητα με τη C δεν θέλετε να επιστρέψετε ένα std::string
, αλλά θέλετε να αντιγράψετε τα δεδομένα του string
σας κάπου για τον καλούντα)string
στην οποία έδειχνε ο δείκτης έχει εγκαταλείψει την εμβέλειά τηςstd::string
(π.χ. STLport και compiler-native) μπορεί να περνούν τα δεδομένα ως ASCIIZ για να αποφεύγονται οι συγκρούσειςΧρησιμοποιήστε τη μέθοδο .c_str()
για const char *
.
Μπορείτε να χρησιμοποιήσετε τη μέθοδο &mystring[0]
για να λάβετε έναν δείκτη char *
, αλλά υπάρχουν μερικές δυσκολίες: δεν θα λάβετε απαραίτητα μια συμβολοσειρά με μηδενικό τερματισμό και δεν θα μπορείτε να αλλάξετε το μέγεθος της συμβολοσειράς. Ιδιαίτερα πρέπει να είστε προσεκτικοί ώστε να μην προσθέσετε χαρακτήρες μετά το τέλος της συμβολοσειράς, αλλιώς θα έχετε buffer overrun (και πιθανότατα συντριβή).
Δεν υπήρχε καμία εγγύηση ότι όλοι οι χαρακτήρες θα ήταν μέρος του ίδιου συνεχόμενου buffer μέχρι την C++11, αλλά στην πράξη όλες οι γνωστές υλοποιήσεις του std::string
λειτουργούσαν έτσι κι αλλιώς- δείτε Does "&s[0]" point to contiguous characters in a std::string?.
Σημειώστε ότι πολλές συναρτήσεις-μέλη της string
θα ανακατανείμουν τον εσωτερικό buffer και θα ακυρώσουν τυχόν δείκτες που μπορεί να έχετε αποθηκεύσει. Καλύτερα να τις χρησιμοποιείτε αμέσως και μετά να τις απορρίπτετε.