在實際項目開發中,字符串拷貝是個很常見用法。方式有不少種,在咱們使用過程當中,通常不會出現什麼問題,或者說是通常編譯器不會編譯報錯,甚至運行報錯。但一些潛在的陷阱是常常存在的,如在使用VS 2017開發時,會發現不少語法編譯器檢測會更嚴謹,之前如vs2010不會報錯的,vs2017會編譯不過,或編譯成功了,運行報錯。ios
廢話:在剛參加工做時,以爲只要功能能實現,即是完成任務了。隨着經驗的積累,不少東西就喜歡去深究,去思考,爲何要這麼用,這麼用有什麼好處,效率會不會更高,性能會不會更優。web
好了,廢話多說無益,下面正式開討論今天的主題。數組
字符串拷貝的方式有不少種,下面我主要說說memcpy、strcpy、string::copy這三種。svg
void* __cdecl memcpy( _Out_writes_bytes_all_(_Size) void* _Dst, _In_reads_bytes_(_Size) void const* _Src, _In_ size_t _Size );
簡單點:函數
void *memcpy(void *dest, const void *src, size_t count);
參數這裏不細說。性能
std::string CopyString(const std::string &strBody) { int nLen = strBody.length(); char *cBody = new char(nLen); memcpy(cBody, strBody.c_str(), nLen); std::cout << "FONCTION:" << cBody << std::endl; return cBody; } int main() { std::string strBody = "This is a Test!"; std::string strReturn = CopyString(strBody); std::cout << "MAIN:" << strReturn.c_str() << std::endl; system("pause"); return 0; }
上面函數,大多數狀況下都不會出現報錯的,但存在一個潛在陷阱。this
內存拷貝不會對字符串結束符'\0'
進行檢查spa
拷貝結束後,在字符串末尾會出現亂碼。指針
申請內存時多申請一個字節內存,以保證將字符串結束符拷貝進去。code
std::string CopyString(const std::string &strBody) { int nLen = strBody.length(); char *cBody = new char(nLen + 1); memcpy(cBody, strBody.c_str(), nLen + 1); std::cout << "FONCTION:" << cBody << std::endl; return cBody; } int main() { std::string strBody = "This is a Test!"; std::string strReturn = CopyString(strBody); std::cout << "MAIN:" << strReturn.c_str() << std::endl; delete strReturn.c_str(); system("pause"); return 0; }
char* __cdecl strcpy( _Out_writes_z_(_String_length_(_Source) + 1) char* _Dest, _In_z_ char const* _Source );
簡單點:
char *strcpy(char *dst, const char *src);
std::string CopyString(const std::string &strBody) { int nLen = strBody.length(); char *cBody = new char(nLen); strcpy(cBody, strBody.c_str()); std::cout << "FONCTION:" << cBody << std::endl; return cBody; } int main() { std::string strBody = "This is a Test!"; std::string strReturn = CopyString(strBody); std::cout << "MAIN:" << strReturn.c_str() << std::endl; delete strReturn.c_str(); system("pause"); return 0; }
strcpy
是專用於字符串拷貝的函數,與memcpy
的區別就是,它會檢測結束符'\0'
,因此在申請內存時不用作多餘申請。
size_type copy(_Out_writes_(_Count) _Elem * const _Ptr, size_type _Count, const size_type _Off = 0) const { // copy [_Off, _Off + _Count) to [_Ptr, _Ptr + _Count) auto& _My_data = this->_Get_data(); _My_data._Check_offset(_Off); _Count = _My_data._Clamp_suffix_size(_Off, _Count); _Traits::copy(_Ptr, _My_data._Myptr() + _Off, _Count); return (_Count); }
仍是簡單點:
size_t copy (char* s, size_t len, size_t pos = 0) const;
這裏解釋一下這幾個參數:
s
指向一組字符的指針。
該數組應包含足夠的存儲空間用於複製的字符。
len
要複製的字符數(若是字符串較短,則複製儘量多的字符)。
pos
要複製的第一個字符的位置。
若是這大於字符串長度,則拋出out_of_range。
注意:字符串中的第一個字符由值0(不是1)表示。
// string::copy #include <iostream> #include <string> int main () { char buffer[20]; std::string str ("This is a Test"); std::size_t length = str.copy(buffer,6,5); std::cout << "buffer contains: " << buffer << '\n'; return 0; }
由於這裏copy
最終調用的函數仍然是memcopy
,因此陷阱同樣,拷貝完成後,會在字符串後面帶上一串亂碼
在拷貝結束後,加上結束符'\0'
。
// string::copy #include <iostream> #include <string> int main () { char buffer[20]; std::string str ("This is a Test"); std::size_t length = str.copy(buffer,6,5); buffer[length]='\0'; std::cout << "buffer contains: " << buffer << '\n'; return 0; }
這裏還有一種方式,可是這種方式待討論,由於我目前的結果是正確的。那就是在申請內存後,第一時間進行初始化,這樣拷貝也不會出現亂碼。
例如:
std::string CopyString(const std::string &strBody) { char *cBody = NULL; int nLen = strBody.size(); cBody = (char *)malloc(nLen); memset(cBody, 0, nLen); strBody.copy(cBody, nLen, 0); std::cout << "FONCTION:" << cBody << std::endl; return cBody; } int main() { std::string strBody = "This is a Test!"; std::string strReturn = CopyString(strBody); std::cout << "MAIN:" << strReturn.c_str() << std::endl; system("pause"); return 0; }
在咱們開發過程當中,不少細節須要咱們去推敲,不注意細節,不出問題倒好,一出問題,連問題都很差找,特別是在大型項目中。