作者:敲代码の流川枫
博客主页:流川枫的博客
专栏:C语言从入门到进阶
语录:Stay hungry stay foolish
工欲善其事必先利其器,给大家介绍一款超牛的斩获大厂offer利器——牛客网
点击免费注册和我一起刷题吧
重点介绍处理字符和字符串的库函数的使用和注意事项
目录
1.求字符串长度
1.1 strlen
1.2 strlen的模拟实现
2.长度不受限制的字符串函数
2.1 strcpy
2.2strcpy的模拟实现
2.3strcat
2.4strcat 的模拟实现
2.5strcmp
2.6 strcmp的模拟实现
3.长度受限制的字符串函数
3.1 strncpy
3.2 strncpy的模拟实现
3.3strncat
3.4 strncat的模拟实现
3.5strncmp
4.字符串查找
4.1 strstr
4.2 strstr的模拟实现
4.3strtok
5.错误信息报告
5.1strerror
6.内存操作函数
6.1 memcpy
6.2 memcpy的模拟实现
6.3memmove
6.4 memmove的模拟实现
6.5memset
6.6 memcmp
1.求字符串长度
1.1 strlen
size_t strlen ( const char * str );
函数功能:strlen函数返回的是在字符串中’\0’前面出现的字符个数(不包含’\0′)
注意事项:
参数指向的字符串必须要以’\0’结束,如果不以‘\0’结束,会一直向后读取字符,直到出现零终止符为止,因此会返回随机值,编译器也会提示需要给字符串添加零终止符
#define _CRT_SECURE_NO_WARNINGS#include#includeint main(){char arr1[] = { 'b','i','t' };int len = strlen(arr1);printf("%d\n", len);return 0;}
注意函数的返回值为size_t,是无符号整型
#define _CRT_SECURE_NO_WARNINGS#include#includeint main(){if (strlen("abc") - strlen("abcdef") > 0)printf(">");if (strlen("abc") - strlen("abcdef") < 0)printf("<");if (strlen("abc") - strlen("abcdef") == 0)printf("==");return 0;}
这段代码不仔细分析会认为输出的是”“,因为size_t返回的是无符号整型,-3的补码被当成无符号整型来处理的时候返回的是非常大的正数,因此会是”>“ ,要注意strlen的返回值是无符号整型,可以这样直接比较strlen(“abc”) >strlen(“abcdef”),也可以将strlen函数返回值强制类型转换为有符号整型
1.2 strlen的模拟实现
#define _CRT_SECURE_NO_WARNINGS#include#include//创建计数器方式size_t my_strlen(const char* str){size_t count = 0;assert(str);while (*str !='\0'){count++;str++;}return count;}int main(){char arr[] = "abcdef";size_t n = my_strlen(arr);printf("%u\n", n);return 0;}
还有两种模拟实现方式:
//指针-指针的方式int my_strlen(char*s){char*p=s;while(*p!='\0')p++;return p -s;}//不能创建临时变量计数器int my_strlen(const char*str){ if(*str=='\0')return 0;else return 1+my_strlen(str+1);}
2.长度不受限制的字符串函数
2.1 strcpy
char* strcpy(char* destination, const char*source );
注意事项:
源字符串必须以’\0’结束
会将源字符串中的’\0’拷贝到目标空间
目标空间必须足够大,以确保能存放源字符串
目标空间必须可变
2.2strcpy的模拟实现
#define _CRT_SECURE_NO_WARNINGS#include#includechar* my_strcpy(char* str1,const char* str2){assert(str1);assert(str2);char*ret = str1;while (*str1++ = *str2++){;}return ret;}int main(){char arr1[] = "abcdef";char arr2[20] = { 0 };my_strcpy(arr2, arr1);printf("%s\n",arr2);return 0;}
char*ret = str1;在函数结束要返回str1的地址时,str1已经改变,因此要在未改变之前创建ret保存它的初始地址用于返回
2.3strcat
char* strcat ( char* destination, const char* source );
注意事项:
目标空间必须可修改,能够将数据追加上去
源字符串必须以’\0’结束,否则无法追加数据
目标空间必须有足够的大,能容纳下源字符串的内容,否则编译器会提示警告,也可以成功追加上去字符串,但是会覆盖掉原有地址的数据,是非常危险的行为
功能:
#define _CRT_SECURE_NO_WARNINGS#include#includeint main(){char arr1[20] = "hello";char arr2[] = "world";strcat(arr1, arr2);printf("%s\n", arr1);return 0;}
2.4strcat 的模拟实现
#define _CRT_SECURE_NO_WARNINGS#include#includechar* my_strcat(char* dest, const char* src){char* ret = dest;assert(dest && src);while(*dest != '\0'){dest++;}while (*dest++ = *src++){;}return ret;}int main(){char arr1[20] = "hollo";my_strcat(arr1, "world");printf("%s\n", arr1);return 0;}
该函数要避免自己给自己追加数据,因为开始追加时,第一个字符会将零终止符覆盖掉,当全部字符都追加完之后,没有零终止符了,出现死循环
2.5strcmp
int strcmp ( const char* str1, const char* str2 );
功能:
比较两个字符串
将 C 字符串str1与 C 字符串str2 进行比较
此函数开始比较每个字符串的第一个字符。如果它们彼此相等,则继续比较下一对,直到字符不同或达到终止空字符
此函数执行字符的二进制比较
2.6 strcmp的模拟实现
#define _CRT_SECURE_NO_WARNINGS#includeint my_strcmp(const char* str1, const char* str2){while (*str1 == *str2){if (*str1 == '\0')return 0;str1++;str2++;}return (*str1 - *str2);}int main(){char arr1[] = "abc";char arr2[] = "abc";int ret = my_strcmp(arr1, arr2);if (ret == 0)printf("==\n");if (ret > 0)printf(">\n");if (ret < 0)printf("<\n");return 0;}
my_strcmp函数的思路是,让两个字符串从第一对开始比较,如果相等则都自增实现下一对的比较,如果比较到最后,第一个字符串结束,即出现零终止符,那么返回0,两字符串相等;如果在出现零终止符之前有一对不相等,那么返回两字符串相减的值,可以比较出大小
3.长度受限制的字符串函数介绍
3.1 strncpy
char * strncpy ( char * destination, const char * source, size_t num )
功能:
拷贝num个字符从源字符串到目标空间
如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个
#define _CRT_SECURE_NO_WARNINGS#include#includeint main(){char arr1[10] = "abcde";char arr2[] = "fghi";strncpy(arr1, arr2, 7);printf("%s\n", arr1);return 0;}
3.2 strncpy的模拟实现
char * __cdecl strncpy ( char * dest, const char * source, size_t count ){ char *start = dest; while (count && (*dest++ = *source++) != '\0') /* copy string */ count--; if (count) /* pad out with zeroes */ while (--count) *dest++ = '\0'; return(start);}
3.3strncat
char * strncat ( char * destination, const char * source, size_t num );
功能:
将源的第一个字符追加到目标,以及终止空字符
如果源中字符串的长度小于num,则仅重复终止空字符之前的内容#define _CRT_SECURE_NO_WARNINGS#include#includeint main(){char arr1[10] = "abcde";char arr2[] = "fghi";strncat(arr1, arr2, 3);printf("%s\n", arr1);return 0;}
该函数追加完要求的字符后会自动追加一个零终止符保证追加完后的数据仍然是一个字符串
3.4 strncat的模拟实现
char * __cdecl strncat ( char * front, const char * back, size_t count ){ char *start = front; while (*front++) ; front--; while (count--) if ((*front++ = *back++) == 0) return(start); *front = '\0'; return(start);}
3.5strncmp
int strncmp ( const char * str1, const char * str2, size_t num );
功能:比较两个字符串的字符
将 C 字符串str1的数目与 C 字符串str2的数目进行比较
此函数开始比较每个字符串的第一个字符。如果它们彼此相等,则继续比较下一对,直到字符不同,直到达到终止空字符,或者直到两个字符串中的数字字符匹配,以先发生者为准
#define _CRT_SECURE_NO_WARNINGS#include#includeint main(){char arr1[] = "abcde";char arr2[] = "fghi";int ret = strncmp(arr1, arr2, 3);printf("%d\n", ret);return 0;}
4.字符串查找
4.1 strstr
char* strstr ( const char* str1, const char* str2);
功能:
查找子串的字符串函数,返回指向str1中第一次出现的str2的指针,如果str2不是str1的一部分,则返回空指针
#define _CRT_SECURE_NO_WARNINGS#include#includeint main(){char arr1[] = "chencli";char arr2[] = "enc";char* ret = strstr(arr1, arr2, 3);if (ret == NULL)printf("字串不存在");elseprintf("找到字串为:%s\n", ret);return 0;}
4.2 strstr的模拟实现
找字串有两种情况,如下图:
情况2中,bbc和bc需要多次匹配
第一次比较:s1!=s2,p++,指向b,s1被赋值指向b,s2被复制为str2
第二次比较:s1==s2相等,s1++,s2++
第三次比较:s1!=s2,跳出while循环,p++,指向第二个b,s1被赋值指向第二个b;s2被赋值为str2
第四次比较:s1==s2,s1++,s2++
第五次比较:s1==s2,返回p,查找子串完成
#define _CRT_SECURE_NO_WARNINGS#include#includechar* my_strstr(const char* str1, const char* str2){assert(str1 && str2);const char* s1 = str1;const char* s2 = str2;const char* p = str1;while (*p){s1 = p;s2 = str2;while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2){s1++;s2++;}if (*s2 == '\0')return p;p++;}return NULL;}
4.3strtok
char* strtok ( char* str, const char* sep );
sep参数是个字符串,定义了用作分隔符的字符集合
第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记
strtok函数找到str中的下一个标记,并将其用\0结尾,返回一个指向这个标记的指针(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改)
strtok函数的第一个参数不为NULL,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置
strtok函数的第一个参数为NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记
如果字符串中不存在更多的标记,则返回NULL指针
#define _CRT_SECURE_NO_WARNINGS#include#includeint main(){const char* sep = "@.";char email[] = "1234567@qq.com";char cp[30] = { 0 }; strcpy(cp, email);//strtok函数找到str中的下一个标记,并将其用\0结尾,返回一个指向这个标记的指针//(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。) char* ret = strtok(cp, sep);//strtok函数的第一个参数不为NULL,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置printf("%s\n", ret); ret = strtok(NULL, sep);//strtok函数的第一个参数为NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记printf("%s\n", ret);ret = strtok(NULL, sep);printf("%s\n", ret);//如果字符串中不存在更多的标记,则返回NULL指针return 0;}
5.错误信息报告
5.1strerror
char* strerror ( int errnum );
功能:
返回错误码,所对应的错误信息
C语言的库函数执行失败的时候都会设置错误码
#define _CRT_SECURE_NO_WARNINGS#include#includeint main(){printf("%s\n", strerror(0));printf("%s\n", strerror(1));printf("%s\n", strerror(2));printf("%s\n", strerror(3));printf("%s\n", strerror(4));return 0;}
#define _CRT_SECURE_NO_WARNINGS#include#includeint main(){FILE* pf = fopen("test.text", "r");if (pf == NULL){printf("%s\n", strerror(errno));return 1;}else{//}return 0;}
errno是c语言设置的一个全局的错误码存放的变量
当没有test.txt被读时,errno就会更新
6.内存操作函数
6.1 memcpy
void* memcpy ( void* destination, const void* source, size_t num );
功能:
复制内存块
将num字节值从源指向的位置直接复制到目标指向的内存块
源指针和目标指针所指向的对象的基础类型与此函数无关结果是数据的二进制副本
该函数不检查源中是否有任何终止空字符 – 它始终精确地复制数字字节注意事项:
函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置
这个函数在遇到’\0’的时候并不会停下来
如果source和destination有任何的重叠,复制的结果都是未定义的,为避免溢出,目标参数和源参数所指向的数组的大小应至少为num个字节,并且不应重叠(对于重叠的内存块,memmove是一种更安全的方法)
6.2 memcpy的模拟实现
#define _CRT_SECURE_NO_WARNINGS#include#includevoid* my_memcpy(void* dest,const void* src, size_t num){assert(dest && src);void* ret = dest;while (num--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}return ret;}int main(){char arr1[100] = "abcdef";char arr2[100] = { 0 };my_memcpy(arr2, arr1,5);printf("%s\n", arr2);return 0;}
函数参数的类型是void*型的,因此可以复制不同元素类型的数组
该函数不能用于内存空间上有重叠的数据
6.3memmove
void* memmove ( void* destination, const void* source, size_t num );
功能:
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的
如果源空间和目标空间出现重叠,就得使用memmove函数处理
6.4 memmove的模拟实现
当需要处理的数据空间上有重叠时,往往有时候需要从前往后拷贝,有时候从后往前拷贝
分析如图:
destsrc
void* my_memmove(void* dest, const void* src, size_t num){assert(dest && src);void* ret = dest;if (dest < src){//前向后拷贝while(num--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}}else{//后向前拷贝while(num--){*((char*)dest + num) = *((char*)src + num);}}}
6.5memset
功能:
填充内存块
将ptr所指向的内存块的前一个字节数设置为指定值(解释为无符号字符)
#define _CRT_SECURE_NO_WARNINGS#include#includeint main(){char arr[] = "hello world";memset(arr + 6, 'x', 3);printf("%s\n", arr);return 0;}
6.6 memcmp
功能:
比较两个内存块
将ptr1所指向的内存块的前num个字节与ptr2指向的第一个字节数进行比较,如果它们都匹配,则返回零,或者如果不匹配,则返回与零不同的值,表示哪个值更大
请注意,与strcmp不同,该函数在找到空字符后不会停止比较
该组内存操作函数和字符串操作函数不同在于字符串操作函数只能作用于字符串,该内存操作函数可以作用于任意类型的数据