背景:
有了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(命名规则一般是在后面加上_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
#include
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陈小旭