strcat

背景:

有了String類之後,重載了operator +操作符,字符串的相加已經變得異常容易了。但是,strcat還是會時常出現在我們的眼前。

看了一些資料,總結了一下。

C的strcat:

C的strcat是最初的strcat,也是不安全的strcat。(當然,不安全的不止是strcat,和char*相關的很多比如strcpy等也都是不安全的)

它的源代碼如下:

char * __cdecl strcat(char * dst, const char * src)
{
	char * cp = dst;          //保存dst指針
	while (*cp)
		cp++;                 //查找dst字符串的末尾
	while (*cp++ = *src++); //拷貝src字符串到dst後面
	return(dst);            //返回dst字符串指針
}

更直觀一點的寫法如下:

char *strcat(char *dst, const char *src)
{
	if ((dst == NULL) || (src == NULL))//增加了判斷是否爲NULL
		return dst; // or throw error
	char *pt = dst;
	while (*dst != '\0') dst++;
	while (*src != '\0') *dst++ = *src++;
	*dst = '\0';
	return pt;
}

VS的strcat_s:

VS提供了更加安全的函數strcat_s(命名規則一般是在後面加上_s)

其函數聲明爲:errno_t strcat_s(char *strDestination, size_t numberOfElements, constchar *strSource);

可以看到,相對於原有的形式,第二個參數是制定dst的長度,來確保不會出現溢出的問題。


思考:

對於C的strcat,主要是會出現前面一個dst的存儲空間不夠,當src的字符添加到其後時,容易出現溢出越界的情況,嚴重的甚至會覆蓋內存中其他數據。

造成系統崩潰,那麼我們爲什麼不能重新申請一段空間來存放dst+src呢。

這裏會有一個問題:strcat(str1,str2)後,雖然其有一個返回值char*,但是它也可以不用返回,直接用str1就可以了。

而當我們新申請一段空間後,str1的地址不能指向新的空間地址(因爲是按char*:字符指針地址的按值傳遞)所以,我們不能更改str1的地址。

除非我們用其返回值,寫成str1 = strcat(str1,str2)來表示。


在寫代碼之前,我們先來看看strcat需要的注意點:

1)dst和src是否是NULL
2)是否需要重新申請空間複製過去
3)結尾放上\0

改進版:

char* strcat_new(char* dst, char* src){
 if (dst == NULL || src == NULL)
  return dst; //or throw error,注意點(1)
 char* ret = new char[strlen(dst) + strlen(src)];//注意點(2)
 char* pt = ret; //保證ret不變,返回的應該是首地址
 while (*dst != NULL){
  *pt++ = *dst++;
 }
 char *pSrc = src;//保證src不變
 while (*pSrc != NULL){
  *pt++ = *pSrc++;
 }
 *pt = '\0';//注意點(3)
 dst = ret;
 return dst;
}

測試代碼:

#include<stdio.h>
#include<iostream>
using namespace std;

char* strcat_new(char* dst, const char* src){//src 前面加上const
	if (dst == NULL || src == NULL)
		return dst; //or throw error,注意點(1)
	char* ret = new char[strlen(dst) + strlen(src) + 1];//注意點(2)
	if (ret == NULL){
		ret = new char[strlen(dst) + strlen(src) + 1];//重新申請或者throw error
	}
	char* pt = ret; //保證ret不變,返回的應該是首地址
	while (*dst != NULL){
		*pt++ = *dst++;
	}
	const char *pSrc = src;//保證src不變
	while (*pSrc != NULL){
		*pt++ = *pSrc++;
	}
	*pt = '\0';//注意點(3)
	return ret;
}


int main(){
	{
		char str1[20] = "I am";//這裏必須要有足夠的空間
		char* str2 = " Apie_CZX !";
		strcat(str1, str2);
		cout << "Use original C's strcat:" << endl;
		puts(str1);
	}
	cout << "-----------------------------------------------" << endl;
	{
		char str1[20] = "I am";//這裏必須要有足夠的空間
		char *str2 = " Apie_CZX !";
		strcat_s(str1, sizeof(str1), str2);
		cout << "Use VS's strcat_s:" << endl;
		puts(str1);
	}
	cout << "-----------------------------------------------" << endl;
	{
		char* str1 = "I am";
		char* str2 = " Apie_CZX !";
		cout << "Use new strcat:" << endl;
		str1 = strcat_new(str1, str2);
		puts(str1);
	}
	return 0;
}

結果截圖:


參考資料:

【1】http://blog.sina.com.cn/s/blog_4a033b090100zarl.html

【2】http://baike.baidu.com/link?url=1Ma7gBfBuMOBqaHyKkn99AikHXrNQ5KEjVJqkZ2_DfYXWOAHZlDHuzWObyQ-7k-7EUrFEVgy4fglv4445M5C6a

【3】http://blog.csdn.net/yuan22003/article/details/6712685

【4】http://www.cnblogs.com/guoyuqiangf8/archive/2012/01/14/2322542.html

——Apie陳小旭



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章