0%

一些库函数的模拟实现合辑(字符串+内存函数)

strlen函数

strlen函数是用来求字符串长度的,且包含 ‘ \\0 ’,它的参数是一个char* 类型的指针,返回值是一个int类型的整数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<stdio.h>

int My_strlen(const char* a)
{
int szie = 0;
while (*a != '\0')
{
a++;
szie++;
}
return szie;
}

int main()
{
char a[25] = { 0 };
scanf("%s", &a);

printf("%d 个字符(不包括休止符)\n", My_strlen(a));
return 0;
}

模拟函数思路是,指针(a)指向的内容不为 ‘ \\0 ’的时候,计数变量(size)自加,指针(a)自加指向下一个元素,直到遇到 ‘ \\0 ’,返回此时计数变量(szie)的值。

除了以上写法,还有一种递归写法,但是不经常使用,故不做过多介绍。

1
2
3
4
5
6
7
int My_strlen(const char* a)
{
if (*a != '\0')
return (1 + My_strlen(a + 1));
else
return 0;
}

strcpy函数

strcpy是字符串拷贝函数,拷贝当前字符串的内容至目标字符串,它的参数分别是两个char*类型的指针,其中一个为了安全起见使用const修饰,它指向字符串(被拷贝的字符串)的首元素地址,另一个则指向拷贝的字符串首元素地址,该函数的返回值类型是一个char *指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
char* My_strcpy(const char* a, char* b)
{
if (a == NULL || b == NULL)//合法性检查
return NULL;
int i = 0;
while (a[i] != '\0')
{
b[i] = a[i];
i++;
}
b[i] = '\0';//由于上面的循环没有加上‘\0’,此处手动添加
return b;
}

int main()
{
char a[20] = { 0 };
char b[20] = { 0 };
scanf("%s", &a);
My_strcpy(a, b);

printf("a : %s\n", a);
printf("b : %s\n", b);

return 0;
}

这个函数的模拟实现思路也比较简单,利用 [ ]取下标的方式循环进行逐个赋值,最后给拷贝出的字符串手动添加休止符即可。

strcat函数

字符串拼接函数,将两个字符串拼接起来的函数,它有两个参数,char*类型的指向被拼接字符串首元素地址的指针,和使用const修饰的char *类型的指向要拼接字符串首元素地址的指针。返回值是一个char *类型的指向拼接好的字符串首地址的指针。在使用的时候需要注意被拼接的字符串需要足够大的空间来存放拼接好的字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
char* My_strcat(char* a,const char* b)
{
if (a == NULL || b == NULL)//合法性检查
return NULL;
int end = 0;
while (a[end] != '\0')
end++;
int i = 0;
while (b[i] != '\0')
{
a[end + i] = b[i];
i++;
}
a[end + i] = '\0';
return a;
}

int main()
{
char a[100] = { 0 };
char b[100] = { 0 };
scanf("%s", &a);
scanf("%s", &b);
My_strcat(a, b);
printf("%s\0", a);
return 0;
}

它的实现思路是,先找到被拼接函数的休止符,并从此开始逐个将拼接字符串的值赋值进去,最后在末尾手动添加休止符。

strcmp函数

字符串比较函数,比较两个字符串的内容(按照字典序),参数是两个由const修饰的char*类型的分别指向两个字符串首地址的指针,返回值是一个int类型的整数。
如果两个字符串内容完全相同,则返回0
如果第一个字符串大于第二个字符串,返回一个大于0的整数,反之返回一个小于0的整数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
int My_strcmp(const char* str1, const char* str2)
{
assert(str1 != NULL && str2 != NULL);//合法性检查
while (*str1 != '\0' && *str2 != '\0')
{//使用循环遍历
if (*str1 > *str2)
return 1;
else if (*str1 < *str2)
return -1;
//判断单个字符的ASCII码值大小
else
{//未比较出大小,指针指向元素后移
str1++;
str2++;
}
}
//此时的情况是两个字符串同时结束或者其中有一个结束
if (*str1 > *str2)//继续判断
return 1;
else if (*str1 < *str2)
return -1;
else//同时结束,字符串内容相同
return 0;
}

int main()
{
char str1[20] = { 0 };
char str2[20] = { 0 };
scanf("%s", &str1);
scanf("%s", &str2);

int ret = My_strcmp(str1, str2);

if (ret == 0)
printf("str1 = str2\n");
else if (ret > 0)
printf("str1 > str2\n");
else
printf("str1 < str2\n");
return 0;
}

该函数的模拟思路在注释中写出。

strstr函数

这个函数(strstr(str1,str2))用于判断字符串str2是否为str1的子串,如果是则返回一个指向str2在str1中首次出现的指针,如果不是则返回NULL。由于仅仅是判断,所以两个char *类型的参数和char *类型的返回值均使用const修饰。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
const char* My_strstr(const char* str1, const char* str2)
{
assert(str1 != NULL && str2 != NULL);//合法性检查
if (*str2 == '\0')//如果str2为空字符串,则直接返回NULL
{
return NULL;
}
const char* black = str1;
while(*black != '\0')//遍历str1
{
const char* red = black;
const char* sub = str2;
while (*red != '\0' && *sub != '\0' && (*red == *sub))
{//从blakc指向的位置向后遍历
//出现了str1和str2都没有结束并且red和sub指向的元素相同,进入循环
red++;
sub++;
}
//此时有三种情况
//1.sub指向了休止符
if (*sub == '\0')//str2已经结束了,则说明str2一定是str1的子串
return black;
//此处先判断sub也是有考究的
//假如两字符串同时结束,又能匹配成功
//那么先判断red会直接返回NULL
//2.red指向了休止符
if (*red == '\0')//str1已经结束了,则说明str2一定不是str1的子串
return NULL;
//3.出现了red和sub指向的元素不同
black++;//str1和str2都没有结束,进度后移
}
return NULL;
}

int main()
{
char str1[20] = { 0 };
char str2[20] = { 0 };
scanf("%s", &str1);
scanf("%s", &str2);

const char* ret = My_strstr(str1, str2);
printf("%p , %p", str1, ret);

return 0;
}

该函数的模拟思路是:创建三个指针变量,black、red和sub
其中,black用来记录当前匹配的起始位置,red和sub用来匹配字符串1和字符串2是否相等,如果匹配成功则返回black,未成功则匹配起始位置后移,直到匹配成功或字符串1结束,返回NULL,具体细节在注释中写出。

memcpy函数

内存拷贝函数,将源内存(src)中的内容拷贝到目标内存(dest)中,为了考虑到函数的普适性,此处的dest,src以及返回值均使用void*类型的指针,只指向地址,不考虑所指向元素的大小,还要一个无符号整形的整数作为参数,来表示需要拷贝多少个字节。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void* My_memcpy(void* dest, void* src, size_t num)
{
assert(dest != NULL && src != NULL);//合法性检查
size_t i = 0;//限制单字节拷贝次数
void* ret = dest;
for (i = 0; i < num; i++)
{//依次赋值
*(char*)dest = *(char*)src;//强制转换成char*是为了单次后移一个字节
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}

int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int b[4] = { 0 };
My_memcpy(b, a, 16);
for (int i = 0; i < 4; i++)
printf("%d ", b[i]);
printf("\n");
return 0;
}

具体模拟思路在注释中给出,需要注意的是,它不能处理内存重叠的情况

memmove函数

内存移动函数,将源内存(src)中的内容移动到目标内存(dest),它的功能同memcpy函数相同,但是它可以处理内存重叠的情况(从后往前拷贝)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
void* My_memcpy(void* dest, void* src, size_t num)
{
assert(dest != NULL && src != NULL);
size_t i = 0;
void* ret = dest;
for (i = 0; i < num; i++)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}

void* My_memmove(void* dest, void* src,size_t num)
{
assert(dest != NULL && src != NULL);//合法性检查
//创建两个变量等于被强制转换成char*的dest和src
//方便单个字节操作
char* cdest = (char*)dest;
char* csrc = (char*)src;
if (csrc < cdest && cdest < csrc + num)
{//判断是否重叠,若重叠则找到当前内存位置加上要移动的字节数减去一
char* pdest = cdest + num - 1;
char* psrc = csrc + num - 1;
for (size_t i = 0; i < num; i++)
{//从后往前依次赋值,对两块内存逐个操作
*pdest = *psrc;
pdest--;
psrc++;
}
}
else
{//不重叠直接调用memcpy函数(偷懒)
My_memcpy(dest, src, num);
}
return dest;
}

int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int b[4] = { 0 };
My_memmove(b, a, 16);
for (int i = 0; i < 4; i++)
printf("%d ", b[i]);
printf("\n");
return 0;
}

具体的模拟思路在注释中写出。

-------------本文结束感谢您的阅读-------------