原创首发于CSDN,转载请注明出处,谢谢!https://blog.csdn.net/weixin_46959681/article/details/116277742
前言
在程序员进行面试时,笔试环节会经常要求自定义函数封装实现相关的 字符API ,来考察面试的程序员对编程的理解和编程水平。从这个层面考虑,我们需要也有必要不调用的系统提供的API,而是自己自定义函数实现与系统API同样的功能。
自定义函数实现字符串API操作(1)
|puts() —— myPuts()
函数原型:
#include <stdio.h>
// 返回值为一个整数(可以自行验证)。
int puts(const char *string);
演示代码: myPuts.c
/* myPuts.c */
#include <stdio.h>
void myputs(char *p) //取内容符号,非指针。
{
printf("myPuts:");
while(*p != '\0' ){
// putchar(*p++);
printf("%c",*p++);
}
putchar('\n');
}
int main()
{
char p[] = "hello,world!";
int i = puts(p);
printf("puts(p):%d\n",i);
myputs(p);
return 0;
}
运行结果:
|gets() —— myGets()
函数原型:
#include <stdio.h>
//返回值为一个字符型指针。
char *gets(char *str);
演示代码: myGets.c
/* myGets.c */
#include <stdio.h>
//除指针的声明之外,一切都为取内容运算符。
int myGets(char *p)
{
int cnt = 0;
while( *p = getchar() )
{
if(*p == '\n'){
return cnt;
}else{
cnt++;
p++;
}
}
return cnt;
}
int main()
{
//开辟字符串空间。
char str[128] = {'\0'};
printf("请输入你的字符串:\n");
int n = myGets(str);
printf("输入的字符数为 %d\n",n);
printf("myGets:%s",str);
return 0;
}
运行结果:
碎碎念:
- 没什么编程能力,代码调试起来太费劲,1小时的功夫搭进去了。教学视频中自定义函数部分的上一段代码的逻辑不成立。因为主函数中所定义的字符串
str
已经被初始化了\0
,不需要再次判定指针是否为 NULL 了。直接砍掉上半段,下半段原封不动,改来改去也是 bug 不断。 😞😞😞
|strlen() —— myStrlen()
函数原型:
#include <string.h>
// 返回值为一个整型数值。
size_t strlen(const char *str)
演示代码: myStrlen.c
/* myStrlen.c */
#include <stdio.h>
int myStrlen(char *p)
{
int cnt = 0;
printf("myStrlen:");
while(*p++ != '\0'){
cnt++;
}
return cnt;
}
int main()
{
//读者可以自行结合上文的myGets,或者使用gets。
//编译器自动在字符串末尾添加空字符 '\0'。
char q[] = "hello,world!";
char *ret = NULL;
myStrlen(q);
printf("%d byte.\n",ret);
return 0;
}
运行结果:
注意⚠️: 可以看到与
myPuts()
中的统计数相比(12 —— 13),少了一个空字符‘\0’
。
|memset() —— myMemset()
函数原型:
# include <string.h>
//返回值为一个无类型的指针。
void *memset(void *s, int c, unsigned long n);
演示代码: myMemset.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//使用 myMemset 或者 *myMemset 都可以。
void *myMemset(char *q, char c, int size)
{
while(size){
//指针指向数组字符的首字符,下面同步减少。
*q = c;
q++;
size--;
}
}
int main()
{
//memset原始用法。
char *p = NULL;
p = (char *)malloc(128);
memset(p,'a',128);
printf("memset: %s\n",p);
//myMemset
myMemset(p,'A',128);
printf("myMemset: %s\n",p);
return 0;
}
运行结果:
|strcpy() —— myStrcpy()
函数原型:
#include <string.h>
//返回值为一个字符型的指针。
char *strcpy(char* dest, const char *src);
演示代码: myStrcpy.c
/* myStrcpy.c */
#include <stdio.h>
#include <string.h>
char *myStrcpy(char *dest, const char *src)
{
if(dest == NULL || src == NULL){
return NULL;
}
while(*src != '\0'){
*dest++ = *src++;
}
return dest;
}
//该函数只拷贝 hello 5个字符。
char *myStrncpy(char *dest, const char *src, int n)
{
if(dest == NULL || src == NULL){
return NULL;
}
//只有当n大于零时,条件才成立。
while(*src != '\0' && n > 0){
*dest++ = *src++;
n--;
}
return dest;
}
int main()
{
char dest[128] = {'\0'};
char *src = "Hello,world!";
strcpy(dest,src);
printf("strcpy test: %s\n",dest);
memset(dest,'\0',128);
myStrcpy(dest,src);
printf("myStrcpy test: %s\n",dest);
memset(dest,'\0',128);
myStrncpy(dest,src,5);
printf("myStrncpy test: %s\n",dest);
return 0;
}
运行结果:
|strcat() —— myStrcat()
函数原型:
#include <string.h>
//返回值为一个字符型的指针。
char *strcat(char *dest, const char *src);
演示代码: myStrcat.c
/* myStrcat.c */
#include <stdio.h>
#include <string.h>
char *myStrcat(char *dest, const char *src)
{
//dest遍历跑到尾部。
while(*dest != '\0'){
dest++;
}
//两个指针一起操作,*dest必须要先跑到尾巴,后*src再赋值。
while(*src != '\0'){
*dest++ = *src++;
}
return dest;
}
int main()
{
int i = 0;
char dest[20] = "111111";
char src[] = "222222";
//使用函数strcat(*dest,*src)时必须保证前一个参数的空间必须能包容后者,否则溢出报错。
strcat(dest,src);
printf("strcat test: %s\n",dest);
//目的是在二次拼接之间显示出对比并且处理了其余的字符。
memset(dest,'1',6);
for(i=6;i<=20;i++){
dest[i] = '\0';
}
myStrcat(dest,src);
printf("myStrcat test: %s\n",dest);
return 0;
}
运行结果:
第一次 | 第二次 |
---|---|
吐槽
虽然仅仅只有六个字符API,但是笔者即便是跟着视频过了一篇就已经耗费了一整天的时间(中间找出一些小小的错误,外加根据自己对C语言的理解更改了代码)。即便如此,仍然还剩余6个API没有完成,strupr()
、strlwr()
、strtok()
、strchr()
、strstr()
、strcmp()
。
博客的排版、代码出 Bug 时的调式、代码的更改、思考花费的时间、按照顺序编译截图(多的时候甚至四五次)…… 当深入这些琐碎的细节时,看得人头晕眼花,内心的焦躁和不耐烦接连滋生,但笔者还是克制住了。成果的展示是简洁和优美的,但这背后的工作量已经让本人的肩膀酸麻了。(看到这里你还不点赞吗……)
文章已经五千字一百多字了,笔者不打算继续写了。这些剩余的API笔者会将其放到第三篇里去,未来空闲时再把这个尾巴补上吧。除非你有大把充裕的时间,不然千万别像笔者一天内干出一篇博文……
参考资料
更新时间记录
- 文章首次发布。「2021.4.29 21:39:52」
- “gets() —— myGets()”一节完成。 「2021.5.2 11:34」
- “memset —— myMemset.c ”一节完成。 「2021.5.2 14:26」
- 把从一到四的函数原型补全。 「2021.5.2 18:51」
- “strcpy() —— myStrncpy()”一节完成。 「2021.5.2 20:00」
- “strcat() —— myStrcat()”一节完成。 「2021.5.2 20:37」
- “吐槽”一节完成。 「2021.5.2 20:58」
- “前言”一节完成。 「2021.5.2 21:33」
- 修改了代码中的小瑕疵,增加了些许代码注释。 「2021.5.3 20:28」