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陈小旭



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