c++ - const char to char
如何將std:: string轉換為const char*或char*? (6)
如何將std::string
轉換為char*
或const char*
?
C ++ 17
C ++ 17 (即將發布的標準)改變了模板basic_string
的概要,添加了一個非const的data()
重載data()
:
charT* data() noexcept;
返回:一個指針p,使得[+,size()]中每個i的p + i ==&運算符。
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開始,標准說:
basic_string
對像中的類char對象應連續存儲。 也就是說,對於任何basic_string
對象,標識&*(s.begin() + n) == &*s.begin() + n
應該適用於&*(s.begin() + n) == &*s.begin() + n
的所有值,使得0 <= n < s.size()
。
const_reference operator[](size_type pos) const;
reference operator[](size_type pos);
如果
pos < size()
,則返回*(begin() + pos)
,否則引用CharT
類型CharT
的對象。 參考值不得修改。
const charT* c_str() const noexcept;
const charT* data() const noexcept;
返回:對於
[0,size()]
每個i
,指針p使得p + i == &operator[](i)
。
有多種可能的方法來獲取非const字符指針。
1.使用C ++ 11的連續存儲
std::string foo{"text"};
auto p = &*foo.begin();
臨
- 簡單而短小
- 快速(只有沒有涉及副本的方法)
缺點
- 最終
'\0'
不會被改變/不一定是非常量內存的一部分。
2.使用std::vector<CharT>
std::string foo{"text"};
std::vector<char> fcv(foo.data(), foo.data()+foo.size()+1u);
auto p = fcv.data();
臨
- 簡單
- 自動內存處理
- 動態
缺點
- 需要字符串複製
3.如果N
是編譯時間常量(足夠小) std::array<CharT, N>
使用std::array<CharT, N>
std::string foo{"text"};
std::array<char, 5u> fca;
std::copy(foo.data(), foo.data()+foo.size()+1u, fca.begin());
臨
- 簡單
- 堆棧內存處理
缺點
- 靜態的
- 需要字符串複製
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]);
臨
- 小內存佔用
- 自動刪除
- 簡單
缺點
- 需要字符串複製
- 靜態(動態使用需要更多的代碼)
- 功能比矢量或陣列少
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;
}
臨
- 最大“控制”
精讀
- 需要字符串複製
- 錯誤的最大責任/易感性
- 複雜
只要看到這個:
string str1("");
const char * str2 = str1.c_str();
但是,請注意,這將返回一個const char *
。對於char *
,請使用strcpy
將其複製到另一個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
。 有兩種直接的方法可以解決這個問題。
提高:: 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 ::矢量
這是標準方式(不需要任何外部庫)。 你使用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()
對const char *
使用.c_str()
方法。
您可以使用&mystring[0]
來獲取char *
指針,但有幾個問題需要注意:您不一定會得到零終止的字符串,並且您將無法更改字符串的大小。 你特別要小心,不要在字符串末尾添加字符,否則你會得到一個緩衝區溢出(並可能崩潰)。
在C ++ 11之前,並不能保證所有的字符都是同一個連續緩衝區的一部分,但實際上所有已知的std::string
實現都是這樣工作的; 請參閱“&s [0]”是否指向std :: string中的連續字符? 。
請注意,許多string
成員函數將重新分配內部緩衝區並使您可能已保存的任何指針無效。 最好立即使用它們,然後丟棄。
鑑於說...
std::string x = "hello";
從字符串中獲取`char *`或`const char *`
如何獲得一個有效的字符指針,而x
仍然在範圍內,並且不會進一步修改
C ++ 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...
以上所有指針都將保持相同的值 - 緩衝區中第一個字符的地址。 即使是空字符串也有“緩衝區中的第一個字符”,因為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()]
僅用於來自&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
允許包含任意數量的NULs - 它們沒有被std::string
(在C ++ 03中相同)沒有特殊處理。
在C ++ 03中 ,事情要復雜得多(重點突出 ):
x.data()
- 將
const char*
返回到字符串的內部緩衝區,標準沒有要求它以NUL結束 (即可能是['h', 'e', 'l', 'l', 'o']
然後是未初始化的或垃圾值,意外訪問具有未定義的行為 )。-
x.size()
字符可以安全讀取,即x[0]
至x[x.size() - 1]
- 對於空字符串,你保證有一些非NULL指針,可以安全地添加0(hurray!),但不應該取消引用該指針。
-
- 將
&x[0]
- 對於空字符串,這有未定義的行為 (21.3.4)
- 例如給定
f(const char* p, size_t n) { if (n == 0) return; ...whatever... }
f(const char* p, size_t n) { if (n == 0) return; ...whatever... }
你不能調用f(&x[0], x.size());
當x.empty()
- 只使用f(x.data(), ...)
。
- 例如給定
- 否則,根據
x.data()
但是:- 對於非常量
x
這會產生一個非const
char*
指針; 你可以覆蓋字符串內容
- 對於非常量
- 對於空字符串,這有未定義的行為 (21.3.4)
x.c_str()
- 將
const char*
返回給值(即['h','e','l','l','o','\ 0'])的ASCIIZ(NUL-terminated)表示。 - 儘管很少有實現選擇這樣做,但C ++ 03標準的措辭允許字符串實現自由地創建一個獨立的NUL終止緩衝區 ,從可能非NUL終止緩衝區“暴露”
x.data()
和&x[0]
-
x.size()
+ 1個字符可以安全讀取。 - 即使對於空字符串也是安全的(['\ 0'])。
- 將
獲取外部法律索引的後果
無論你如何獲得一個指針,你都不能訪問指針以外的內存,而不是上面描述中保證的字符。 嘗試這樣做的方式有未定義的行為 ,即使是讀取也會出現應用程序崩潰和垃圾結果的真實機會,並且還會出現批量數據,堆棧損壞和/或寫入安全漏洞。
這些指針何時失效?
如果您調用一些修改string
成員函數或保留更多容量,則上述任何方法事先返回的任何指針值都將失效 。 您可以再次使用這些方法來獲取另一個指針。 (規則與迭代器到string
s相同)。
另請參見如何獲取字符指針,即使在x
離開作用域後仍然有效,或者在下面進一步修改 ....
那麼,哪個更好用?
從C ++ 11開始,對ASCIIZ數據使用.c_str()
,對“二進制”數據使用.data()
(下面進一步解釋)。
在C ++ 03中,除非確定.data()
足夠,否則使用.c_str()
,並且通過&x[0]
偏好.data()
,因為它對於空字符串是安全的。
...嘗試了解程序足以在適當時使用data()
,否則您可能會犯其他錯誤...
由.c_str()
保證的ASCII NUL'\ 0'字符被許多函數用作表示相關和安全訪問數據結束的.c_str()
值。 這適用於像fstream::fstream(const char* filename, ...)
這樣的僅用於C ++的函數fstream::fstream(const char* filename, ...)
以及用strchr()
和printf()
等C函數共享。
鑑於C ++ 03的.c_str()
對返回緩衝區的保證是超級集合.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);
其他原因需要從string
生成char*
或const char*
所以,上面你已經看到瞭如何獲得( const
) char*
,以及如何使文本的副本獨立於原始string
,但是你可以用它做什麼? 隨機一些例子...
- 給
printf("x is '%s'", x.c_str());
- 將
x
的文本複製到由函數調用者指定的緩衝區(例如,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())
)的字符數組中 - 注意不要溢出緩衝區(在許多情況下,您可能需要使用strncat
) - 從函數返回一個
const char*
或char*
(可能出於歷史原因 - 客戶端使用您現有的API - 或者為了兼容C,您不想返回std::string
,但是想要復製string
的數據來電者的地方)- 注意不要返回一個指針,該指針可能在調用者被指針指向的本地
string
變量左側的作用域後被解除引用 - 對於不同的
std::string
實現(例如,STLport和編譯器本地)編譯/鏈接的共享對象的某些項目可能會將數據作為ASCIIZ傳遞以避免衝突
- 注意不要返回一個指針,該指針可能在調用者被指針指向的本地
char* result = strcpy((char*)malloc(str.length()+1), str.c_str());