前言:
我们前面已经学习了函数、指针等一系列知识,接下来,咱们一起来模拟实现strlen、qsort、strcpy、strcat、strcmp、strstr、memmove、memcpy、atoi,以及strncat、strncmp、strtok、strerror、memset、memcmp的使用。
一、使用并模拟实现qsort
qsort可以对函数进行排序,使函数从无序变成有序。以下是官网解释:
qsort函数也称之为快排。在确定好是降序还是升序后,经行快速排序,在所有排序中时间复杂度最低,效率更高。
下面是用代码演示一下:
1. #include <stdio.h> /* printf */ 2. #include <stdlib.h> /* qsort */ 3. 4. int values[] = { 40, 10, 100, 90, 20, 25 }; 5. 6. int compare(const void* a, const void* b) 7. { 8. return (*(int*)a - *(int*)b); 9. } 10. 11. int main() 12. { 13. int n; 14. qsort(values, 6, sizeof(int), compare); 15. for (n = 0; n < 6; n++) 16. printf("%d ", values[n]); 17. return 0; 18. }
运行结果如下:
相信大家对此函数有了初步了解,那下面进行模拟实现(冒泡实现)。
1. #include<stdio.h> 2. #include<assert.h> 3. int cmp(const void* p1,const void* p2) 4. { 5. return ((*(int*)p1) - (*(int*)p2)); 6. } 7. void sweap(void* p1, void* p2, int sz) 8. { 9. for (int i = 0; i < sz; i++) 10. { 11. int cmp = *((char*)p1+i); 12. *((char*)p1 + i) = *((char*)p2 + i); 13. *((char*)p2 + i) = cmp; 14. } 15. } 16. void bubble(int* arr, int n, int sz, int(*cmp)(void*, void*))//使用void*为了适应各种指针类型 17. { 18. assert(arr); 19. for (int i = 0; i < n-1; i++) 20. { 21. for (int j = 0; j < n - 1 - i; j++) 22. { 23. if (cmp((char*)arr + j * sz, (char*)arr + (j + 1) * sz) > 0) 24. { 25. sweap((char*)arr + j * sz, (char*)arr + (j + 1) * sz,sz);//使用char*能够访问到每个比特位 26. } 27. } 28. } 29. } 30. int main() 31. { 32. int arr[10] = { 1,2,3,8,9,4,5,1,3,10 }; 33. bubble(arr, sizeof(arr)/sizeof(arr[0]),sizeof(int), cmp); 34. for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) 35. { 36. printf("%d", arr[i]); 37. } 38. return 0; 39. }
二、使用模拟实现strlen
strlen可以实现统计数组中元素个数(包括空格)。官网解释如下:
字符串以 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前⾯出现的字符个数(不包 含 '\0' )。
参数指向的字符串必须要以 '\0' 结束。
注意函数的返回值为size_t,是⽆符号的( 易错 )
strlen的使⽤需要包含头⽂件
使用代码如下:
1. /* strlen example */ 2. #include <stdio.h> 3. #include <string.h> 4. 5. int main () 6. { 7. char szInput[256]; 8. printf ("Enter a sentence: "); 9. gets (szInput); 10. printf ("The sentence entered is %u characters long.\n",(unsigned)strlen(szInput)); 11. return 0; 12. }
对其模拟如下:
1. #include<stdio.h> 2. //法一:使用计数器方式 3. my_strlen(char* arr) 4. { 5. int count = 0; 6. while (*arr != 0) 7. { 8. count++; 9. arr++; 10. } 11. return count; 12. } 13. //法二:使用递归方式 14. int my_strlen(char* arr) 15. { 16. if (*arr == '\0') 17. { 18. return 0; 19. } 20. else 21. return 1 + my_strlen(arr + 1); 22. } 23. //法三:使用指针-指针方式 24. int my_strlen(char* arr) 25. { 26. char* s = arr; 27. while (*arr != '\0') 28. { 29. arr++; 30. } 31. return arr - s; 32. } 33. int main() 34. { 35. char arr[] = "12345"; 36. int ret = my_strlen(arr); 37. printf("%d", ret); 38. return 0; 39. }
以上三种方式供大家学习。
三、使⽤和模拟实现strcpy 函数
strcpy可将一个字符串拷贝到一个数组中。官网解释如下:
源字符串必须以 '\0' 结束。
会将源字符串中的 '\0' 拷⻉到⽬标空间。
⽬标空间必须⾜够⼤,以确保能存放源字符串。
⽬标空间必须可修改。
代码实现如下:
1. /* strcpy example */ 2. #include <stdio.h> 3. #include <string.h> 4. 5. int main() 6. { 7. char str1[] = "Sample string"; 8. char str2[40]; 9. char str3[40]; 10. strcpy(str2, str1); 11. strcpy(str3, "copy successful"); 12. printf("str1: %s\nstr2: %s\nstr3: %s\n", str1, str2, str3); 13. return 0; 14. }
模拟实现如下:
1. #include<stdio.h> 2. char* my_strcpy(char* dest, const char* src) 3. { 4. char* str = dest; 5. while (*dest++ = *src++) 6. { 7. ; 8. } 9. return str; 10. } 11. int main() 12. { 13. char arr1[] = "123456"; 14. char arr2[] = "abcdef"; 15. my_strcpy(arr1, arr2); 16. printf("%s", arr1); 17. return 0; 18. }
四、使用并模拟实现strcat 函数
strcat函数是字符串追加函数,也就是在字符串后面追加另一个字符串。官网解释如下:
使用 该函数代码如下:
1. #include <stdio.h> 2. #include <string.h> 3. 4. int main() 5. { 6. char str[80]; 7. strcpy(str, "these "); 8. strcat(str, "strings "); 9. strcat(str, "are "); 10. strcat(str, "concatenated."); 11. puts(str); 12. return 0; 13. }
源字符串必须以 '\0' 结束。
⽬标字符串中也得有 \0 ,否则没办法知道追加从哪⾥开始。
⽬标空间必须有⾜够的⼤,能容纳下源字符串的内容。
⽬标空间必须可修改。
模拟实现如下:
1. #include<stdio.h> 2. char* my_strcat(char* destination, const char* source) 3. { 4. char* ret = destination; 5. while (*destination) 6. { 7. destination++; 8. } 9. while (*destination++ = *source++) 10. { 11. ; 12. } 13. return ret; 14. } 15. int main() 16. { 17. char arr1[] = "hello"; 18. char arr2[] = " world"; 19. my_strcat(arr1,arr2); 20. printf("%s", arr1); 21. return 0; 22. }
五、使用并模拟实现strcmp函数
strcmp函数是C语言中字符串比较函数,用于比较两个字符串的大小。官网解释如下:
如果相等则,返回零,若str>str2,则大于零,反之小于零。
使用该函数如下:
1. #include <stdio.h> 2. #include <string.h> 3. 4. int main() 5. { 6. char key[] = "apple"; 7. char buffer[80]; 8. do 9. { 10. printf("Guess my favorite fruit? "); 11. fflush(stdout); 12. scanf("%79s", buffer); 13. } while (strcmp(key, buffer) != 0); 14. puts("Correct answer!"); 15. return 0; 16. }
模拟实现该函数如下:
1. #include<stdio.h> 2. #include<assert.h> 3. int my_strcmp(const char* str1, const char* str2) 4. { 5. assert(str1 && str2); 6. while (*str1++ == *str2++) 7. { 8. if (*str1 == *str2) 9. { 10. return 0; 11. } 12. } 13. return *str1 - *str2; 14. } 15. int main() 16. { 17. char arr1[] = "abcd"; 18. char arr2[] = "ace"; 19. int ret = my_strcmp(arr1, arr2); 20. if (ret > 0) 21. { 22. printf("arr1>arr2\n"); 23. } 24. else if (ret < 0) 25. { 26. printf("arr1<arr2\n"); 27. } 28. else 29. { 30. printf("arr1=arr2\n"); 31. } 32. return 0; 33. }
六、使用并模拟实现strstr函数
strstr函数是在字符串str1中查找是否含有字符串str2,如果存在,返回str2在str1中第一次出现的地址;否则返回NULL。官网解释如下:
对其使用代码如下:
1. #include <stdio.h> 2. #include <string.h> 3. 4. int main() 5. { 6. char str[] = "This is a simple string"; 7. char* pch; 8. pch = strstr(str, "simple"); 9. if (pch != NULL) 10. strncpy(pch, "sample", 6); 11. puts(str); 12. return 0; 13. }
Returns a pointer to the first occurrence of str2 in str1, or a null pointer if str2 is not part of str1. (函数返回字符串str2在字符串str1中第⼀次出现的位置)。
The matching process does not include the terminating null-characters, but it stops there.(字符 串的⽐较匹配不包含 \0 字符,以 \0 作为结束标志)。
模拟实现该函数如下:
1. #include<stdio.h> 2. char* my_strstr(const char* str1, const char* str2) 3. { 4. char* p = (char*)str1; 5. if (!*str2) 6. { 7. return str1; 8. } 9. while (*p) 10. { 11. char* s1 = p; 12. char* s2 = (char*)str2; 13. while (*s1 && *s2 && !(*s1 - *s2)) 14. { 15. s1++; 16. s2++; 17. } 18. if (*s2) 19. { 20. return p; 21. } 22. p++; 23. } 24. return NULL; 25. } 26. int main() 27. { 28. char arr1[] = "This is a simple string"; 29. char arr2[] = "simple"; 30. my_strstr(arr1, arr2); 31. printf(arr2); 32. return 0; 33. }
七、使用strtok函数
char * strtok ( char * str, const char * sep);
sep参数指向⼀个字符串,定义了⽤作分隔符的字符集合
第⼀个参数指定⼀个字符串,它包含了0个或者多个由sep字符串中⼀个或者多个分隔符分割的标 记。
strtok函数找到str中的下⼀个标记,并将其⽤ \0 结尾,返回⼀个指向这个标记的指针。(注: strtok函数会改变被操作的字符串,所以在使⽤strtok函数切分的字符串⼀般都是临时拷⻉的内容 并且可修改。)
strtok函数的第⼀个参数不为 NULL ,函数将找到str中第⼀个标记,strtok函数将保存它在字符串 中的位置。
strtok函数的第⼀个参数为 NULL ,函数将在同⼀个字符串中被保存的位置开始,查找下⼀个标 记。
如果字符串中不存在更多的标记,则返回 NULL 指针。
官网解释如下:
使用该函数:
1. #include <stdio.h> 2. #include <string.h> 3. 4. int main() 5. { 6. char str[] = "- This, a sample string."; 7. char* pch; 8. printf("Splitting string \"%s\" into tokens:\n", str); 9. pch = strtok(str, " ,.-"); 10. while (pch != NULL) 11. { 12. printf("%s\n", pch); 13. pch = strtok(NULL, " ,.-"); 14. } 15. return 0; 16. }
八、使用strerror函数
strerror函数可以把参数部分错误码对应的错误信息的字符串地址返回来。 在不同的系统和C语⾔标准库的实现中都规定了⼀些错误码,⼀般是放在 errno.h 这个头⽂件中说明 的,C语⾔程序启动的时候就会使⽤⼀个全⾯的变量errno来记录程序的当前错误码,只不过程序启动 的时候errno是0,表⽰没有错误,当我们在使⽤标准库中的函数的时候发⽣了某种错误,就会讲对应 的错误码,存放在errno中,⽽⼀个错误码的数字是整数很难理解是什么意思,所以每⼀个错误码都是 有对应的错误信息的。strerror函数就可以将错误对应的错误信息字符串的地址返回。
官网解释如下:
代码使用函数:
1. #include <stdio.h> 2. #include <string.h> 3. #include <errno.h> 4. 5. int main() 6. { 7. FILE* pFile; 8. pFile = fopen("unexist.ent", "r"); 9. if (pFile == NULL) 10. printf("Error opening file unexist.ent: %s\n", strerror(errno)); 11. return 0; 12. }
九、使用strncmp函数
strcmp函数与strncmp函数从函数名看来基本一样,确实,它们确实大体上相同,区别为:n确定了比较多少个元素,官网解释如下:
其返回值也是相同。 对其使用如下:
1. #include <stdio.h> 2. #include <string.h> 3. 4. int main() 5. { 6. char str[][5] = { "R2D2" , "C3PO" , "R2A6" }; 7. int n; 8. puts("Looking for R2 astromech droids..."); 9. for (n = 0; n < 3; n++) 10. if (strncmp(str[n], "R2xx", 2) == 0) 11. { 12. printf("found %s\n", str[n]); 13. } 14. return 0; 15. }
十、使用strncat函数
strncat函数与strcat函数的区别也类似与上述函数,只不过区别是拼接字节的个数仅此而已。官网解释如下:
使用该函数如下:
1. #include <stdio.h> 2. #include <string.h> 3. 4. int main() 5. { 6. char str1[20]; 7. char str2[20]; 8. strcpy(str1, "To be "); 9. strcpy(str2, "or not to be"); 10. strncat(str1, str2, 6); 11. puts(str1); 12. return 0; 13. }
十一、使用并模拟实现memcpy函数
memcpy函数是C语言中内存函数,其功能和strcpy函数类似,以下为官网解释:
函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置。
这个函数在遇到 '\0' 的时候并不会停下来。
如果source和destination有任何的重叠,复制的结果都是未定义的。
对其使用结果如下:
1. #include <stdio.h> 2. #include <string.h> 3. 4. struct { 5. char name[40]; 6. int age; 7. } person, person_copy; 8. 9. int main() 10. { 11. char myname[] = "Pierre de Fermat"; 12. 13. /* using memcpy to copy string: */ 14. memcpy(person.name, myname, strlen(myname) + 1); 15. person.age = 46; 16. 17. /* using memcpy to copy structure: */ 18. memcpy(&person_copy, &person, sizeof(person)); 19. 20. printf("person_copy: %s, %d \n", person_copy.name, person_copy.age); 21. 22. return 0; 23. }
对其模拟代码如下:
1. #include<stdio.h> 2. #include<assert.h> 3. void* my_memcpy(void* destination, const void* source, size_t num) 4. { 5. assert(destination && source); 6. void* p = destination; 7. while (num--) 8. { 9. *(char*)destination = *(char*)source; 10. destination = (char*)destination + 1; 11. source = (char*)source + 1; 12. } 13. return (p); 14. } 15. int main() 16. { 17. int arr1[] = { 1,2,3,4,5,6,7,8,9,10 }; 18. int arr2[20] = { 0 }; 19. my_memcpy(arr2, arr1,40); 20. for (int i = 0; i < 10; i++) 21. { 22. printf("%d ", arr2[i]); 23. } 24. return 0; 25. }
十二、使用并模拟实现memmove函数
memmove函数也是内存函数,它的作用是可以实现对目标字符串的拷贝并放入指定位置。官网解释如下:
和memcpy的差别就是memmove函数处理的源内存块和⽬标内存块是可以重叠的。
如果源空间和⽬标空间出现重叠,就得使⽤memmove函数处理。
试用该函数如下:
1. #include <stdio.h> 2. #include <string.h> 3. 4. int main() 5. { 6. char str[] = "memmove can be very useful......"; 7. memmove(str + 20, str + 15, 11); 8. puts(str); 9. return 0; 10. }
模拟实现如下:
1. #include<stdio.h> 2. #include<assert.h> 3. void* my_memmove(void* dest, const void* sour, size_t num) 4. { 5. void* p = dest; 6. if (dest < sour) 7. { 8. while (num--) 9. { 10. *(char*)dest = *(char*)sour; 11. dest = (char *)dest+1; 12. sour = (char*)sour + 1; 13. } 14. } 15. else 16. { 17. dest = (char*)dest + num - 1; 18. sour = (char*)sour + num - 1; 19. 20. while (num--) 21. { 22. *(char*)dest = *(char*)sour; 23. dest = (char*)dest - 1; 24. sour = (char*)sour - 1; 25. } 26. } 27. return p; 28. } 29. int main() 30. { 31. char str[] = "memmove can be very useful......"; 32. my_memmove(str + 20, str + 15, 11); 33. puts(str); 34. return 0; 35. }
十三、使用memset函数
memset是⽤来设置内存的,将内存中的值以字节为单位设置成想要的内容。官网解释如下:
使用函数如下:
1. #include <stdio.h> 2. #include <string.h> 3. 4. int main() 5. { 6. char str[] = "almost every programmer should know memset!"; 7. memset(str, '-', 6); 8. puts(str); 9. return 0; 10. }
十四、使用 memcmp函数
该函数即为比较函数与上文strncmp函数类似。官网解释如下:
使用该函数如下:
1. #include <stdio.h> 2. #include <string.h> 3. 4. int main() 5. { 6. char buffer1[] = "DWgaOtP12df0"; 7. char buffer2[] = "DWGAOTP12DF0"; 8. 9. int n; 10. 11. n = memcmp(buffer1, buffer2, sizeof(buffer1)); 12. 13. if (n > 0) 14. { 15. printf("'%s' is greater than '%s'.\n", buffer1, buffer2); 16. } 17. else if (n < 0) 18. { 19. printf("'%s' is less than '%s'.\n", buffer1, buffer2); 20. } 21. else 22. { 23. printf("'%s' is the same as '%s'.\n", buffer1, buffer2); 24. } 25. return 0; 26. }
十五、使用并模拟实现atoi函数
它的功能:
解析将其内容解释为整数的 C 字符串,该整数作为 类型的值返回。
该函数首先根据需要丢弃尽可能多的空格字符(如 ),直到找到第一个非空格字符。然后,从此字符开始,取一个可选的首字母加号或减号,后跟尽可能多的 10 进制数字,并将它们解释为数值。
字符串可以在构成整数的字符之后包含其他字符,这些字符将被忽略,并且对此函数的行为没有影响。
如果 中的第一个非空格字符序列不是有效的整数,或者由于为空或仅包含空格字符而不存在此类序列,则不执行转换并返回零。
官网解释:
使用该函数如下:
1. #include <stdio.h> 2. #include <string.h> 3. int main() 4. { 5. int i; 6. char buffer[256]; 7. printf("Enter a number: "); 8. fgets(buffer, 256, stdin); 9. i = atoi(buffer); 10. printf("The value entered is %d. Its double is %d.\n", i, i * 2); 11. return 0; 12. }
模拟实现如下:
1. #include<stdio.h> 2. #include<assert.h> 3. #include<ctype.h> 4. #include<stdlib.h> 5. enum State 6. { 7. INVAILD, 8. VAILD 9. }a; 10. int my_atoi(const char* str) 11. { 12. assert(str); 13. if (*str == '\0') 14. { 15. return 0; 16. } 17. while (isspace(*str)) 18. { 19. str++; 20. } 21. int flag = 1; 22. if (*str == '+') 23. { 24. flag = 1; 25. str++; 26. } 27. if (*str == '-') 28. { 29. flag = -1; 30. str++; 31. } 32. long long ret = 0; 33. while (*str != '\0') 34. { 35. if (isdigit(*str)) 36. { 37. ret = ret * 10 + flag * (*str - '0'); 38. if (ret > INT_MAX) 39. { 40. return INT_MAX; 41. } 42. if (ret < INT_MIN) 43. { 44. return INT_MIN; 45. } 46. } 47. else 48. { 49. (int)ret; 50. } 51. str++; 52. } 53. if (*str == '\0') 54. { 55. a =VAILD; 56. } 57. return (int)ret; 58. } 59. int main() 60. { 61. char arr[20] = "-123456"; 62. int ret = my_atoi(arr); 63. if (a == VAILD) 64. { 65. printf("合法转化:%d\n", ret); 66. } 67. else if(a == INVAILD) 68. { 69. printf("非法转化:%d\n", ret); 70. } 71. return 0; 72. }
完!