前言: 学习C语言的过程中模拟实现各种库函数可以让我们了解更加底层的原理,在这里我整理了部分函数的模拟实现和注意事项.
一、strlen
1.函数介绍
strlen的原型为
1 2 3
| #include<string.h> . int strlen(char *str);
|
.
其作用和原理是
传入要统计字符串的地址
统计该字符串’\0’(字符串结束标志)前的字符个数
2.模拟实现
① 指针计数器方式
1 2 3 4 5 6 7 8 9 10 11
| int my_strlen(const char *str) { assert(str); int count = 0; while(*str != '\0') { count++; str++; } return count; }
|
② 函数递归方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| int my_strlen(const char *str) { assert(str); if(*str != '\0') { return (1 + my_strlen(str + 1) ); } else { return 0; } }
|
③ 指针减指针方法
我们要知道
同类型的指针减指针得到的结果为两个指针间的元素个数
所以我们可以通过指针减指针的方法来模拟实现strlen
具体实现方法如下
1 2 3 4 5 6 7 8 9 10
| int my_strlen(const char * str) { assert(str); char * mem = str; while( *str != '\0') { str++; } return str - mem; }
|
二、strcpy
1.函数介绍
strcpy的原型为
1 2 3
| #include<string.h> . char *strcpy(char* dest, const char *src);
|
.
strcpy的作用和原理是
先后传入空间的目标空间的起始地址和源空间的起始地址
将源空间的内容拷贝至目标空间
该函数的脾气
如果目标空间不够,会越界访问,导致报错
所以目标空间必须足够大,确保能放进去;
源空间的’\0’也会被拷贝过去;
目标空间必须可以改变(const,指针指向字符串常量字符串就不行)
2.模拟实现
具体实现方法如下:
1 2 3 4 5 6 7 8 9 10 11 12
| void my_strcpy(char *destination,const char * source) { assert( destination && source); while( *source != '\0') { *destination = *source; destination++; source++; } *destination = *source; }
|
略微观察一下这个代码,我们可以写出更简洁的版本,如下
1 2 3 4 5 6 7 8
| void my_strcpy(char *destination,const char * source) { assert( destination && source); while( *destination++ = *source) { ; } }
|
我一开始有疑问,当source字符串里面有0这个函数功能是不是就失效了呢?
原来,判断的根据是ASCII码值为0 ,我所想的’0’ ASCII值为48
而ASCII值为0的就是 ‘\0’ (空字符)
以上
三、qsort
1.函数介绍
qsort函数的原型
1 2 3 4 5 6 7 8 9
| #include<stdlib.h>
void qsort( void*base, size_t num, size_t size, int(*compar)(const void* e1, const void* e2) );
|
我们以整形排序实例来看一下qsort的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| int Cmp_int(const void*e1,const void*e2) { return *(int*)e1 - *(int*)e2; } int main() { int arr[] = {9,8,7,6,5,4,3,2,1}; int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr,sz,sizeof(int),Cmp_int);
}
|
通过qsort排序结构体
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
| struct stu { char name[20]; int age; };
int sortby_age(const void*e1,const void*e2) { return (struct stu *)e1 -> age - (struct stu *)e2 -> age; }
int sortby_name(const void*e1,const void*e2) { return strcmp((struct stu *)e1 -> name, (struct stu *)e2 -> name); }
int main() { struct stu s[3] = {{"zhang",30},{"lisi",34},{"wang",20}}; int sz = sizeof(s) / sizeof(s[0]); qsort(s,sz,sizeof(s[0]),sortby_age);
qsort(s,sz,sizeof(s[0],sortby_name);
|
.
2.模拟实现
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
| void swap(char* buf1, char * buf2 , int width) { int i = 0; for(i = 0 ; i < width ; i++) { char tem = *buf1; *buf1 = *buf2; *buf2 = tem; buf1++; buf2++; } }
void my_qsort( void*base, size_t sz, size_t width, int(*cmp)(const void* e1, const void* e2) )
{ int i = 0; for(i = 0; i < sz-1 ; i++) { int j = 0; for(j = 0 ; j < sz - 1 - i ; j++) { if( cmp( (char*)base + j*width , (char*)base + (j+1)*width)>0) { swap( (char*)base + j*width , (char*)base + (j+1)*width) } } } }
|
四、strcat
1.函数介绍
strcat原型
1 2 3
| #include<string.h> . char *strcat(char *des, const char *src);
|
strcat的作用
将源空间中的内容追加到目标空间后
目标空间中的 ‘\0’ 被替换为源空间中第一个元素
注意:strcat不能自己追加自己,会使'\0'被覆盖,导致无法终止
2.模拟实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| char* my_strcat(char * dest, const char * src) { assert(dest && src); char * ret = dest; while(*dest) { dest++; } while( *dest++ = *src++); return ret; }
|
五、strcmp
1.函数介绍
函数原型为
1 2 3
| #include<string.h> . int strcmp(const char* str1, const char* str1);
|
函数的作用
我们知道,两个字符串无法直接用大于小于号来进行比较
因此就存在了strcmp这个函数
.
对字符串逐一比较字符ASCII大小
一旦有大于或小于便停止,返回结果,若都相等则返回0
若前面都相等,则较长的win
2.模拟实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| int my_strcmp(const char* s1,const char* s2) { assert(s1 && s2); while(*s1 == *s2) { if(*s1 == '\0') { return 0; } s1++; s2++; } return *s1 - *s2; }
|
六、strncpy
1.函数介绍
该函数原型为
1 2 3
| #include<string.h> . char* strncpy(char* dest, const char* src, size_t num);
|
该函数作用和原理为
选择一定数量的字符拷贝至目标空间
.
若传递数量超过源空间含有的数量,并且空间足够
那么就会先将源空间内所有字符传进去后,按照规定数量补’\0’
2.模拟实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| char* my_strncpy(char * dest,const char * src,int num) { char* ret = dest; while(num && (*dest++ = *src++) != '\0') { num--; } if(num) { while(--num) { *dest++ = '\0'; } } return ret; }
|
七、strncat
1.函数介绍
函数的原型为
1 2 3
| #include<string.h> . char *strncat(char *dest, const char *src, size_t num);
|
函数作用为
选择固定数量的字符从源空间中追加到目标空间内
2.模拟实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| char* strncat(char *dest, const char *src, size_t num) { char *ret = dest; while(*dest++) { dest--; } while(num--) { if((*dest++ == *src++)==0) { return ret; } } *dest='\0'; return ret; }
|
八、strncmp
1.函数介绍
strncmp的原型为
1 2 3
| #include<string.h> . int strncmp(const char* str1, const char* str2, size_t num);
|
strncmp的作用为
固定一个数量
使两个字符串的前几个数量的字符进行比较
2.模拟实现
1 2 3 4 5 6 7 8 9 10 11 12 13
| int My_strncmp(const char* str1, const char* str2, size_t n) { if (n==0) { return 0; } while (n-- && *str1 != '\0' && *str1 == *str2) { str1++; str2++; } return *str1 - *str2; }
|
九、strstr
1.函数介绍
strstr函数原型为
1 2 3
| #include<string,h> . char*strstr(const char* str1,const char* str2);
|
该函数作用为
在str1中寻找str2
返回str1中出现str2首元素的地址
若找不到则返回空指针
2.模拟实现
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_strstr ( const char * str1, const char * str2) { assert(str1 && str2); const char * s1 = NULL; const char * s2 = NULL; const char * cp = str1; if(*str2 == '\0') { return (char*)str1; while(*cp) { s1 = cp; s2 = str2; while(*s1 && *s2 &&(*s1 == *s2) ) { s1++; s2++; } if(*S2 == '\0') { return (char*)cp; } cp++; } return NULL; }
|
十、strtok
1.函数介绍
该函数原型为
1 2 3
| #include<string.h> . char* strtok(char * str, const char * sep);
|
该函数的介绍
在str中 以sep中的字符分割str
sep中可以有一个或多个分隔符标记
.
strtok函数找到str中的下一个标记,并将其用’\0’结尾,返回一个指向这个标记的指针
所以该函数会改变被操作的字符串,所以在使用时一般面向对象都是临时拷贝的内容
.
strtok函数第一个参数不为空指针,函数将找到str中第一个标记,strtok会保存它在字符串中的位置
strtok函数第一个参数为空指针,函数将在同一字符串中被保存的位置开始,查找下一个标记
如果字符串中不存在标记则会返回空指针
我们以一个实例来加深理解
1 2 3 4 5 6 7 8 9 10
| char arr[30] = "9TSe@qq.com"; char *p = "@.";
char tmp[30] = {0}; strcpy(tmp,arr);
strtok(tmp,p); strtok(NULL,p); strtok(NULL,p);
|
那么实际上使用该函数的用法为
1 2 3 4 5 6 7 8 9 10 11
| char arr[30] = "9TSe@qq.com"; char *p = "@.";
char tmp[30] = {0}; strcpy(tmp,arr);
char * ret = NULL; for(ret = strtok(tmp,p); ret != NULL ; ret = strtok(NULL,p) ) { printf("%s\n",ret); }
|
该函数由于使用情况不多,暂且不做模拟实现
十一、strerror
1.函数介绍
该函数原型为
1 2 3 4
| #include<string.h> #include<errno.h> . char * strerror(int errnum);
|
该函数的作用为
返回错误码所对应的错误信息
使用情况一般如下
1
| printf("%s",strerror(errno));
|
模拟暂不实现
十二、perror
1.函数介绍
该函数原型为
1 2 3
| #include<stdio.h> . void perror(const char * str);
|
该函数的作用为
perror可以直接转换错误信息并且打印
比如
perror(“fopen”) ;
fopen: No such file or directory
该函数暂不模拟实现
十三、字符分类函数和字符转换函数
1.函数介绍
他们公用原型
1 2 3
| #include<ctype.h> . int xxx(int c);
|
函数功能为
为真返回非0
为假返回0
函数 |
如果符合以下条件返回真 |
iscntrl |
任何控制字符 |
isspace |
空白字符:空格’ ‘ ,换页’\f’,换行’\n’,回车’\r’,制表符’\t’,垂直制表符’\v’ |
isdigit |
十进制数字 0 ~ 9 |
isxdigit |
十六进制数字,包括所有十进制数字和大小写字母 af 、AF |
islower |
小写字母 a ~ z |
isuper |
大写字母 A ~ Z |
isalpha |
字母 a ~ z 和 A ~ Z |
isalnum |
字母或数字 a ~ z , A ~ Z , 0 ~ 9 |
ispunct |
标点符号,任何不属于数字或字母的图形字符(可打印) |
isgraph |
任何图形字符 |
isprint |
任何可打印字符,包括图形字符和空白字符 |
字符转换函数 |
功能 |
tolower |
将字符大写转换为小写 |
toupper |
将字符小写转换为大写 |
十四、memcpy
1.函数介绍
该函数原型为
1 2 3
| #include<string.h> . void * memcpy(void * dest , const void * src , size_t num);
|
该函数的功能
memcpy为内存拷贝,将src中的内容拷贝至dest
num中存放的为拷贝的字节大小
2.模拟实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| void * my_memcpy(void * dest , const void * src , size_t num) { assert(dest && src); void * ret = dest; while(num--) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = (char*)src + 1;
} return ret; }
|
注意:memcpy不能拷贝重叠的内存
1 2 3 4
| int arr[10] = {1,2,3,4,5,6,7,8,9,10}; memcpy(arr+2,arr,20);
|
但是memmove却可以做到
十五、memmove
1.函数介绍
该函数原型为
1 2 3
| #include<string.h> . void * memmove( void * dest, const void * src , size_t num);
|
2.模拟实现
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
| void * memmove( void * dest, const void * src , size_t num) { assert(dest && src); void * ret = dest;
if(dest<src) { while(num--) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = (char*)src + 1; } } else { while(num--) { *((char*)dest+num) = *((char*)src+num); } } return ret; }
|

如图为分情况讨论理解图
十六、memcmp
1.函数介绍
该函数原型为
1 2 3
| #include<string.h> . int memcmp(const void * str1 , const void * str2 , size_t num);
|
该函数作用
对比str1和str2中前 num 字节的内容
该函数较strcmp较相似,不再模拟实现
十七、memset
1.函数介绍
该函数原型为
1 2 3
| #include<string.h> . void * memset(void * ptr , int value , size_t num);
|
该函数作用为
设置ptr内前num字节 全部初始化为 value
该函数不进行模拟实现