学习库函数的模拟实现之前,要先了解两个这两个
const关键字和assert()库函数
1.对于const关键字,这位博主讲的十分详细.
2.assert函数简单介绍.
函数名:断言函数
头文件:
#include <assert.h>
作用:
判断参数表达式的真假,如果为假就会终止程序,打印错误信息.
对于程序员这是一个非常友好的函数,可以减少出错的可能性,由于会返回错误到具体的哪一行,也就省去了调试的成本.
assert()中的参数表达式为真时,什么也不会发生,在release版本中,assert会被优化掉.
推荐查询库函数的网站:
一、strlen函数
函数原型:
函数作用:
用于计算字符串的长度.(输入字符串首地址,返回字符串长度).
头文件
#include <string.h>
函数参数:
参数 | 意义 |
const char * str | 要计算长度的字符串的首地址 |
返回值:
该字符串’\0’之前的字符个数,即不包括’\0’.
函数应用
#include <stdio.h> #include <string.h> int main() { char arr[100]={0}; printf("请输入字符串:\n"); scanf("%s", arr);//这里可以不用&arr,因为arr是数组名,是数组的首地址 size_t ret = strlen(arr); printf("字符串长度%zd", ret); return 0; }
模拟实现:
//模拟实现strlen #include <stdio.h> #include <assert.h> size_t my_strlen1(const char* str)//方法一:计数法 { assert(str); int count = 0; while (*str++) { count++; } return count; } size_t my_strlen2(const char* str)//方法二:递归方式实现 { assert(str); if (*str != '\0') { return 1 + my_strlen2(str + 1); } return 0; } size_t my_strlen3(const char* str)//方法三:指针-指针 { assert(str); char* ret = str; while (*ret) { ret++; } return ret - str; } int main() { char arr[] = { "abcdefg" }; printf("%d\n", my_strlen1(arr)); printf("%d\n", my_strlen2(arr)); printf("%d\n", my_strlen3(arr)); return 0; }
注意事项:
要保证被计算的字符串中包含’\0’,如果字符串没有包含’\0’,就会接续往后寻找,直到遇到’\0’,即产生随机值.
#include <stdio.h> #include <string.h> int main() { char arr[] = { 'a','b','c' }; int ret = strlen(arr); printf("%d", arr); return 0; }
运行结果:
随机值
小细节:
你知道size_t是什么吗?
你知道这段代码的运行结果是什么吗?
#include <stdio.h> #include <string.h> int main() { if (strlen("abc") - strlen("abcdef")) { printf("大于"); } else { printf("小于等于"); } return 0; }
运行结果:
大于
原因就是size_t
3-6虽然结果是-3,但是对于无符号整形来说,就是一个很大的正数了.
其实size_t的本质是无符号整形,毕竟一个字符串的长度是非负数,所以库函数的设计还是比较合理的.
typedef unsigned int size_t;
二、strcpy函数
函数模型
函数作用:
用于将源地址的字符串拷贝到目的地址.
函数名:字符串拷贝函数
头文件:
#include <string.h>
函数参数:
参数 | 意义 |
参数1:char * destination | 目的地址: 指向要在其中复制内容的目标数组的指针。 |
参数2:const char * source | 源地址:要复制的 C 字符串。 |
返回值:拷贝完成后目的地址.
🌰栗子
#include<stdio.h> #include <string.h> int main() { char arr1[20] = "xxxxxxxxxxxxxxx" ; char arr2[] = "hello csdn" ; printf("%s", strcpy(arr1, arr2)); return 0; }
运行结果:
hello csdn
为了更加方便的观察拷贝的结果,我们可以通过调试,在监视窗口观察arr1的存放结果.
很明显,strcpy函数是从源地址的首地址处开始,直到遇到’\0’,最后将’\0’也拷贝过去.
为了验证是否真的只是到’\0’而不是所有数据.
将代码改成:
#include<stdio.h> #include <string.h> int main() { char arr1[20] = "xxxxxxxxxxxxxxx" ; char arr2[] = "hello\0 csdn" ; printf("%s", strcpy(arr1, arr2)); return 0; }
验证结果是正确的.
模拟实现:
#include <stdio.h> //destination目的地 source源地址 char* my_strcpy1(char* dest, char* src)//第一种 { char* ret = dest;//记录首地址 while (*src != '\0') { *dest = *src; dest++; src++; } *dest++ = *src++; return ret; } char* my_strcpy2(char* dest, char* src)//优化1,代码变得更加简洁 { char* ret = dest; while (*dest++=*src++) { ; } return ret; } #include <assert.h> char* my_strcpy3(char* dest, const char* src) { assert(dest && src);//防止传入空指针 char* ret = dest; while (*dest++ = *src++)//刚好最后将'\0'覆盖过去 { ; } return ret; } int main() { char arr1[] = { "HELLO CSDN!" }; char arr2[20] = { "xxxxxxxxxxxxxxxxxx" }; char* p = NULL;//空指针 printf("%s\n", my_strcpy1(arr2, arr1)); //printf("%s\n", my_strcpy2(arr2, arr1)); //printf("%s\n", my_strcpy3(arr2, arr1)); return 0; }
注意事项:
- 源字符串必须以 ‘\0’ 结束。
- 会将源字符串中的 ‘\0’ 拷贝到目标空间。
- 目标空间必须足够大,以确保能存放源字符串
三、strcat函数
函数模型:
函数作用:
将源字符串(source)的副本(复制一份)追加到目标字符串(destination)。目标中的终止空字符(‘\0’)被源字符串的第一个字符覆盖,并且在目标中由两者串联形成的新字符串的末尾包含一个空字符(‘\0’)。
函数参数
参数 | 意义 |
参数1:char * destination | 目的地址: 指向要在后面追加字符串的目标数组的指针。 |
参数2:const char * source | 源地址:要追加的 C 字符串。 |
函数应用:
#include <stdio.h> #include <string.h> int main() { char arr1[20] = "HELLO "; char arr2[] = "CSDN!"; strcat(arr1,arr2); printf("%s", arr1); return 0;
运行结果:
HELLO CSDN!
模拟实现:
#include <stdio.h> #include <assert.h> my_strcat(char* dest, const char* src) { assert(dest && src);//防止传入空指针 char* ret = dest; while (*dest) { dest++; } while (*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = "HELLO "; char arr2[] = "CSDN!"; my_strcat(arr1,arr2); printf("%s", arr1); return 0; }
注意事项:
- 源字符串必须以 ‘\0’ 结束。
- 目标空间必须有足够的大,能容纳下源字符串的内容。
- 目标空间必须可修改。
重点
- 不可自己追加自己,否则不知道什么时候停下来,因为’\0’被改掉了.
四、strcmp函数
函数模型:
头文件:
#include <stdio.h>
函数作用:
将 C 字符串 str1 与 C 字符串 str2 进行比较。
此函数开始比较每个字符串的第一个字符。如果它们彼此相等,则继续向后比较,直到字符不同或达到终止空字符。
返回值: 返回一个整数值,该值指示字符串之间的关系
返回值 | 意义 |
<0 | 第一个不匹配的字符的str1的值小于str2中的值 |
0 | 两个字符串的内容相等 |
>0 | 第一个不匹配的字符的str1的值大于于str2中的值 |
函数参数:
参数 | 意义 |
参数1:const char * str1 | 指向字符串1的指针 |
参数2 const char * str2 | 指向字符串2的指针 |
函数应用:
实现一个设置密码,只有三次机会.
#define MAX 3 #include <stdio.h> #include <string.h> int main() { char password[] = "1433223"; char input[20]; for (int i = MAX; i > 0; i--) { printf("请输入密码:\n你还有%d次机会:\n", i); scanf("%s", input); if (password== input) { printf("恭喜你,密码正确!!!"); break; } else { printf("很遗憾,密码错误\n"); } } return 0; }
运行结果:
请输入密码:
你还有3次机会:
123
很遗憾,密码错误
请输入密码:
你还有2次机会:
1433223
很遗憾,密码错误
请输入密码:
你还有1次机会:
1234
很遗憾,密码错误
当密码输入正确时,也显示密码错误,因为字符串不能直接用"=="进行比较,而要借助库函数,strcmp函数进行比较.
#define MAX 3 #include <stdio.h> #include <string.h> int main() { char password[] = "1433223"; char input[20]; for (int i = MAX; i>0; i--) { printf("请输入密码:\n你还有%d次机会:\n",i); scanf("%s", input); if (strcmp(password,input)==0) { printf("恭喜你,密码正确!!!"); break; } else { printf("很遗憾,密码错误\n"); } } return 0; }
运行结果:
请输入密码:
你还有3次机会:
123
很遗憾,密码错误
请输入密码:
你还有2次机会:
123456
很遗憾,密码错误
请输入密码:
你还有1次机会:
1433223
恭喜你,密码正确!!!
模拟实现:
int my_strcmp(const char* str1, const char* str2) { assert(str1 && str2); while (*str1 == *str2) { if (*str2 == '\0')//循环条件是,字符串的元素相等,如果一方是'\0',说明两者都是'\0'. { return 0;//返回0,说明两者相等 } str1++; str2++; } //跳出循环,说明有字符元素不相等 if (*str1 > *str2)//比较哪方的字符更大 { return 1; } else { return -1; } }
注意事项:
此函数执行字符的二进制比较,实际比较的是字符的ASCII码值,并不是按长度比较.
而是比较第一次出现不同字符的大小.
在vs编译器下,
第一个字符串大于第二个字符串,返回1;
相等返回0
第一个字符串小于第二个字符串,返回-1;
而与库函数本身有点小的差别,库函数要求返回>0 0和<0的数即可.