关于C语言字符串函数的思考
当前位置:以往代写 > C/C++ 教程 >关于C语言字符串函数的思考
2019-06-13

关于C语言字符串函数的思考

关于C语言字符串函数的思考

副标题#e#

C语言并不是一种很利便的语言,它的字符串就是一例。凭据C语言的界说,“字符串就是一段内存空间,内里包括ASCII字符,而且,以“\0”末了,总共能存放n-1个字符。”凭据这个描写,字符串处理惩罚确实很贫苦,还很容易堕落。

为了利便用户,C语言尺度库向用户提供了一些字符串函数,如字符串拷贝、结构、清空等函数,在必然水平上利便了用户的利用。可是,我无意中发明,这些函数照旧有些隐患的。

工作很简朴,我留意到我写的一些措施,总是有内存读写错误,可是,颠末仔细查抄我所有的数据Buffer,以及相关的处理惩罚函数,又没有找到什么错误。于是我把猜疑的眼光投向我常用的一些字符串处理惩罚函数上,如strcpy、sprintf等。在颠末屡次仔细地跟踪之后,我发明内存错误出自于此。于是,我开始研究如何安详地利用字符串这个话题。

1.字符串拷贝函数

1.1 不安详的strcpy

首先,我写了这样一个测试函数:

     void strcpyTest0()
    {
    int i;
    char szBuf[128];
    for(i=0;i<128;i++) szBuf[i]='*';
    szBuf[127]='\0';     //结构一个全部是*的字符串
    char szBuf2[256];
    for(i=0;i<256;i++) szBuf2[i]='#';
    szBuf2[255]='\0';   //结构一个全部是#的字符串
    strcpy(szBuf,szBuf2);
    printf("%s\n",szBuf);
    }

很简朴,把一个字符串拷贝到别的一个空间,可是,很不幸,源字符串例如针地点要长,因此,措施很悲凉地死去了。

1.2 照旧不安详的strncpy

通过上例,我发明我需要在拷贝时多输入一个参数,来标明目标地点有多长,查抄C语言的库函数说明,有一个strncpy可以到达这个目标,这个函数的原型如下:

char *strncpy( char *strDest, const char *strSource, size_t count );

好了,这下我们的问题办理了,我写出了如下代码:

 void strcpyTest1()
{
       int i;
       char szBuf[128];
       for(i=0;i<128;i++) szBuf[i]='*';
       szBuf[127]='\0';
       char szBuf2[256];
       for(i=0;i<256;i++) szBuf2[i]='#';
       szBuf2[255]='\0';
       strncpy(szBuf,szBuf2,128);
       printf("%s\n",szBuf);
}

一切都显得很好,可是,当我输出功效的时候,发明白问题,字符串后头有时会跟几个奇怪的字符,仿佛没有用“\0”竣事,于是我把上面的拷贝语句改成“strncpy(szBuf,szBuf2,8);”,只拷贝8个字符,问题呈现了,措施输出如下:

########***********************************************************************************************************************

公然,当请求的方针地点空间比源字符串空间要小的时候,strncpy将不再用“\0”来竣事字符串。庞大的隐患。


#p#副标题#e#

1.3 安详地字符串拷贝函数

我仔细想了想,我认为我需要如下一个字符串拷贝函数:

1、答允用一个整数界定方针地点空间尺寸。

2、当方针地点空间nD小于源字符串长度nS时,应该只拷贝nD个字节。

3、任何环境下,方针地点空间均应该以“\0”竣事,保持一个正当的字符串身份。因此,获得的字符串最大长度为nD-1.

于是,我写了这么一个字符串拷贝函数:

 void xg_strncpy1(char *pD, char *pS,int nDestSize)
{
       memcpy(pD,pS,nDestSize);
       *(pD+nDestSize-1)='\0';
}

很EASY是不,将这个拷贝函数代入上面的例子,只输出7个“#”, 功效正确。

1.4 内存读错误的思考

原来觉得可以就此打住了,不外,没多久,我就发明一个奇怪的现象,这个函数在VC的Debug模式下有错误,可是Release模式下却一切正常。

我奇怪了好久,终于有一天我忍不住了,抉择办理这个问题,我把上面的memcpy用本身的一个复制轮回取代,单步跟踪,想看看毕竟怎么回事?

原因找到了,我但愿拷贝一个256字节长的字符串,可是,拷贝到第33字节时堕落,查抄措施,发明我的源字符串空间只有32 Bytes,本来,我上面的代码只是防备了内存写出界,但没有针对读出界举办查抄,在VC的Debug模式下,内存读出界也是一种犯科错误,因此被报错。

知道了原因,办理就很简朴了,我把上面的拷贝函数改成如下形状:

#p#分页标题#e#

 void xg_strncpy2(char *pD, char *pS,int nDestSize)
{
       int nLen=strlen(pS)+1;
       if(nLen>nDestSize) nLen=nDestSize;
       memcpy(pD,pS,nLen);
       *(pD+nLen-1)='\0';
}

一切OK.

2.字符串结构函数

2.1 不安详的sprintf

如同上例,我在修改拷贝函数的同时,我也想到了别的一个我常用的字符串结构函数sprintf,显然,这个函数没有界定方针地点空间的尺寸,也是不安详的,下面的代码将会造成瓦解:

 void sprintfTest0()
{
       int i;
       char szBuf[128];
       for(i=0;i<128;i++) szBuf[i]='*';
       szBuf[127]='\0';
       char szBuf2[256];
       for(i=0;i<256;i++) szBuf2[i]='#';
       szBuf2[255]='\0';
       sprintf(szBuf,szBuf2);
       printf("%s\n",szBuf);
}

#p#副标题#e#

2.2 照旧不安详的_snprintf

查阅库函数手册,找到这么一个函数_snprintf,其函数原型如下:

int _snprintf( char *buffer, size_t count, const char *format [, argument] …… );

这个函数答允界定方针地点尺寸,可是,由于研究拷贝函数的履历,我猜疑它也有strncpy沟通的问题,因此,我写了这么一段代码测试:

 void sprintfTest1()
{
       int i;
       char szBuf[128];
       for(i=0;i<128;i++) szBuf[i]='*';
       szBuf[127]='\0';

       char szBuf2[256];
       for(i=0;i<256;i++) szBuf2[i]='#';
       szBuf2[255]='\0';

       _snprintf(szBuf,8,szBuf2);
       printf("%s\n",szBuf);
}

公然,措施输出如下:

########***********************************************************************************************************************

同样的错误,没有用“\0”竣事,我必需别的想要领。

别的,还发明白别的一个不敷,就是这个时候,_snprintf函数返回-1,不再返回打印的字符数,那么,我们假如利用如下代码将会造成逻辑错误,甚至大概瓦解:

char szBuf[256];
int nCount=0;
while(1)  //这里暗示轮回结构
{
       nCount+=_snprintf(szBuf+nCount,256-nCount,”... ...”);  //多个字符串结构成一个字符串
}

留意,代码操作_snprintf返回的值,来确定下一个起始点,这很常用,可是,当_snprintf返回-1的时候,有大概会写到*(szBuf-1)的位置上,典范的内存写出界。

2.3 安详地字符串结构函数

颠末仔细思考,我结构了如下一个函数:

 int xg_printf(char* szBuf,int nDestSize,char *szFormat, ...)
{
       int nListCount=0;
       va_list pArgList;
       va_start (pArgList,szFormat);
       nListCount+=_vsnprintf(szBuf+nListCount,
              nDestSize-nListCount,szFormat,pArgList);
       va_end(pArgList);
       *(szBuf+nDestSize-1)='\0';
       return strlen(szBuf);
}

留意,这里我回收了变参函数设计,为的是和sprintf一样利便,别的,最后一个return也很是重要,因为许多场所,我们需要知道毕竟打印了几多字符。将这段函数代入上面的例子后一切正常。

总结:C语言字符串库函数大概是出于提高机能目标,在一旦条件不足的时候,往往直接返回,忘了回收“\0”竣事字符串。这会造成下一次读取字符串时,数据界线不行控。名目化打印函数,返回值设计不公道,不永远是一个正整数,会造成逻辑隐患。因此,发起各人有乐趣可以参考一下我提供的两个函数。

别的,以上仅为我小我私家测试之作,限于本人程度所限,必定尚有没思量到的处所,接待各人展开接头。假如各人需要上面的源代码,请和我接洽。

    关键字:

在线提交作业