文章目录
前言
C语言中对字符和字符串的处理非常频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。字符串常量适用于那些对它不做修改的字符串函数。
一、常用字符串函数
1.strlen
size_t strlen ( const char * str );
(1)字符串以’\0’作为结束标志,strlen函数返回的是在字符串中’\0’前面出现的字符个数(不包含 ‘\0’ )。
(2)参数指向的字符串必须要以 ‘\0’ 结束。
(3)注意函数的返回值为size_t,是无符号的( 易错 )。
2.strcpy字符串拷贝
char* strcpy(char * destination, const char * source );
将source的字符串拷贝到destination中,对应空间上的会覆盖掉。
(1)源字符串必须以 ‘\0’ 结束。
(2)会将源字符串中的 ‘\0’ 拷贝到目标空间。
(3)目标空间必须足够大,以确保能存放源字符串。
(4)目标空间必须可变。
补充:
“目标空间足够大”由程序调用者保证,因此是不怎么靠谱的。C语言之后的编程语言不再是这样,类似的操作都支持自适应,自动扩容。
3.strcat字符串拼接
char * strcat ( char * destination, const char * source );
将source的字符串追加到destnation字符串的后面。
(1)源字符串必须以 ‘\0’ 结束。
(2)目标空间必须有足够的大,能容纳下源字符串的内容。
(3)目标空间必须可修改。
4.strcmp字符串比较
int strcmp ( const char * str1, const char * str2 );
标准规定:
第一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数字
按照字典序进行比较。例如"zhangsan"和"lisi"两个字符串,比较第一个字母,‘z’的ASCII码大于’l’,则函数返回一个大于零的数字。若ASCII码相等,则继续比较下一个字母的ASCII码,直到一个字符串结束。如"abc"和"abcd",到’c’为止,均相等,那么较长的那个字符串较大。
5.strstr查找子串
char * strstr ( const char *, const char * )
例如:"llo"是“helloworld”的一个子串,若找到子串,strstr函数返回一个指向子串位置的指针;若没找到,则返回空指针NULL。
6.strtok字符串切分
char * strtok ( char * str, const char * sep );
字符串切分是一个实际开发中非常高频使用的功能。
例如,若想切分以下字符串:
p = strtok(str," ");
第一次调用,参数传了str,strtok会从str位置开始往后找,找到空格符之后,就会把这个位置改成’\0’,同时记录这个位置,最后返回起始位置(指向I的指针)。
p = strtok(NULL," ");
第二次调用,参数传了NULL,表示从上次切分的位置的下一个位置继续切分。
strtok从上次位置继续往后找空格符,找到后改成’\0’,记录位置,返回指向’a’的指针。
第三次调用同第二次,返回’a’的指针。
第四次调用的时候,从student的s出发往后找,没有找到空格,而是遇到了’\0’,那么这次strtok的调用也是结束了,返回’s’的指针。
第五次调用,起始位置已经是’\0’了,所以直接返回NULL表示整个切分都已经结束了。
注意:strtok有很多缺陷,在实际开发中很少使用。
1、使用太麻烦(需要搭配循环,且每次循环参数不同)。
2、修改了原始字符串。
3、需要使用static变量来记录上次的切分位置,线程不安全。
二、常用内存函数
1.memcpy拷贝
void * memcpy ( void * destination, const void * source, size_t num );
(1)函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
(2)这个函数在遇到 ‘\0’ 的时候并不会停下来。
(3)如果source和destination有任何的重叠,复制的结果都是未定义的。
模拟实现memcpy的代码如下:
void* myMemcpy(void* dest, const void* src, size_t num) {
assert(dest != NULL && src != NULL);
char* pDest = (char*)dest;
char* pSrc = (char*)src;
for (size_t i = 0; i < num; i++) {
//*(dest + i) = *(src + i);
*(pDest + i) = *(pSrc + i);
}
return dest;
}
2.memmove重叠拷贝
void * memmove ( void * destination, const void * source, size_t num );
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。如果源空间和目标空间出现重叠,就得使用memmove函数处理。
模拟实现代码如下:
void* myMemcpy(void* dest, const void* src, size_t num) {
assert(dest != NULL && src != NULL);
char* pDest = (char*)dest;
char* pSrc = (char*)src;
for (size_t i = 0; i < num; i++) {
//*(dest + i) = *(src + i);
*(pDest + i) = *(pSrc + i);
}
return dest;
}
补充——关于合法性判定
assert是一种比较严厉的处理方式,一旦触发,程序就会崩溃。
注意事项:
1、一般是拿参数和空指针判定。
2、若当前场景下是比较严重的问题,适合使用assert。若不是,就是应该使用if语句自行处理问题。
3、判定的时机最好是double check,调用的时候要判定,调用内部也要判定。