字符串拷貝方法以及存在陷阱


前言

在實際項目開發中,字符串拷貝是個很常見用法。方式有很多種,在我們使用過程中,一般不會出現什么問題,或者說是一般編譯器不會編譯報錯,甚至運行報錯。但一些潛在的陷阱是經常存在的,如在使用VS 2017開發時,會發現很多語法編譯器檢測會更嚴謹,以前如vs2010不會報錯的,vs2017會編譯不過,或編譯成功了,運行報錯。

廢話:在剛參加工作時,覺得只要功能能實現,便是完成任務了。隨着經驗的積累,很多東西就喜歡去深究,去思考,為什么要這么用,這么用有什么好處,效率會不會更高,性能會不會更優。

好了,廢話多說無益,下面正式開討論今天的主題。

拷貝方式

字符串拷貝的方式有很多種,下面我主要說說memcpy、strcpy、string::copy這三種。

內存拷貝memcpy

函數原型

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

上面函數,大多數情況下都不會出現報錯的,但存在一個潛在陷阱。

陷阱

內存拷貝不會對字符串結束符'\0'進行檢查

結果

拷貝結束后,在字符串末尾會出現亂碼。

解決方案

申請內存時多申請一個字節內存,以保證將字符串結束符拷貝進去。

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

字符串拷貝strcpy

函數原型

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',所以在申請內存時不用做多余申請。

string方法拷貝string::copy

原型

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

后語

在我們開發過程中,很多細節需要我們去推敲,不注意細節,不出問題倒好,一出問題,連問題都不好找,特別是在大型項目中。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM