背景:
有了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陳小旭