目录
🌇前言
🌇正文
🌆字符串函数
🌉长度不可控的字符串函数
🌃strlen 长度统计
🌃strcpy 拷贝
🌃strcmp 比较
🌃strcat 追加
🌉长度可控的字符串操作函数
🌃strncpy 可控拷贝
🌃strncmp 可控比较
🌃strncat 可控追加
🌉特殊字符串函数
🌃strstr 寻找
🌃strtok 分割
🌃strerror 报错
🌆字符分类函数
🌃isdigit 十进制判断
🌃isxdigit 十六进制判断
🌃isupper 大写判断
🌃islower 小写判断
🌃toupper 转为大写
🌃tolower 转为小写
🌆内存函数
🌃memcpy 拷贝
🌃memmove 移动
🌃memcmp 比较
🌃memset 设置
🌇总结
🌇前言
这是牛客网上的一道简单题:判断输入字符是否为字母,一般的解决方法是通过ASCII码判断,不过这样做的话判断表达式较长,此时我们可以利用C语言中的库函数isalpha(判断是否为字母) 来完成这个题目,不仅代码量少,而且通俗易懂。要实现这种效果,就需要学习C语言中的各种库函数,而本文会列出大多数字符串函数和内存函数的使用及其实现,如果你想学习C语言库函数或对字符串、内存有好奇之心,不妨仔细来看看吧!🎉🎉🎉
此文章分为三部分:字符串函数、 字符分类函数、内存函数。
通过库函数简化后的代码
🌇正文
首先我们从字符串函数开始介绍,顾名思义,字符串函数就是为字符串设计的函数,我们比较熟悉的有字符串长度统计函数 strlen、字符串比较函数 strcmp,除此之外还有很多实用的字符串函数,比如字符串追加、字符串分割、字符串寻找等等,话不多说,让我们直接进入主题:
🌆字符串函数
🌉长度不可控的字符串函数
下面介绍的是对目标字符串操作长度不可控的函数,使用场景相对有限。
🌃strlen 长度统计
strlen 是用来统计字符串长度的一个库函数,因为字符串有个重要特征:以'\0'作为结束标志。strlen 正是根据这一特点,从首地址开始,逐个比对统计,得出此字符串的长度。当然如果没有结束标记,strlen 就会一直往后找,得出一个随机值。
这是 strlen 的标准格式
strlen 计算字符串长度
使用注意事项:
- strlen 统计的是 \0 之前出现的字符个数
- 使用时,必须包含结束标志
- 返回值是 size_t (unsigned int 无符号整型)
- 模拟实现 strlen
这个函数的工作原理我们已经清楚了,可以试着模仿库函数的方式,写出一个属于自己的 strlen。
既然是模仿库函数,那么在返回类型、参数类型等方面要和库函数一致,在统计长度前,我们可以使用一个临时指针记录起始位置,当我们的源指针指向结束标志时,循环结束,将两个指针相减,就能得到元素个数(关于指针 - 指针得到元素个数) ,也就是字符串长度。下面来看看具体代码实现吧:
//strlen 计算字符串长度 size_t myStrlen(const char* p) { assert(p);//断言,防止空指针 char* tmp = p;//记录起始位置 while (*p) { p++;//在循环内指向+1操作,避免位置出错 } return (size_t)(p - tmp);//指针 - 指针得到元素个数 } int main() { char* pa = "Hello World!"; //size_t len = strlen(pa); //printf("库函数实现结果:\n%zu\n", len); size_t len = myStrlen(pa);//此时调用我们写的函数 printf("模拟函数实现结果:\n%zu\n", len); return 0; }
同样的,我们使用之前的示例来验证此函数的可行性 ,可以看到结果与库函数一致。
🌃strcpy 拷贝
字符串拷贝需要两个字符串,字符数组 dest(目标) 与字符串 src(源),strcpy 中只需要这两个参数,即把源字符串内容拷贝到目标字符数组中(源字符串中的结束标志也会拷贝),其中要确保字符数组 dest 可改变,数组空间也要足够大,不然装不下源字符串就尴尬了。
strcpy 标准格式
使用注意事项:
- 源字符串中必须包含 \0
- 源字符串中的 \0 会拷贝到目标字符数组中
- 目标空间必须足够大,能够装下源字符串
- 目标空间必须是可修改的
- 模拟实现 strcpy
同样的,我们可以对这个函数进行模拟实现,拷贝的本质就是赋值,当源字符串中的首元素拷贝到目标字符数组中后仍然位于首位置,也就是说两个字符串元素拷贝位置是同步的,既然源字符串中的结束标志也要拷贝过去,那么我们就可以将其和赋值写进一个循环判断条件中(这样会构成一段非常奇妙的代码),这样一来我们整个程序的可读性就很不错了。
//strcpy 字符串拷贝 char* myStrcpy(char* dest, const char* src) { assert(dest && src);//断言 char* tmp = dest;//记录起始位置 //当*src 为结束标志并赋给 *dest时,整体为假, //循环终止,目标数组也拿到了结束标志 while (*dest++ = *src++) { ;//空语句 } return tmp;//返回起始地址 } int main() { char arr1[20] = "xxxxxxxxx"; char arr2[] = "Hello!"; //printf("库函数实现结果:\n%s\n", strcpy(arr1, arr2)); printf("模拟函数实现结果:\n%s\n", myStrcpy(arr1, arr2)); return 0; }
当然,我们的模拟函数也能实现需求
🌃strcmp 比较
字符串比较函数是从首字符开始比较,通过字符对应的ASCII码值对比,就能知道大小,如果str1>str2,返回1,如果str1<str2,返回-1,如果两个字符串相等,则返回0。因为比较并不需要改变值,所以使用常量字符串也能比较。
strcmp 标准格式
strcmp 的返回值
使用注意事项:
- 字符串大小比较时,与长度无关
- 从首字符开始,逐字符比较
- 通过字符对应的ASCII码值做对比
- 模拟实现 strcmp
我们可以通过指针的移动来模拟实现这个函数,即指向 str1 的指针 dest、指向 str2 的指针 src,对两个指针解引用后的值进行比较,如果相同就同时向后偏移,直到找到不同的值或移动到结束标志处停止,最后再分情况确定返回值就行了。
//strcmp 字符串比较 int myStrcmp(const char* dest, const char* src) { assert(dest && src);//断言 //当找到不同数或移动到\0处时,循环停止 while (*dest == *src && (*dest || *src)) { dest++;//没有找到不同的元素 src++;//需要向后偏移 } //分情况判断,确定返回值 if (*dest - *src > 0) return 1; else if (*dest - *src < 0) return -1; else return 0; } int main() { char* str1 = "BATZ"; char* str2 = "BAT"; printf("库函数实现结果:\n%d\n", strcmp(str1, str2)); //printf("模拟实现函数结果:\n%d\n", myStrcmp(str1, str2)); return 0; }
使用模拟函数通过测试用例:
🌃strcat 追加
追加,就是在目标字符数组的末尾(\0处)添加源字符串的值,比如目标字符串数组中为abcd,源字符串为1234,经过追加后,字符数组就变为了abcd1234。值得一提的是,strcat 无法自己给自己追加,因为在追加过程中,目标字符数组结束标志会被覆盖掉,导致源字符串(其实就是目标字符,因为是自己给自己追加)中的结束标志也消失了,追加过程会无法停止。
strcat 标准格式
使用注意事项:
- 源字符串和目标字符数组中都必须有\0
- 目标空间必须足够大
- 目标空间必须可修改,所以是字符数组
- 模拟实现 strcat
既然是在目标字符数组的末尾处追加字符,就需要把指向首地址处的指针 dest 移向尾地址,当然在移动前要保存此地址,将此时的尾地址看作首地址2,将源字符串中的元素从此处开始拷贝至目标字符数组中,这样就完成了追加的操作,最后再返回之前记录的首地址就行了。
//strcat 字符串追加 char* myStrcat(char* dest, const char* src) { assert(dest && src);//断言 char* tmp = dest;//记录目标字符数组首地址 while (*dest) { dest++;//将指针dest移动至尾元素处 } //类似 strcpy 拷贝操作 while (*src) { //判断条件用 *src就行了 *dest++ = *src++;//确保源字符串中的每个元素都能追加上 } return tmp;//返回首地址 } int main() { char arr1[20] = "ABCD"; char arr2[] = "1234"; //printf("库函数实现结果:\n%s\n", strcat(arr1,arr2)); printf("模拟函数实现结果:\n%s\n", myStrcat(arr1, arr2)); return 0; }
🌃strncmp 可控比较
同样的,strncmp 也能控制比较长度,当然控制长度不能超过源字符串的长度,不然是无意义的
strncmp 标准格式
返回值与 strcmp 完全一致
使用注意事项:
- 与 strcmp 基本一致
- 控制比较字节数不能为负数
- 模拟实现 strncmp
这个模拟实现也比较简单,大体思路与 strcmp 的模拟一样,只是循环判断条件变为了 k 和 *dest (当对比到结束标志处,循环也会停止)。
//strncmp n个字符串比较 int myStrncmp(const char* dest, const char* src, size_t k) { assert(dest && src);//断言 //为何使用前置--? //因为这样能有效避免多判断一次的情况 while (--k && *dest == *src) { dest++;//确保每位都能对比到 src++; } //分情况返回 if (*dest - *src > 0) return 1; else if (*dest - *src < 0) return -1; else return 0; } int main() { char* str1 = "BATZ"; char* str2 = "BAT"; //printf("库函数实现结果:\n%d\n", strncmp(str1, str2, 3)); printf("模拟函数实现结果:\n%d\n", myStrncmp(str1, str2, 3)); return 0; }
🌃strncat 可控追加
可控追加,旨在控制源字符串中字符追加数,比如目标字符数组为abcd,源字符串为1234,我们传递字节数为2,当追加结束后,目标字符数组变为abcd12,同所有可控家族成员一样,strncat 也会自动添加结束标志 \0。因此利用 strncat 自己给自己追加,能够很好的完成任务,避免结束标志吞噬问题。
strncat 标准格式
使用注意事项:
- 目标字符数组中必须有\0
- 目标空间必须足够大
- 目标空间必须可修改
- 源字符串中可以不包含\0
- 模拟实现 strncat
代码大体与模拟 strcat 的一致,只不过有两个地方需要注意:1.循环判断条件 2.最后 \0 的添加
//strncat n个字符串追加 char* myStrncat(char* dest, const char* src, size_t k) { assert(dest && src);//断言 char* tmp = dest;//记录起始位置 //使指针移向目标字符数组的末尾处 while (*dest) { dest++; } //判断条件为k,即传入的控制字节数 while (k--) { *dest++ = *src++;//确保每个元素都能追加 } *dest = '\0'; return tmp;//返回目标字符数组的起始地址 } int main() { char arr1[20] = "xxxxxxx"; char arr2[] = "Hello World!"; //printf("库函数实现结果:\n%s\n", strncat(arr1, arr2, 5)); printf("模拟函数实现结果:\n%s\n", myStrncat(arr1, arr2, 5)); return 0; }
🌉特殊字符串函数
🌃strstr 寻找
字符串寻找函数,作用是在目标字符串中寻找是否出现过源字符串,如果出现则返回第一次其在目标字符串中第一次出现的地址,如果没有出现,则返回一个空指针。
ststr 标准格式
使用注意事项:
- 只要传入的字符串地址就行了
- 这个函数没有什么需要特别注意的事项