您的位置:首页 > 其它

字符串操作函数的一些细节问题

2015-08-07 15:20 288 查看
最近在写一个数据库的部分数据显示工具,然后把结果以表格形式输出时需要很多的字符串操作函数和多维数组及指针的操作,对其理解更多,现在总结一下。

1、strcpy 、strncpy 和 strlcpy 函数

strcpy

原型:extern char *strcpy(char *dest,char *src);
功能:把从src地址开始且含有NULL结束符的字符串赋值到以dest开始的地址空间,返回dest(地址中存储的为复制后的新值)。
要求:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。
该函数的参数是字符指针,也就是可以是字符串变量和字符数组,因为它们的变量名代表首字符地址。字符串默认有一个null结束符,字符数组没有。所以此处需要注意:因为src要求有null结束符,所以字符数组的长度必须大于等于src包含null结束符的总长度。例如,char* src="abcd"; char dest[5]; 这里dest的长度就至少为5。
strcpy 是不安全的,因为不会检查第一个参数的内存空间是否足够,所以可能会导致内容被覆盖。所以推荐使用安全的strncpy。


strncpy:

原型:char * strncpy(char *dest, char *src, size_tn);
功能:将字符串src中最多n个字符复制到字符数组dest中(它并不像strcpy一样遇到NULL才停止复制,而是等凑够n个字符才开始复制),返回指向dest的指针。
要求:如果n > dest串长度,dest栈空间溢出产生崩溃异常。该函数注意的地方和strcpy类似,但是n值需特别注意。
对于n的大小需要注意:
1)src串长度<=dest串长度,(这里的串长度包含串尾NULL字符)


  如果n在0和 src串长度之间,src的前n个字符复制到dest中。但是由于没有NULL字符,所以直接访问dest串会发生栈溢出的异常情况。这时,一般建议采取memset将dest的全部元素用null填充,如:memset(dest,0,7)(7为从dest起始地址开始前7个位置填充null,dest可以为字符指针和数组名)。注意:char* pc=”abc”; char chs[5]; sizeof(pc)为4(包含null),strlen(pc)为3,sizeof(chs)为5。

  如果n = src串长度,与strcpy一致。

  如果n = dest串长度,[0,src串长度]处存放于desk字串,(src串长度, dest串长度]处存放NULL。

  2)src串长度>dest串长度

  如果n =dest串长度,则dest串没有NULL字符,会导致输出会有乱码。如果不考虑src串复制完整性,可以将dest最后一字符置为NULL。

  所以,一般把n设为dest(含null)的长度(除非将多个src复制到dest中)。当2)中n=dest串长度时,定义dest为字符数组,因为这时没有null字符拷贝。

char buf[8];
strncpy( buf, "abcdefgh", 8 );


看这个程序,buf 将会被 “abcdefgh” 填满,但却没有 /0 结束符了。

另外,如果 src 的内容比较少,而 n 又比较大的话,strncpy 将会把之间的空间都用 /0 填充。这又出现了一个效率上的问题,如下:

char buf[80];
strncpy( buf, "abcdefgh", 79 );


上面的 strncpy 会填写 79 个 char,而不仅仅是 “abcdefgh” 本身。

strncpy 的标准用法为:(手工写上 /0)

strncpy(path, src, sizeof(path) - 1);
path[sizeof(path) - 1] = '/0';
len = strlen(path);


strlcpy

size_t strlcpy(char *dst, const char *src, size_t siz);

而使用 strlcpy,就不需要我们去手动负责 /0 了,仅需要把 sizeof(dst) 告之 strlcpy 即可:

strlcpy(path, src, sizeof(path));
len = strlen(path);
if ( len >= sizeof(path) )
printf("src is truncated.");


并且 strlcpy 传回的是 strlen(str),因此我们也很方便的可以判断数据是否被截断。

但是strlcpy并不是标准函数。

2、strcat和strcatt_s函数

strcat

  原型:extern char* strcat(char *dest, const char *src);

  功能:将字符串src所有元素追加到字符串dest的尾部。

  要求:dest必须有足够的空间来容纳两个字符串连接后新字符串。

  在追加过程中,并不会检测dest所指的内存空间容量和内容,因此在复制过程中存在安全隐患,有可能会改写字符串后的一些重要数据。故这个安全问题需要调用该函数者自己处理,比如多申请足够多的空间,使其能够保存追加后的字符串。

  strcat的实现是先去掉dest尾部的结束符,然后再把src连到dest尾部。这就要注意字符串的结束符是’\0’,也就是二进制0,那么strcat的时候系统会忽略串最后的’\0’,比如strA=”abcd \0”,strB=”ABC\0”,那么strcat(strA,strB)= “abcdABC\0”,如果strA=”abcd\0\0\0……”结果也是一样的。如果前面把strA用memset(strA,0,sizeof(strA));这样初始化了,再用strcat去连接,结果是strA和strB是一样的结果。正确的是 memset(strA,‘0’,sizeof(strA));

strcat_s

我在做目前这个项目的时候并没有用到strcat_s,但是看到网上有它的资料,记录一下,以后有需要可以试试。

函数声明:

errno_t strcat_s(
char *strDestination,
size_t numberOfElements,
const char *strSource
);


要注意的是第二个参数,实际上第二个参数是合并字符串后的字符数量。即,源串大小 + 目标串大小 + 字符串结束符大小(“\0”)

1、strcat_s(ret, sizeof(ret), str1);
2、strcat_s(ret, strlen(str1), str1);


要注意sizeof(ret)大小是否够合并后字符串的大小,如果不够会报错。第二行也是会有可能出现同样错误,BUFF大小不够。

当没有初始化字符串的时候也会出现错误。要注意’memset(ret, 0, sizeof(ret));‘

3、strcmp和strncmp、 memcmp

strcmp

原型:int strcmp (const char *s1, const char *s2)

功能:这个函数用来比较s1和s2字符串(按ASCII值大小相比较),这个函数将返回一个值,它的符号与第一对不同的字符的比较结果相关。 如果两个字符串相等的话,strcmp将返回0。 如果s1是s2的一个子串的话,s1小于s2 。该函数只能比较字符串。

要求:strcmp是按照字节比较的,如果出现”\0”的情况会终止比较,一旦任意一个字符串指针前进过程中遇到结束符,将终止比较。

strncmp

原型:int strncmp (const char *s1, const char *s2, size_t size)

功能:此函数与strcmp极为类似。不同之处是,strncmp函数是指定比较size个字符。也就是说,如果字符串s1与s2的前size个字符相同,函数返回值为0。

memcmp

原型:extern int memcmp(void *buf1, void *buf2, unsigned int count);

功能:比较a1和a22个buff内的前size个字节,如果相同返回的结果是0。不仅限于比较字符串。

要求:memcmp 用来比较内存块的内容是否一致,不常用于字节的比较,中包含一些由于边界对齐需求而填入结构对象中的空格、联合 (union)结束的额外空格、字符串所分配的空间未使用完的部分引起的“holes”的话,最好使用memcmp来完成,这些“holes”的内容是不确定的,在执行byte-wise比较时结果也是不明确的。

效率差异: strcmp比较的字符串,而memcmp比较的是内存块,strcmp需要时刻检查是否遇到了字符串结束的 /0 字符,而memcmp则完全不用担心这个问题,所以memcmp的效率要高于strcmp
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  字符串操作