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

前言

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

廢話:在剛參加工做時,以爲只要功能能實現,即是完成任務了。隨着經驗的積累,不少東西就喜歡去深究,去思考,爲何要這麼用,這麼用有什麼好處,效率會不會更高,性能會不會更優。web

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

拷貝方式

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

內存拷貝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;
}

上面函數,大多數狀況下都不會出現報錯的,但存在一個潛在陷阱。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;
}

字符串拷貝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;
}

後語

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

相關文章
相關標籤/搜索