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