目录
一、库函数的模拟实现
1、模拟实现strlen
方法一:
方法二 :
2、模拟实现strcpy
3、模拟实现strcat
4、strcmp函数的模拟实现
5、模拟实现库函数strstr
6、模拟实现库函数 memcpy
7.模拟实现库函数memmove
在C语言编程中,我们会使用到很多库函数,库函数是将函数封装入库,供用户使用的一种方式。首先,我们来看看有哪一些函数:
字符串说明 | 字符串类型 |
---|---|
求字符串长度 | strlen |
长度不受限制的字符串函数 | strcpy,strcat,strcmp |
长度受限制的字符串函数 | strncpy,strncat,strncmp |
字符串查找 | strstr,strtok |
错误信息报告 | strerror |
内存操作函数 | memcpy,memmove,memset,memcmp |
在这里,我推荐一个网站https://cplusplus.com,在这里面可以查看每个库函数的具体结构和组成框架 。
strlen是我们计算字符串长度时使用的一个库函数,知道遇到空字符串时停止计算,到不包括空结束字符串。
以下是关于strlen()函数的声明:
size_t strlen(const char *str)
注意:函数的返回值是size_t,是无符号的。
首先我的思路是将字符传到我自己模拟的函数里去,然后进行字符的逐个统计,再进行计数,直到遇到‘\0’停下,具体代码如下:
#include
#include
int my_strlen(const char* str)
{
int count = 0;
assert(str); //非空指针
while (*str != '\0')
{
str++;
count++;
}
return count;
}
int main()
{
char arr[] = "abcdef";
int len = my_strlen(arr);
printf("%d", len);
return 0;
}
注意:
1、代码中const修饰的指针类型表示指针指向的内容不可更改 。2、assert语句称为断言,它的本质就是一个宏,宏之中是一个表达式。
“断言”:
如果表达式为真,断言通过,进行之后的逻辑。
如果表达式为假,断言失败,程序主动停止,直接退出。
像我这个程序,assert的作用就是用来检验传递过来的是否为非空指针。
还有一种思路是用递归来做的,具体代码如下:
#include
#include
int my_strlen(const char * str)
{
if(*str == '\0')
return 0;
else
return 1 + my_strlen(str+1);
}
int main()
{
char arr[] = "abc";
int ret = my_strlen(arr);
printf("%d\n", ret);
return 0;
}
库函数 strcpy把 一个字符串的内容复制到另一个字符串上。
声明如下:
char* strcpy(char * destination, const char * source )
实现逻辑就是首先我们要把字符串传入我们的模拟实现函数中,注意形参我们要以char*的形式接收,因为我们的实参是以数组名的形式传入的,传入的是首元素的地址。然后我们就可以通过使用while语句来实现字符之间的复制,判断的标准自然就是des解引用后是否是’\0’,如果不是我们则继续复制,是的话我们就跳出该地址。但是由于在循环过程中我们的str的地址是不断在变化的,所以我们最好把初始的str地址存放在另一个地址中。具体实现如下:
char my_strcpy(char* str, const char* des) {
assert(str);
assert(des);
char* ret = des;
while (*str++ =*des++) {
;
}
return ret;//返回的是目标函数的首地址
}
int main()
{
char arr1[10] = {0};
char arr2[] = "holle";
my_strcpy(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
还需要注意一点:
目标空间必须足够大,以确保能存放源字符串
函数主要用来字符串追加与拼接功能
char* strcat(char* destination,const char* source)
实现功能也很简单,把des指向的字符串追加到str字符的结尾,跟strcpy很相似
char my_strcat(char* str,char*des) {
assert(str && des);
char* ret = des;
while (*str != '\0') {//找到目标字符串的‘\0’
str++;
}
//追加源字符串到‘\0’
while (*str++ = *des++) {
;
}
return ret;
}
int main() {
char arr1[20] = "holle ";
char arr2[] = "world";
my_strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
strcmp函数实现功能是将字符串str1与字符串str2进行比较
int strcmp(const char*str1,const char*str2)
因为返回值是int类型,所以,第一个字符串大于第二个字符串,则返回大于0的数字。第一个字符串等于第二个字符串,则返回0。第一个字符串小于第二个字符串,则返回小于0的数字。
具体实现如下:
#include
int my_strcmp(char* str1, char* str2)
{
assert(str1 && str2);
while (*str1 == *str2) //先看首字符是否相等,如果相等,继续比下去
{
if (*str1 == '\0')//判断是否比完所有字符,返回0,说明两个字符相等
{
return 0;
}
else
{
str1++;
str2++;
}
}
//如果两个字符不相等,那就比大小,返回大于0的数则说明str1>str2,反之小于
return (*str1 - *str2);
}
int main()
{
char arr1[20] = "abcd";
char arr2[20] = "abc";
int ret=my_strcmp(arr1,arr2);
if (ret < 0)
{
printf("<\n");
}
else if (ret == 0)
{
printf("==\n");
}
else
{
printf(">\n");
}
return 0;
}
函数的目的是在一个字符串中查找其他子字符串
char* strstr(const char* str1,const char*str2)
strstr函数的实现逻辑相较于前面几个C库函数会复杂一点点。
首先我们需要传入两个字符串,然后将各自的初始地址先分别存入另外的指针。然后我们就可以利用循环语句,以是否遇到结束标志’\0’为标准。然后我们分别从两个字符串的第一个字符开始寻找,这里往下继续寻找的条件要考虑全面。无论是哪个字符串都不可以是’\0’,并且需要两个字符是相等的。就这样不断寻找下去,如果找不到则将我们传入的第一个字符串的首元素地址向下推移一个字符,以此类推,直到知道和传入的第二个字符串相同的一段字符,然后输出它,如果直到最后都没有相同字符串部分,则会返回空指针。
代码如下:
char* 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 (char*)p;
}
p++;
}
return NULL;//找不到
}
int main()
{
char arr1[] = "abcdefabcdef";
char arr2[] = "cdq";
char* p = my_strstr(arr1, arr2);
if (p == NULL)
{
printf("不存在\n");
}
else
{
printf("%s\n", p);
}
return 0;
}
这个函数是内存操作函数,可以对任意类型的数据进行操作,这个函数的用途也是用来复制字符串的,具体声明如下:
void *memcpy(void *str1, const void *str2, size_t n)
先来给大家解释一下这些个参数的含义:
str1 – 指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。
str2 – 指向要复制的数据源,类型强制转换为 void* 指针。
n – 要被复制的字节数。
memcpy的实现逻辑和strcpy有所不同。首先我们需要传入我们上面讲到的几个参数,具体的一些注释我们放在了下面的模拟实现代码中。大致的思路就是利用循环语句,将str1中的n个字节复制到str2的n个字节中,这里的n个字节就是我们之前传入的num。
void* my_memcpy(void* str,void* des,int num) {//因为memcpy是内存拷贝函数,所以必须什么类型都能接收,所以此处用void*做参数类型和返回值类型
assert(str && des);
void* ret = des;
while (num--) {
*(char*)str = *(char*)des;//最少单位是char,强制转换最小类型,然后一个一个字节替换
des = (char*)des + 1; //void*类型不能进行++操作,这里也强制转换类型.
str = (char*)str + 1;
}
return ret;
}
void print(int arr[], int sz) {
int i = 0;
for (i = 0; i < sz; i++) {
printf("%d", arr[i]);
}
printf("\n");
}
int main() {
int arr1[10] = { 0 };
int arr2[] = { 0,1,2,3,4,5,6,7,8,9 };
my_memcpy(arr1, arr2, 20);//此处n是字节数,20个字节也就是5个int型值
int sz = sizeof(arr1) / sizeof(arr1[0]);
print(arr1,sz);
}
先来看看memmove函数的声明:
void *memmove(void *str1, const void *str2, size_t n)
C 语言库函数 void *memmove(void *str1, const void *str2, size_t n) 从 str2 复制 n 个字符到 str1,但是在重叠内存块这方面,memmove() 是比 memcpy() 更安全的方法。如果目标区域和源区域有重叠的话,memmove() 能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,复制后源区域的内容会被更改。如果目标区域与源区域没有重叠,则和 memcpy() 函数功能相同。代码实现:
void* my_memmove(void* str, const void* des, int num) {
assert(str && des);
void* ret = des;
if (str < des)//比较两个地址的大小
{
//从前->后拷贝
while (num--) {
*(char*)str = *(char*)des;
des = (char*)des + 1;
str = (char*)str + 1;
}
}
else {
//从后->前拷贝
while (num--) {
*((char*)str + num) = *((char*)des + num);
}
}
}
void print(int arr[], int sz) {
int i = 0;
for (i = 0; i < sz; i++) {
printf("%d", arr[i]);
}
printf("\n");
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memmove(arr1 + 2, arr1, 20); //将1,2,3,4,5拷贝到3,4,5,6,7的位置上去
int sz = sizeof(arr1) / sizeof(arr1[0]);
print(arr1, sz);
return 0;
}