本篇博客主要内容是C语言常见的函数的模拟实现,我们可以自定义一个函数,拥有和原库函数相似效果。
重点关注以下几点:函数功能,返回值类型,参数类型 实现方式并不唯一~
strlen
【文档链接】
size_t strlen(const char *str)
功能:
计算字符串 str
的长度,直到空结束字符,但不包括空结束字符。
返回值:size_t
类型(无符号整数),返回字符串字符的个数
参数:const char *str
(字符串的首地址)
因为str指向内容在函数内部无需更改所以用const
修饰。
模拟实现:
方法一:计数器方法
#include <stdio.h>
#include <assert.h>
//1.计数器 实现strlen
size_t My_strlen(char* pc)
{
assert(pc);
int count = 0;//记录字符的个数
while (*pc)//一直递增直到遇到\0
{
count++;
pc++;
}
return count;
}
int main()
{
char str[] = "hello world";
int len = My_strlen(str);
printf("%d\n", len);//输出11
return 0;
}
方法二:指针相减方法
//2.指针-指针 实现strlen
#include <stdio.h>
#include <assert.h>
size_t My_strlen(char* pc)
{
assert(pc);
char* begin = pc;//储存起始地址
while (*pc)//找到\0的地址
{
pc++;
}
return pc - begin;//末尾地址减起始地址结果等于中间元素的个数
}
int main()
{
char str[] = "hello world";
int len = My_strlen(str);
printf("%d\n", len);//输出11
return 0;
}
方法三:递归方法
//strlen 模拟实现 3.递归
#include <stdio.h>
#include <assert.h>
size_t My_strlen(char* pc)
{
assert(pc);
if (*pc)//遇到终止字符\0停止递归
{
return 1 + My_strlen(pc+1);
}
return 0;
}
int main()
{
char str[] = "hello world";
int len = My_strlen(str);
printf("%d\n", len);//输出11
return 0;
}
strcpy
【文档链接】
char *strcpy(char *dest, const char *src)
功能:把 src
所指向的字符串复制到 dest
。
需要注意的是如果目标数组 dest
不够大,而源字符串的长度又太长,可能会造成缓冲溢出的情况。
返回值类型:char *
(写成char*
目的是可以链式访问)
参数:char *dest
(目标字符串的首地址), const char *src
(源字符串的首地址)
模拟实现
#include <stdio.h>
#include <assert.h>
char* My_strcpy(char* dest, char* src)
{
assert(dest && src);//断言:拦截空指针
char* ret = dest;//记录目标数组首地址,以便于作为返回值
while (*src)//一个一个字符复制源字符串,直到\0跳出循环
{
*dest = *src;
dest++;
src++;
}
*dest = *src;//把没有复制的\0复制过去
return ret;//返回目标数组首地址
}
int main()
{
char str1[] = "xxxxxxxx"
char str2[] = "Hello";
My_strcpy(str1, str2);//把str的内容复制到str1中
printf(str1);//输出Hello
return 0;
}
strcat
【文档链接】
char *strcat(char *dest, const char *src)
功能
把 src
所指向的字符串追加到 dest
所指向的字符串的结尾。
返回值
char*
类型 返回目标字符串的首地址。
参数
char *dest
目标字符串的首地址。
const char *src
源字符串的首地址。
模拟实现
//strcat 函数模拟实现
#include <stdio.h>
#include <assert.h>
char* My_strcat(char* dest, char* src)
{
assert(dest&&src);//断言:拦截空指针
char* ret = dest;//记录目标字符串的首地址,以便于返回
//找到'/0'
while (*dest)
{
dest++;
}
//替换 这一步类似于strcpy函数
while (*src)
{
*dest = *src;
dest++;
src++;
}
//拷贝'\0'作为字符串结束的标志
*dest = *src;
return ret;//返回目标字符串首地址
}
int main()
{
char str1[100] = "Hello ";
char str2[] = "World!";
My_strcat(str1, str2);
printf(str1);//打印Hello
return 0;
}
strcmp
【文档链接】
int strcmp(const char *str1, const char *str2)
**功能:**把 str1
所指向的字符串和 str2
所指向的字符串进行比较。(详细比较规则见: strcmp)
返回值
int
类型
-
如果返回值小于 0,则表示
str1
小于str2
。 -
如果返回值大于 0,则表示
str1
大于str2
。 -
如果返回值等于 0,则表示
str1
等于str2
。
参数
-
str1 – 要进行比较的第一个字符串。
-
str2 – 要进行比较的第二个字符串。
模拟实现
//strcmp函数的模拟实现
#include<stdio.h>
#include<assert.h>
int My_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);//断言:拦截空指针
while (*str1 == *str2)
{
if (*str1 == '\0')
{
return 0;//*str1与*str2一直相等至\0 说明两个字符串相等 返回0
}
//比较完一个字符就都移动一次
str1++;
str2++;
}
return *str1 - *str2;//比较两个字符或者数字大小 直接相减 如果>0(<0)说明*str1>*str2(*str1<*str2)
}
int main()
{
char str1[] = "abc";
char str2[] = "abad";
//str1 > str2
if (My_strcmp(str1, str2) > 0)
{
printf("str1 > str2");
}
else if (My_strcmp(str1, str2) == 0)
{
printf("str1 = str2");
}
else
{
printf("str1 < str2");
}
return 0;
}
strstr
【文档链接】
const char * strstr ( const char * str1, const char * str2 );
**功能:**查找str1
中是否有子字符串str2
,如果有返回第一次出现子字符串的位置,如果没有返回NULL
参数
const char * str1
被检索的字符串
const char * str2
可能出现在str1
中的子字符串
返回值
const char *
类型
如果存在:返回子字符串str2
第一次出现在str1
中的位置
如果不存在:返回空指针NULL
模拟实现
#include <stdio.h>
#include <assert.h>
//模拟实现strstr
//返回str1中str2作为子字符串第一次出现的地址
//找不到返回空指针
const char* My_strstr(const char* str1, const char* str2)
{
assert(str1&&str2);
char* pc1 = str1;//用于判断当前地址是不是子串的起始地址
char* pc11 = pc1;//用于跟随移动
char* pc2 = str2;//用于定位子串的起始位置
char* pc22 = pc2;//用于跟随移动
while (*pc1 != '\0')
{
pc11 = pc1;//初始化跟随移动指针的内容
pc22 = pc2;
while (*pc22&&*pc11&&*pc22 == *pc11 )//判断是否是子字符串
{
pc22++;
pc11++;
}
if (*pc22 == '\0')//如果遍历完了str2说明确实子字符串str2存在于str1
{
return pc1;//返回第一次出现的地址
}
pc1++;
}
if (*pc1 == '\0')//str1遍历完依旧没有相等
{
return NULL;//返回空指针
}
}
int main()
{
char str1[] = "abbcde";
char str2[] = "bce";
char str3[] = "cde";
if (My_strstr(str1, str2) == NULL)
{
printf("No\n");
}
else
{
printf("Yes\n");
}
if (My_strstr(str1, str3) == NULL)
{
printf("No\n");
}
else
{
printf("Yes\n");
}
return 0;
}
内存函数
memcpy
【文档链接】
void * memcpy ( void * destination, const void * source, size_t num );
**功能:**内存拷贝函数,从源地址 source
复制 num
个字节到目标地址 sdestination
。
参数
-
destination
– 指向用于存储复制内容的目标数组,类型强制转换为void*
指针。 -
source
– 指向要复制的数据源,类型强制转换为void*
指针。 -
num
– 要被复制的字节数。
返回值
类型void *
,目的地destination
被返回。
模拟实现
#include <stdio.h>
#include <assert.h>
//memcpy 模拟实现
void* My_memcpy(void* dest, const void* src, size_t num)
{
assert(dest && src);
void* ret = (char*)dest;//存储目的地地址,用作函数返回值
while (num--)//循环num次
{
*(char*)dest = *(char*)src;//强转成char*解引用时访问一个字节,并拷贝过去
(char*)dest = (char*)dest + 1;//dest移动一字节
(char*)src = (char*)src+1;//src移动一字节
}
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[20];
My_memcpy(arr2, arr1, 10*sizeof(int));//拷贝arr1中10个整形内容(40字节)到arr2
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr2[i]);//输出1 2 3 4 5 6 7 8 9 10
}
return 0;
}
注意事项:在C语言标准中,memcpy无法处理重复的内存区域,比如
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[20];
memcpy(arr1+2, arr1, 20);
//我们想输出的是 1 2 1 2 3 4 5 8 9 10
//但是有内存重叠部分 编译器可能无法做到拷贝重叠的内存,需要用到memmove
memmove
【文档链接】
void * memmove ( void * destination, const void * source, size_t num );
**功能:**从 source
复制 num
个字节到destination
,但是在重叠内存块这方面,memmove()
是比 memcpy()
更安全的方法。如果目标区域和源区域有重叠的话,memmove()
能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,复制后源区域的内容会被更改。如果目标区域与源区域没有重叠,则和 memcpy()
函数功能相同。
参数:
-
destination
– 指向用于存储复制内容的目标数组,类型强制转换为void*
指针。 -
source
– 指向要复制的数据源,类型强制转换为void*
指针。 -
num
– 要被复制的字节数。
返回值
void
类型,目的地destination
被返回
模拟实现
//memmove 函数模拟实现
//有重叠的情况下
//低地址 拷贝到 高地址 (src < dest)反向复制
//高地址 拷贝到 低地址 (src > dest)正向复制
#include <stdio.h>
#include <assert.h>
void* My_memmove(void* dest,void*src,size_t num)
{
assert(dest && src);
void* ret = dest;//存放目的地的地址,用作函数返回值
//src < dest 反向复制
if (src < dest)
{
while (num--)//判断完之后已经-1了
{
*((char*)dest + num) = *((char*)src + num);//逐字节复制
}
}
//src > dest 正向复制
else
{
while (num--)
{
*((char*)dest) = *((char*)src);//逐字节复制
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
return ret;//返回目的地地址
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
My_memmove(arr1+2, arr1, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);//输出1 2 1 2 3 4 5 8 9 10
}
return 0;
}
本文结束!大家也可以动手试试自己的实现方法~