一些库函数的模拟实现

前言

库函数并非C语言本身的组成部分,而是C语言编译系统为方便用户使用而提供的公共函数。以下将对部分库函数进行模拟实现。

1.strlen的模拟实现

strlen函数

功能:计算字符串的长度并以整型形式返回,不包括结束符NULL(即’\0’)
原函数使用方法:

1
2
3
4
5
6
7
8
9
10
11
#include<stdio.h>
#include<string.h>

int main()
{
char ch[] = "abcdef";
int ret = 0;
ret = strlen(ch);
printf("%d",ret);
return 0;
}

那么这个函数是如何实现的呢,以下将用三种方法模拟实现strlen
基本思路:定义一个字符指针从字符串的第一个元素开始向后计算位数,直到遇见 ‘\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
#include<stdio.h>
#include<assert.h>

int my_strlen(const char *str)
{
int count = 0;
assert(str != NULL);
while(*str != '\0')
{
count++;
str++;
}
return count;
}

int main()
{
char ch[] = "abcdef";
int ret = 0;
ret = my_strlen(ch);
printf("此字符串的长度为:%d",ret);
return 0;
}

法二. 指针-指针:指针减指针返回的是两个指针之间元素的个数,所以我们定义两个指针,一个指针将字符串首元素的地址保存起来,

然后当字符串指针没有找到 ‘\0’时让指针后移,最后将字符串指针-刚才定义的首元素地址的指针,得到的就是字符串的个数。
具体实现:

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

int my_strlen(const char *str)
{
assert(str != NULL);
const char *start = str;
while(*str != '\0')
{
str++;
}
return str - start;
}

int main()
{
char *p = "abcdef";
int ret = my_strlen(p);
printf("%d",ret);
return 0;
}

法三. 递归:即当字符串为0即一开始就找到了’\0’,那么函数返回0,否则返回1+参数为字符串指针加1再调用此函数。

具体实现:

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

int my_strlen(const char *str)
{
assert(str != NULL);
if (*str == '\0')
{
return 0;
}
else
{
return 1 + strlen(str + 1);
}

}
int main()
{
char ch[] = "abcdefjikjas";
int ret = 0;
ret = my_strlen(ch);
printf("字符串的长度为:%d\n", ret);
return 0;
}

2.strcpy的模拟实现

strcpy()函数: 复制字符串

将一个字符串的内容复制到另一个字符串的空间中去
基本思路:定义两个字符指针,一个指向目标字符串,一个指向原字符串,从原字符串首元素开始一一拷贝字符到目标字符串,知道遇到原字符串的‘/0’后停止
原函数使用方法:

1
2
3
4
5
6
7
8
9
10
#include<stdio.h>
int main()
{
char ch1[20] = "abcdef";
char ch2[50] = { 0 };
strcpy(ch2, ch1);
printf("%s\n", ch2);
system("pause");
return 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
#include<stdio.h>
#include<assert.h>
char *my_strcpy(char *dest, const char *src) //strcop返回的是目标字符串首元素地址
{
assert(dest && src);
char *address = dest;
while (*src != '\0' )
{
*dest = *src;
dest++;
src++;
}
return address;
}

int main()
{
char ch1[] = "abcdef";
char ch2[50] = { 0 };
my_strcpy(ch2, ch1);
printf("%s\n", ch2);
system("pause");
return 0;
}

3.strcat的模拟实现

strcat()函数: 连接两个字符串

将一个字符串连接到另一个字符串的末尾
基本思路:定义两个字符指针,一个指向目标字符串,一个指向原字符串,首先找到目标字符串的末尾‘\0’,从此处开始依次将原字符串的内容拷贝到目标字符串的后面
原函数使用方法:

1
2
3
4
5
6
7
8
9
10
#include<stdio.h>
int main()
{
char ch1[50] = "abcdef";
char ch2[] = "aaaaa";
strcat(ch1, ch2);
printf("%s\n", ch1);
system("pause");
return 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
#include<stdio.h>
#include<assert.h>

char *my_strcat(char *dest, const char *src) //返回值为目标字符串首元素地址
{
assert(dest && src);
char *address = dest;
while (*dest != '\0')
{
dest++;
}
while (*src != '\0')
{
*dest = *src;
dest++;
src++;
}
return address;
}

int main()
{
char ch1[50] = "hello ";
char ch2[] = "world";
my_strcat(ch1, ch2);
printf("%s\n", ch1);
system("pause");
return 0;
}

4.strcmp的模拟实现

strcmp(s1,s2)两个字符串大小比较(比的是ASCII码),当s1=s2 返回值为0; s1>s2 返回值为1; s1<s2 返回值为-1; (实际在库函数的定义中,大于是返回的是一个大于1的值,小于时返回的是一个小于1的值,但在vs编辑器下直接返回1,-1,0)

模拟函数实现基本思路:定义两个字符串指针,从两个字符串的第一位开始比较,如果相同则两个指针继续往后比较,一但有两个字符的值不相同则返回
原函数使用方法:

1
2
3
4
5
6
7
8
9
10
#include<stdio.h>
int main()
{
char ch1[] = "bc";
char ch2[] = "abc";
int ret = strcmp(ch1, ch2);
printf("%d\n", ret);
system("pause");
return 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
#include<stdio.h>
#include<assert.h>
int my_strcmp(const char *s1, const char *s2)
{
assert(s1 && s2);

while (*s1 == *s2)
{
if (*s1 == '\0')
{
return 0;
}
s1++;
s2++;
}
if (*s1 > *s2)
{
return 1;
}
else
{
return -1;
}
}

int main()
{
char ch1[] = "bc";
char ch2[] = "abc";
int ret = my_strcmp(ch1, ch2);
printf("%d\n", ret);
system("pause");
return 0;
}

5.strstr的模拟实现

strstr(s1,s2)的功能:判断一个字符串(s2)是否是另外一字符串(s1)的子字符串

函数返回值为子字符串(s2)首字符的地址
模拟函数实现基本思路:从s1的首字符开始,当发现有与s2相同的字符时两个指向s1,s2的指针开始依次向后计算,并返回首字符的地址
原函数使用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include<stdio.h>
#include<string.h>
int main()
{
char s1[] = "abcdef";
char s2[] = "abc";
char *p;
p = strstr(s1, s2); //返回的是子字符串s2在原字符串s1中第一次出现时首元素的地址
if (p != NULL)
{
printf("%s\n", p);
}
else
{
printf("不存在\n");
}
system("pause");
return 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
44
45
46
47
48
49
50
#include<stdio.h>
#include<assert.h>

char *my_strstr(const char *str, const char *substr)
{
const char *s1 = str;
const char *s2 = substr;
const char *cur = str;
assert(str && substr);

if (*substr == '\0')
{
return (char*)str;
}

while (*cur)
{
s1 = cur;
s2 = substr;
while (*s1 && *s2 && (*s1 == *s2))
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return (char*)cur;
}
cur++;
}
return NULL;
}

int main()
{
char *ch1 = "abbbcdef";
char *ch2 = "bbd";
char *p;
p = my_strstr(ch1, ch2);
if (p != NULL)
{
printf("%s\n", p);
}
else
{
printf("原字符串中无此子字符串\n");
}
system("pause");
return 0;
}

6.memcpy(内存拷贝函数)的模拟实现

memcpy()的功能:从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中 返回目标起始地址

memcpy和strcpy的区别:
1.复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
2.用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy
3.复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符”\0”才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。
size_t 指无符号整型 他的单位是字节 即拷贝是是按照字节数拷贝的
函数的基本用法:

1
2
3
4
5
6
7
8
9
10
11
#include<stdio.h>
#include<string.h>

int main()
{
int ch1[50] = {0};
int ch2[20] = { 1, 2, 3, 4, 5 };
memcpy(ch1, ch2, 20);
system("pause");
return 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
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>

void* my_memcpy(void *dest, void *src, size_t count)
{
void *ret = dest;
assert(dest && src);
while (count--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}

int main()
{
int ch1[50] = { 0 };
int ch2[20] = { 1, 2, 3, 4, 56,7,8,9};
my_memcpy(ch1, ch2, 20);
system("pause");
return 0;
}

7.mommove

在上面的momcpy函数中,如果我们想将1,2,3,4拷贝3,4,5,6的地方,我们会发现memcpy是无法完成的。而memmove则可以很好的完成此需求
原函数的使用:

1
2
3
4
5
6
7
8
9
#include<stdio.h>
#include<string.h>
int main()
{
int arr[] = { 1, 2, 3, 4, 5, 6,7,8,9 };
memmove(arr + 2, arr, 16);
system("pause");
return 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
#include<stdio.h>
#include<assert.h>

void *my_memmove(void *dest, void *src, size_t count)
{
void *ret = dest;
assert(dest && src);

if(dest<src)
{
while(count--)
{
*(char *)dest = *(char *)src;
dest = (char *)dest+1;
src = (char *)src+1;
}
}
else
{
while(count--)
{
*((char *)dest+count) = *((char *)src+count);
}
}
return ret;
}

int main()
{
int arr[] = { 1, 2, 3, 4, 5, 6,7,8,9 };
my_memmove(arr+3,arr,20);
return 0;
}

运行结果:
图片无法正常显示

8.strchr

函数功能:可以查找字符串s中首次出现字符c的位置。返回值为字符c首次出现的地址。

函数的基本用法:

1
2
3
4
5
6
7
8
9
10
#include<stdio.h>
int main()
{
char str1[] = "abcdef";
char ch = 'b';
char *p = strchr(str1, ch);
printf("%s\n", p);
system("pause");
return 0;
}

运行结果:bcdef
函数的模拟实现:

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
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
char *my_strchr(const char *str, int c)
{
assert(str != NULL);
const char *p = str;

if (*str == '\0')
{
return NULL;
}
while (*p)
{
if (*p == (char)c)
{
return (char*)p;
}
p++;
}
return NULL;
}

int main()
{
char ch1[50] = "abcdef";
char ch2 = 'b';
char *p = my_strchr(ch1, ch2);
printf("%s\n", p);
system("pause");
return 0;
}

运行结果:
图片无法正常显示

9.strncpy的模拟实现

strncpy和strcpy的区别就在于 strncpy可以指定拷贝的字节数,strcpy是以自身为长度和条件来拷贝字符串,碰见”\0”时便结束拷贝,而strncpy可以指定参数拷贝,但需要我们在拷贝结束时为他加上’\0’,与strcpy相比,strncpy更加的安全,灵活.此函数的返回值依然为目标字符串首元素地址
模拟实现:

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
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

char *my_strncpy(char* dest, const char *src, int count) //strncpy需要三个参数
{
char *address = dest;
assert(dest && src);
while (count-- && *src)
{
*dest++ = *src++;
}
*dest = '\0';
return address;
}

int main()
{
char arr1[50] = { 0 };
char arr2[] = "abcdef";
char *ret = my_strncpy(arr1, arr2,sizeof(arr2)); //三个参数:目标字符串 、源字符串、拷贝的字节数
printf("%s\n", ret);
system("pause");
return 0;
}

10.strncat的模拟实现

strncat同strcat的区别也是他可以指定字节数拷贝。需要注意的是拷贝完成后要在目标字符串后加上’\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
char *my_strncat(char* dest, const char *src, int count)
{
char *address = dest;
assert(dest && src);
while (*dest)
{
dest++;
}
while (count-- && *src)
{
*dest++ = *src++;

}
*dest = '\0';
return address;
}

int main()
{
char str1[100] = "hello ";
char str2[] = "world";
char *ret = my_strncat(str1, str2, strlen(str2));
printf("%s\n", ret);
system("pause");
return 0;
}

11.strncmp的模拟实现

strncpy也是比较两个字符串大小的,他也有三个参数,其中最好一个参数来指定比较的位数。但他与strcpy在实现上还是有一定区别的。首先在比较时他的循环条件是需要比较的位数,而不是像strcmp一样,直接将两个字符串内容拿出来比较,这点需要注意,与上面strcmp的实现多做比较
具体实现:

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
int my_strncmp(char *str1, char *str2, int count)
{
assert(str1 && str2);
while (count--)
{
if (*str1 == *str2)
{
str1++;
str2++;
}
else
{
if (*str1 > *str2)
{
return 1;
}
else
{
return 0;
}
}
}
return 0;
}
int main()
{
char s1[50] = "abcd";
char s2[10] = "aaaaa";
int ret = my_strncmp(s1, s2, strlen(s1));
if (ret == 0)
{
printf("相等\n");
}
else if (ret > 0)
{
printf("str1 > str2\n");
}
else if (ret < 0)
{
printf("str1 < str2\n");
}
system("pause");
return 0;
}

12.strrchr

strchr函数是找到一个字符在一个字符串中第一次出现的位置并打印出第一次出现的位置后的所有字符
strrchr函数是找到一个字符在一个字符串中最后一次出现的位置并打印出此字符串后的所有字符。
他的基本实现思路:从目标字符串的最后一个元素开始,倒着查找出目标字符第一次出现的位置。
具体实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
char *my_strrch(char *arr, char c)
{
char *address = NULL;
int count = strlen(arr);
assert(arr);
while (count--)
{
if (*(arr + count-1) == c)
{
address = arr + count-1;
return address;
}
}
}

int main()
{
char arr[] = "abcdebbbbaafr";
char c = 'b';
char *ret = my_strrch(arr, c);
printf("%s\n", ret);
system("pause");
return 0;
}