C的实用笔记37——几种常用的字符串处理API(二)

本文介绍了C语言中常用的字符串处理函数,如strcat、strncat、strcmp、strncmp等,包括它们的功能、使用方法和注意事项,并提供了实现这些函数的习题及解题思路。文章还涉及了字符串查找、分割和转换为数值的函数,如strchr、strtok、atoi、atof,以及如何自己编写类似功能的函数。
摘要由CSDN通过智能技术生成

6.字符串拼接函数

0、知识点:

  1. 内存污染(存储字符串的目的内存不够用时,后面的内存会被污染,也就是被修改,类似于下标越界)

  2. strcat其实就是另一种形式的strcpy,一个从末尾开始复制,一个从头开始复制。

1、strcat函数:复习以下知识点后,练习 习题5,自己实现strncat()函数

  1. 函数原型:char*   strcat(char  *dest, const  char  *src);
  2. 操作数:①目的字符数组的首地址dest;②来源字符串的首地址src。
  3. 函数功能:把src所指向的字符串(包括“\0”)拼接到dest所指向的字符串的末尾(末尾指的是dest所指字符串的第一个空字符'\0',并删除*dest原来末尾的“\0”),然后返回指向dest的指针。
  4. 缺点:①当*dest不足以容纳被复制进来的*src时,字符串会溢出,造成内存污染。尽量避免。
    #include <stdio.h>
    #include <string.h>
    int main(int argc, char const *argv[])
    {
        char str[20] = "abcde";
        char *src = "hello";
        printf("拼接前:%s\n", str);
        strcat(str, src);
        printf("拼接后:%s\n", str);
        return 0;
    }

2、strncat函数:复习以下知识点后,练习 习题6,自己实现strncat()函数

  1. 函数原型:char*   strncat(char  *dest, const  char  *src,  size_t   count);
  2. 操作数:①目的字符数组的首地址dest;②来源字符串的首地址src;③限制拼接的字节数count。
  3. 函数功能:把src所指向的字符串中以src地址开始的后count个字节,拼接到dest所指向的字符串末尾(删除*dest原来末尾的“\0”)。返回指向dest的指针。
  4. 内部决策:当count大小 > src所指字符串长度时,多拼接的部分会用空字符'\0'填充。
  5. 缺点:①当*dest不足以容纳被复制进来的*src时,字符串会溢出,造成内存污染。尽量避免。
    #include <stdio.h>
    #include <string.h>
    int main(int argc, char const *argv[])
    {
        char str[20] = "abcde";
        char *src = "hello";
        printf("拼接前:%s\n", str);
        strncat(str, src, 2);
        printf("拼接前2个字节后:%s\n", str);
        return 0;
    }

7.字符串比较函数

1、strcmp函数:复习以下知识点后,练习 习题7,自己实现strcmp()函数

  1. 函数原型:int   strcmp(const   char  *str1,  const  char  *str2);
  2. 操作数:①字符串1的首地址str1;②字符串2的首地址str2。
  3. 函数功能:逐字符比较str1 和 str2 指向的字符串中相同位置字符的ASCII码,如果第0个字符相同,就比较下一个字符,依此类推,直到比较出大小,若str1=str2,则返回0;若str1<str2,则返回-1;若str1>str2,则返回1。
  4. 使用方法:一般用来判断两个字符串是否一样,我们不关心两个字符串谁大谁小。
    #include <stdio.h>
    #include <string.h>
    int main(int argc, char const *argv[])
    {
        char *str1 = "abcd2chen3";
        char *str2 = "abcdeche13567";
        int ret = strcmp(str1, str2);
        printf("RET=%d\n", ret);
        if(ret == 0){
            printf("两个字符串一样\n");
        }else{
            printf("两个字符串不一样");
        }
        return 0;
    }

2、strncmp函数:复习以下知识点后,练习 习题8,自己实现strncmp()函数

  1. 函数原型:int   strncmp(const   char  *str1,    const  char  *str2,   size_t   count);
  2. 操作数:①字符串1的首地址str1;②字符串2的首地址str2;③最大比较字节数count。
  3. 函数功能:逐字符比较str1 和 str2 指向的字符串中相同位置字符的ASCII码,如果第0个字符相同,就比较下一个字符,依此类推,最多比较前 count 个字节。若str1=str2,则返回0;若str1<str2,则返回-1;若str1>str2,则返回1。
  4. 使用方法:一般用来判断两个字符串的前count个字节是否一样,我们不关心两个字符串谁大谁小。
    #include <stdio.h>
    #include <string.h>
    int main(int argc, char const *argv[])
    {
        char *str1 = "abcd2chen3";
        char *str2 = "abcdeche13567";
        int ret = strncmp(str1, str2, 10);
        printf("RET=%d\n", ret);
        if(ret == 0){
            printf("两个字符串的前10个字符一样\n");
        }else{
            printf("两个字符串的前10个字符不一样");
        }
        return 0;
    }

8.字符串查找函数

1、strchr函数:复习以下知识点后,练习 习题10,自己实现strchr()函数

  1. 函数原型:char*   strchr(const   char   *str,   int   c);
  2. 操作数:①字符串的首地址str;②被查找字符c的ASCII码。
  3. 函数功能:在字符指针 str 所指向的字符串中按照ASCII码,搜索第一次出现字符 c(一个无符号字符)的位置。并返回字符 c 的地址。
  4. 使用方法:①一般用来判断str所指字符串中是否含有字符c(如下示例)。②用来找出字符串str中所有字符c的个数和位置,见习题9
    #include <stdio.h>
    #include <string.h>
    int main(int argc, char const *argv[])
    {
        char *str = "abcde12345 abcde12345 abcde12345";
        char *p = strchr(str, '3');
        if(p == NULL){
            printf("找不到字符'3'");
            return 0;
        }
        printf("第一个字符'3'距离字符串首地址%d个字节", p - str);
        return 0;
    }

2、strrchr函数:略。它与strchr唯一的不同就是从尾部开始查找,r是英文“rear”(尾部)的首字母。

3、strstr函数:复习以下知识点后,练习 习题11,自己实现strstr()函数

  1. 函数原型:char*   strstr(const   char   *str,   const   char   *substr);
  2. 操作数:①字符串的首地址str;②被查找的子字符串substr。
  3. 函数功能:在str指向的字符串中从头开始查找,若substr是str的子串,则返回substr在str中首次出现的地址。
  4. 使用方法:一般用来判断str所指字符串中是否含有子字符串substr。
    #include <stdio.h>
    #include <string.h>
    int main(int argc, char const *argv[])
    {
        char *str = "abcde1234$#$ abcde5678$#$";
        char *substr = "$#$";
        char *p = strstr(str, substr);
        if(p == NULL){
            printf("找不到子字符串\n");
            return 0;
        }
        printf("第一个子字符串%s距离字符串首地址%d个字节", substr, p-str);
        return 0;
    }

9.字符串分割函数

1、strtok函数:复习以下知识点后,练习习题12(解析信息),巩固strtok函数的用法

  1. 函数原型:char*   strtok(char  *str,   const   char  *delim);
  2. 操作数:①字符数组的首地址str;②分隔符的首地址delim。
  3. 函数功能:按照字符指针delim指向的分隔符中的字符对str所指字符串进行逐字符查找,如果在str中发现了delim中的字符,那么就使得str中的这个字符变成'\0',函数结束并返回从str中切割出来的子字符串的首地址。
  4. 内部决策:①strtok函数遇到str中的空字符'\0',则代表分割失败;②strtok函数具有记忆功能,在上一次切割完成后,strtok函数的起始分割位置会往后偏移,不能再将str所指字符串的首地址传递给strtok函数(因为会造成分割失败),strtok函数的设定是:再次调用strtok函数时,需要将NULL传进第一个参数中;③如果在str所指字符串中出现了连续的几个delim字符,strtok函数只会将第一个delim字符变成'\0';
  5. 使用方法:只要没切到'\0',就一直调用strtok函数,用一个字符指针数组来保存子字符串首地址。如图:
    #include <stdio.h>
    #include <string.h>
    int main(int argc, char const *argv[])
    {
        char str[80] = "xiaoming:21,,,.男.,北京:haidian";
        const char *delim = ":,.";    //分隔符是冒号、逗号、句号
        char *token[10];            
        int i = 0;
        token[i] = strtok(str, delim);
        printf("第%d次切割,p[%d]=%s\n", i+1, i, token[i]);
        while(token[i] != NULL){
            i++;
            token[i] = strtok(NULL, delim);
            printf("第%d次切割,p[%d]=%s\n", i+1, i, token[i]);
        }
        return 0;
    }

 

10.字符串转数值函数(<stdlib.h>)

1、atoi函数:复习以下知识点后,练习 习题13,自己实现atoi()函数。

  1. 函数原型:int   atoi(const   char  *str);
  2. 操作数:①字符串的首地址str。
  3. 函数功能:在字符串str中逐字符读取,直到读到的字符不是①'-'(首次读取的'-'才有效,并且必须在字符串首位的才有效)和 ②'0' ~ '9' ,将读取到的字符转换成int型数据。读取失败返回0。
    #include <stdio.h>
    #include <stdlib.h>
    int main(int argc, char const *argv[])
    {
        char *str1 = "0123abc";
        char *str2 = ".5f";
        char *str3 = "abc123.5";
        char *str4 = "-0123.5";
        printf("atoi(%s)=%d\n", str1, atoi(str1));
        printf("atoi(%s)=%d\n", str2, atoi(str2));
        printf("atoi(%s)=%d\n", str3, atoi(str3));
        printf("atoi(%s)=%d\n", str4, atoi(str4));
        return 0;
    }

2、atof函数:复习以下知识点后,练习 习题14,自己实现atof()函数。

  1. 函数原型:double   atof(const   char  *str);
  2. 操作数:①字符串的首地址str。
  3. 函数功能:在字符串str中逐字符读取,直到读到的字符不是①'-'(首次读取的'-'才有效,并且必须在字符串首位的才有效)和 ②'0' ~ '9' 和③ '.'(首次读取的'.'才有效),将读取到的字符转换成double型数据。读取失败返回0。
    #include <stdio.h>
    #include <stdlib.h>
    int main(int argc, char const *argv[])
    {
        char *str1 = "123abc";
        char *str2 = ".5f";
        char *str3 = "abc123.5";
        char *str4 = "-0123.5";
        printf("atof(%s)=%lf\n", str1, atof(str1));
        printf("atof(%s)=%lf\n", str2, atof(str2));
        printf("atof(%s)=%lf\n", str3, atof(str3));
        printf("atof(%s)=%lf\n", str4, atof(str4));
        return 0;
    }

习题

习题5:试着实现strcat的函数功能

  1. 思路:
    f1. 封装实现strcat函数功能的API: char* my_strcat(char *dest, const char *src); 
    	f1.1 判断指针dest或者指针src的值是否是NULL
        	f1.1.1 如果是,
            		那么,代表函数执行失败,提前结束函数调用,返回NULL
        f1.2 定义一个备份指针ptr指向dest所指内容
    	f1.3 while循环,控制循环的变量是*dest,当指针dest没有偏移到末尾 时,进入循环
        //内在逻辑:先让dest指针自行偏移到字符数组的末尾(第一个'\0')
    		f1.3.1 偏移字符指针dest的指向: dest++;
    	f1.4 while循环,控制循环的变量是*src,指针src没有偏移到末尾 时,进入循环
        //内在逻辑:从dest指向的字符数组的末尾(第一个'\0')开始复制src
        	f1.4.1 通过指针间接改变dest字符数组中的字符,将src中的字符复制到dest对应位置:
    				*dest = *src;
            f1.4.2 偏移字符指针str的指向: str++;
    		f1.4.3 偏移字符指针dest的指向: dest++;
    	f1.5 通过指针间接修改,令拷贝过来的最后一位是空字符'\0': *dest = '\0';
    	f1.6 返回ptr的值
    1. 初始化或者输入一个字符串,只可以放在栈区、堆区,比如: char str[20] = "abcde";
    2. 调用API1. 将某个字符串拼接到str的末尾,比如: my_strcat(str, "hello");
    3. 打印拼接前后的字符串

  2. 代码:
    #include <stdio.h>
    char* my_strcat(char *dest, const char *src);
    int main(int argc, char const *argv[])
    {
        char str[20] = "abcde";
        char *src = "hello";
        printf("拼接前:%s\n", str);
        my_strcat(str, src);
        printf("拼接后:%s\n", str);
        return 0;
    }
    char* my_strcat(char *dest, const char *src)
    {
        if(dest==NULL || src==NULL){
            return NULL;
        }
        char *ptr = dest;
        while(*dest != '\0'){
            dest++;
        }
        while(*src != '\0'){
            *dest = *src;
            dest++;
            src++;
        }
        *dest = '\0';
        return ptr;
    }

 

习题6:试着实现strncat的函数功能

  1. 思路:
    f1. 封装实现strcat函数功能的API: char* my_strncat(char *dest, const char *src, int count); 
    	f1.1 判断指针dest或者指针src的值是否是NULL
        	f1.1.1 如果是,
            		那么,代表函数执行失败,提前结束函数调用,返回NULL
        f1.2 定义一个备份指针ptr指向dest所指内容
    	f1.3 while循环,控制循环的变量是*dest,指针dest没有偏移到末尾时,进入循环
        //内在逻辑:先让dest指针自行偏移到字符数组的末尾(第一个'\0')
    		f1.3.1 偏移字符指针dest的指向: dest++;
    	f1.4 while循环,控制循环的变量是*src和count,当指针src没有偏移到末尾 并且count>0 时,进入循环
        //内在逻辑:不妨先假设count<src所指字符串长度,这时条件count>0先不满足
        //那么,就从dest指向的字符数组的末尾(第一个'\0')开始复制src
        	f1.4.1 通过指针间接改变dest字符数组中的字符,将src中的字符复制到dest对应位置:
    				*dest = *src;
            f1.4.2 偏移字符指针str的指向: str++;
    		f1.4.3 偏移字符指针dest的指向: dest++;
    		f1.4.4 修改循环变量count: count--;
    	f1.5 判断count是否大于0
        //内在逻辑:条件*src!='\0'先不满足,说明count>src所指字符串长度,那么让多拼接的部分用'\0'填充
        	f1.5.1 如果是,那么说明count>src所指字符串长度
        		f1.5.1.1 while循环,循环变量count,当count>0 时,进入循环
            		f1.5.1.1.1 通过指针间接修改: *dest = '\0';
    				f1.5.1.1.2 偏移字符指针dest的指向: dest++;
    				f1.5.1.1.3 修改循环变量count: count--;
    	f1.6 返回ptr的值
    1. 初始化或者输入一个字符串,只可以放在栈区、堆区,比如: char str[20] = "abcde";
    2. 调用API1. 将某个字符串的前几个字节拼接到str末尾,比如: my_strncat(str, "hello", 2);
    3. 打印拼接前后的字符串

  2. 代码:
    #include <stdio.h>
    char* my_strncat(char *dest, const char *src, int count);
    int main(int argc, char const *argv[])
    {
        char str[20] = "abcde";
        char *src = "hello";
        printf("拼接前:%s\n", str);
        my_strncat(str, src, 2);
        printf("拼接前2个字节后:%s\n", str);
        return 0;
    }
    char* my_strncat(char *dest, const char *src, int count)
    {
        if(dest==NULL || src==NULL){
            return NULL;
        }
        char *ptr = dest;
        while(*dest != '\0'){
            dest++;
        }
        while(count>0 && *src!='\0'){
            *dest = *src;
            dest++;
            src++;
            count--;
        }
        if(count > 0){
            while(count > 0){
                *dest = '\0';
                dest++;
                count--;
            }
        }
        return ptr;
    }

 

习题7:试着实现strcmp的函数功能

  1. 思路:                                      
    f1. 封装实现strcmp函数功能的API: int my_strcmp(const char *str1, const char *str2); 
    	f1.1 判断指针dest或者指针src的值是否是NULL
        	f1.1.1 如果是,
            		那么,代表函数执行失败,提前结束函数调用,返回2(打个比方)
        f1.2 定义结果变量ret,不妨先假设两个字符串相等,即: int ret = 0;
    	f1.3 while循环,控制循环的变量是*str1和*str2,当指针str1和str2都没有偏移到末尾 时,进入循环
        //内在逻辑:不对字符串长度进行判断,直接在两个字符串的有效长度内进行逐字符的ascii码大小比较
        	f1.3.1 判断*str1是否大于*str2
            	f1.3.1.1 如果是,
                	f1.3.1.1.1 那么结果就是str1>str2,直接返回1,提前结束函数: return 1;
            	f1.3.1.2 否则,判断*str1是否小于*str2
                	f1.3.1.2.1 如果是,
                    	f1.3.1.2.1.1 那么结果就是str1<str2,直接返回-1,提前结束函数: return -1;
            	f1.3.1.3 否则,
            		f1.3.1.3.1 偏移字符指针str1的指向: str1++;
    				f1.3.1.3.2 偏移字符指针str2的指向: str2++;
    	f1.4 判断指针str1是否没有偏移到字符串末尾,即*str1 !='\0'
        //内在逻辑:如果上述循环一直正常结束,那么肯定有一个字符串读到了'\0',因此
        //不妨假设str1的长度比str2的长,这时条件*str2!='\0'先不满足
        	f1.4.1 如果是,
            		   那么不用往下比较了,str1肯定大于str2,令: ret = 1;
        	f1.4.2 否则,判断指针str2是否没有偏移到字符串末尾,即*str2 !='\0'
    			f1.4.2.1 如果是,
            				那么不用往下比较了,str1肯定小于str2,令: ret = -1;
    	f1.6 返回ret的值
    1. 初始化或者输入两个字符串,可以放在栈区、堆区、常量区,比如: 
    	char *str1 = "abcdechen";
    	char *str2 = "abcdechem";
    2. 调用API1. 比较str1和str2,结果保存在变量ret中: ret = my_strcmp(str1, str2);
    3. 打印ret的值

  2. 代码:
    #include <stdio.h>
    int my_strcmp(const char *str1, const char *str2);
    int main(int argc, char const *argv[])
    {
        char *str1 = "abcd2chen3";
        char *str2 = "abcdechem3";
        int ret = my_strcmp(str1, str2);
        printf("RET=%d\n", ret);
        if(ret == 0){
            printf("两个字符串一样\n");
        }else{
            printf("两个字符串不一样");
        }
        return 0;
    }
    int my_strcmp(const char *str1, const char *str2)
    {
        if(str1==NULL || str2==NULL){
            printf("无效字符串\n");
            return 2;
        }
        int ret = 0;
        while(*str1!='\0' && *str2!='\0'){
            if(*str1 > *str2){
                return 1;
            }else if(*str1 < *str2){
                return -1;
            }else{
                str1++;
                str2++; 
            }
        }
        if(*str1!='\0'){
            ret = 1;
        }else if(*str2!='\0'){
            ret = -1;
        }
        return ret;
    }

 

习题8:试着实现strncmp的函数功能。

  1. 思路:
    f1. 封装实现strncmp函数功能的API: 
    int my_strncmp(const char *str1, const char *str2, int count); 
    	f1.1 判断指针dest或者指针src的值是否是NULL
        	f1.1.1 如果是,
            		那么,代表函数执行失败,提前结束函数调用,返回2(打个比方)
        f1.2 定义结果变量ret,不妨先假设两个字符串相等,即: int ret = 0;
    	f1.3 while循环,控制循环的变量是*str1、*str2、count,
        当指针str1和str2都没有偏移到末尾 并且count>0 时,进入循环
        //内在逻辑:不对字符串长度进行判断,直接在两个字符串的有效长度内进行逐字符的ascii码大小比较
        //同时假设count是小于这两个字符串的有效长度的
        	f1.3.1 判断*str1是否大于*str2
            	f1.3.1.1 如果是,
                	f1.3.1.1.1 那么结果就是str1>str2,直接返回1,提前结束函数: return 1;
            	f1.3.1.2 否则,判断*str1是否小于*str2
                	f1.3.1.2.1 如果是,
                    	f1.3.1.2.1.1 那么结果就是str1<str2,直接返回-1,提前结束函数: return -1;
            	f1.3.1.3 否则,
            		f1.3.1.3.1 偏移字符指针str1的指向: str1++;
    				f1.3.1.3.2 偏移字符指针str2的指向: str2++;
    				f1.3.1.3.3 修改循环变量count: count--;
    	f1.4 判断count是否仍然大于0
        //内在逻辑:如果count>0,那么str1和str2两个指针必定有一个读到结束符'\0'了
    		f1.4.1 判断指针str1是否没有偏移到字符串末尾,即*str1 !='\0'
        	//内在逻辑:不妨假设str1的长度比str2的长,这时条件*str2!='\0'先不满足
        		f1.4.1.1 如果是,
            				那么不用往下比较了,str1肯定大于str2,令: ret = 1;
        		f1.4.1.2 否则,判断指针str2是否没有偏移到字符串末尾,即*str2 !='\0'
    				f1.4.1.2.1 如果是,
            					那么不用往下比较了,str1肯定小于str2,令: ret = -1;
    	f1.6 返回ret的值
    1. 初始化或者输入两个字符串,可以放在栈区、堆区、常量区,比如: 
        char *str1 = "abcd2chen3";
        char *str2 = "abcdeche13567";
    2. 调用API1. 比较str1和str2的前几个字节,结果保存在变量ret中: ret = my_strcmp(str1, str2, 10);
    3. 打印ret的值

  2. 代码:
    #include <stdio.h>
    int my_strncmp(const char *str1, const char *str2, int count);
    int main(int argc, char const *argv[])
    {
        char *str1 = "abcd2chen3";
        char *str2 = "abcdeche13567";
        int ret = my_strncmp(str1, str2, 10);
        printf("RET=%d\n", ret);
        if(ret == 0){
            printf("两个字符串的前10个字符一样\n");
        }else{
            printf("两个字符串的前10个字符不一样");
        }
        return 0;
    }
    int my_strncmp(const char *str1, const char *str2, int count)
    {
        if(str1==NULL || str2==NULL){
            printf("无效字符串\n");
            return 2;
        }
        int ret = 0;
        while(*str1!='\0' && *str2!='\0' && count>0){
            if(*str1 > *str2){
                return 1;
            }else if(*str1 < *str2){
                return -1;
            }else{
                str1++;
                str2++;
                count--; 
            }
        }
        if(count > 0){
            if(*str1!='\0'){ 
                ret = 1;
            }else if(*str2!='\0'){
                ret = -1;
            }
        }
        return ret;
    }

 

习题9:统计字符串中某个字符的个数和具体位置

  1. 思路:需要对字符串有深刻认识,字符串都能看作字符数组,某个字符串中的任意字符到末尾的空字符'\0'都能形成一个字符串(一个末尾为字符'\0'的字符数组)。
    1. 初始化或输入一个字符串,可以放在栈区、堆区、常量区,比如: 
    	char *str = "abcde12345 abcde12345 abcde12345";
    2. 先调用strchr函数对str做一次字符查找,并将返回地址保持在字符指针p中,比如: 
    	char *p = strchr(str, '3');
    3. 紧接着判断查找的字符是否存在,如果不存在就结束程序
    4. while循环,控制循环遍的变量是代表字符地址的p,当p != NULL 时,进入循环
    	4.1 修改用于记录字符'3'总数的变量num: num++; //不要忘了初始化为0
        4.2 打印第num个字符'3'距离字符串str的首地址多少字节数
        4.3 修改代表查找字符地址的循环遍变量p,让它指向str中第num个字符'3'后一个字节: p++;
    	4.4 修改代表查找字符地址的循环遍变量p,让它指向下一个字符'3'的地址:
    		p = strchr(p, '3');
    5. 打印字符'3'的总数num

  2. 代码:
    #include <stdio.h>
    #include <string.h>
    int main(int argc, char const *argv[])
    {
        char *str = "abcde12345 abcde12345 abcde12345";
        int num = 0;
        char *p = strchr(str, '3');
        if(p == NULL){
            printf("找不到字符'3'");
            return 0;
        }
        while(p != NULL){
            num++;
            printf("第%d个字符'3'距离字符串首地址%d个字节\n", num, p-str);
            p++;
            p = strchr(p, '3');
        }
        printf("共有%d个字符'3'", num);
        return 0;
    }

习题10:试着实现strchr的函数功能

  1. 思路:
    f1. 封装实现strchr函数功能的API: char* my_strchr(char *str, int c); 
    	f1.1 判断指针str的值是否是NULL
        	f1.1.1 如果是,
            		那么,代表函数执行失败,提前结束函数调用,返回NULL
        f1.2 while循环,死循环
        	f1.2.1 判断当前指针str是否偏移到末尾
            	f1.2.1.1 如果是,
                			那么,断定没有找到字符c,提前结束函数调用,返回NULL
                f1.2.1.2 否则,判断当前指针str指向的内容是否等于字符c
                	f1.2.1.2.1 如果是,
                    				那么,代表找到字符c,提前结束函数调用,返回str
            	f1.2.1.3 否则,修改循环变量str的值,让指针str指向下一个字符: str++;
    			//内在逻辑:当str既没有偏移到末尾,有没有匹配上时,就让str自加,继续进行匹配
    1. 初始化或者输入一个字符串,可以放在栈区、堆区、常量区,比如: 
    	char *str = "abcde12345 abcde12345 abcde12345";
    2. 调用API1. 在字符串str中找字符,首地址保持在字符指针p中,比如: 
    	char *p = my_strchr(str, '3');
    3. 紧接着判断查找的字符是否存在,如果不存在就结束程序
    4. 打印第一个字符'3'距离字符串str首地址多少字节

  2. 代码:
    #include <stdio.h>
    char* my_strchr(char *str, int c);
    int main(int argc, char const *argv[])
    {
        char *str = "abcde12345 abcde12345 abcde12345";
        char *p = my_strchr(str, '3');
        if(p == NULL){
            printf("找不到字符'3'");
            return 0;
        }
        printf("第一个字符'3'距离字符串首地址%d个字节", p - str);
        return 0;
    }
    char* my_strchr(char *str, int c)
    {
        if(str == NULL){
            printf("字符串无效\n");
            return NULL;
        }
        while(1){
            if(*str == '\0'){
                return NULL;
            }else if(*str == c){
                return str;
            }
            str++;
        }
    }

习题11:试着实现strstr的函数功能

  1. 思路:循环嵌套、goto语句
    f1. 封装实现strstr函数功能的API: char* my_strstr(char *str, char *substr);
    	f1.1 判断指针str的值是否是NULL
        	f1.1.1 如果是,
            		那么,代表函数执行失败,提前结束函数调用,返回NULL
        f1.2 备份子字符串的首地址给字符指针ptr: char *ptr = substr;
    	//内在逻辑:我们的第二层循环是去让substr与str做匹配,如果中途失败,需要让指针substr复位
        f1.3 while循环,控制循环的变量是*str, 当指针str没有偏移到末尾 时,进入循环
        	f1.3.1 设定一个back标签: back:
    		//内在逻辑:每当第二层循环中substr与str不匹配,我们需要回到此处,继续执行以下相同代码
        	f1.3.2 判断当前str所指字符是否与当前substr所指字符相等
            	f1.3.2.1 如果是, //str和substr的头对上了,开始进入二层循环进行匹配
                	f1.3.2.1.1 定义一个复合语句中的局部字符指针变量p来记住当前str位置: char *p=str;
    				f1.3.2.1.2 while循环,控制循环的变量是*substr, 当指针substr没有偏移到末尾 时,进入循环
                    	f1.3.2.1.2.1 判断当前str所指字符是否与当前substr所指字符相等
                        	f1.3.2.1.2.1.1 如果是,
                            	f1.3.2.1.2.1.1.1 修改循环变量str的值,让指针str指向下一个字符: str++;
    							f1.3.2.1.2.1.1.2 修改循环变量substr的值,让指针substr指向下一个字符: substr++;
    						f1.3.2.1.2.1.2 否则,// 意味着不得不退出二层循环,终止匹配
                            	f1.3.2.1.2.1.2.1 让指针substr复位: substr = ptr; //5行
    							f1.3.2.1.2.1.2.2 用goto语句回跳到一层循环的开始: goto back: //8行
    				f1.3.2.1.3 提前结束函数调用,返回字符指针p: return p; //12行
    				//内在逻辑:顺利通过二层循环,说明substr已经和str匹配上了,返回起始匹配位置
                f1.3.2.2 否则,
                    f1.3.2.2.1 修改循环变量str的值,让指针str指向下一个字符: str++;
        f1.4 结束函数调用,返回NULL
        //内在逻辑:顺利通过一层循环,说明str偏移到末尾还没有和substr匹配上,故返回NULL表示没找到
    1. 初始化或者输入两个字符串,可以放在栈区、堆区、常量区,比如: 
    	char *str = "abcde1234$#$ abcde5678$#$";
    	char *substr = "$#$";
    2. 调用API1. 在字符串str中找子串substr,首地址保持在字符指针p中,比如: 
    	char *p = my_strstr(str, substr);
    3. 紧接着判断查找的子字符串是否存在,如果不存在就结束程序
    4. 打印第一个子字符串substr的首地址距离字符串str的首地址多少个字节

  2. 代码:
    #include <stdio.h>
    char* my_strstr(char *str, char *substr);
    int main(int argc, char const *argv[])
    {
        char *str = "abcde1234$#$ abcde5678$#$";
        char *substr = "$#$";
        char *p = my_strstr(str, substr);
        if(p == NULL){
            printf("找不到子字符串\n");
            return 0;
        }
        printf("第一个子字符串%s距离字符串首地址%d个字节", substr, p-str);
        return 0;
    }
    char* my_strstr(char *str, char *substr)
    {
        if(str==NULL || substr==NULL){
            printf("无效字符串\n");
            return NULL;
        }
        char *ptr = substr;
        while(*str != '\0'){
            back:
            if(*str == *substr){
                char *p = str;
                while(*substr != '\0'){
                    if(*str == *substr){
                        str++;
                        substr++;
                    }
                    substr = ptr;
                    goto back;
                }
                return p;
            }
            str++;
        }
        return NULL;
    }

 

习题12:从手机短信格式中解析信息 "+CMGR:REC UNREAD,+8613857607035,23/01/03,19:31:24+00,你好吗?",写成一个函数。

  1. 思路:通过二级指针引用普通指针数组
    f1. 封装解析短信信息的API: int msg_deal(char *msg_src, char *delim, char **msg_done); 
     形参是短信来源字符串、分隔符、调用字符指针数组的二级指针msg_done
    	f1.1 调用strtok函数,用delim分割msg_src,获取首个子字符串首地址,保存在字符指针数组第0个元素:
    		int i = 0;	//i是字符指针数组的下标
    		msg_done[i] = strtok(msg_src, delim);
    	f1.2 while循环,控制循环的变量是分割到的子字符串首地址msg_done[i],地址不为NULL 时,进入循环
        	f1.2.1 修改代表字符指针数组下标的循环变量i的值: i++;
    		f1.2.2 调用strtok函数,从msg_src中分割子字符串,子串的首地址保存在字符指针数组第i个元素:
    			msg_done[i] = strtok(NULL, delim);
    	f1.3 返回i的值,因为我们顺便用i记录子字符串的个数
    1. 初始化或输入一个字符串,代表短信来源字符串,只可以放在栈区、堆区,比如: 
    	char msg_src[100] = "+CMGR:REC UNREAD,+8613857607035,23/01/03,19:31:24+00,你好吗?";
    2. 初始化一个字符串,代表分隔符,可以放在栈区、堆区、常量区,这里规定用逗号分割:
    	char *delim = ",";
    3. 定义一个字符指针数组,一会用来存放分割来的字符串的首地址: char *p[6];
    4. 调用API1. 将msg_src用delim分割,字符串首地址保存在字符指针数组中: 
    	msg_deal(msg_src, delim, p);
    5. 打印字符串个数num,以及短信信息内容。

  2. 代码:
    #include <stdio.h>
    #include <string.h>
    int msg_deal(char *msg_src, char *delim, char **msg_done);
    int main(int argc, char const *argv[])
    {
        char msg_src[100] = \
        "+CMGR:REC UNREAD,+8613857607035,23/01/03,19:31:24+00,你好吗?";
        char *delim = ",";
        char* p[6];
        int num;
        num = msg_deal(msg_src, delim, p);    
        printf("字符串个数=%d\n", num);
        printf("状态  :%s\n", p[0]+10);
        printf("手机号:%s\n", p[1]+3);
        printf("日期  :%s\n", p[2]);
        *(p[3] + 8) = '\0';
        printf("时间  :%s\n", p[3]);
        printf("内容  :%s\n", p[4]);
        return 0;
    }
    int msg_deal(char *msg_src, char *delim, char **msg_done)
    {
        int i = 0;
        msg_done[i] = strtok(msg_src, delim);
        while (msg_done[i] != NULL){
            i++;
            msg_done[i] = strtok(NULL, delim);
        }
        return i;
    }

习题13:试着实现atoi的函数功能

  1. 思路:
    f1. 封装实现atoi功能的函数: int my_atoi(const char *str);
    	f1.1 判断str字符串的首个字符是否是'-',
        	f1.1.1 如果是,
            	f1.1.1.1 令记录数值正负号的变量flag为-1: flag = -1; // 一开始定义成 int flag = 1;
                f1.1.1.2 让指针str指向下一个字符: str++;
    	f1.2 while循环,控制循环的变量是*str,当指针str没有偏移到末尾 时,进入循环
        	f1.2.1 判断指针str当前指向的字符是否是'0'或'1'或'2'或'3'或'4'或'5'或'6'或'7'或'8'或'9'
            	f1.2.1.1 如果是,
                	f1.2.1.1.1 修改保存整型数值的变量temp,它等于自身×10,再加上本次读取数字:
    						temp = temp*10 + (*str-'0');//temp一开始初始化成0,万一找不到就返回0
    				f1.2.1.1.2 让指针str指向下一个字符: str++;
    			f1.2.1.2 否则,
                			那么说明碰到别的字符,需要用break提前退出循环
    	f1.3 通过flag判断整型数是否是负数,
        	f1.3.1 如果是,
            		那么,返回 (-1)*temp;
    		f1.3.2 否则,
            		返回 temp;

  2. 代码:
    #include <stdio.h>
    int my_atoi(const char *str);
    int main(int argc, char const *argv[])
    {
        printf("%d\n", my_atoi("-01234.a"));
        return 0;
    }
    int my_atoi(const char *str)
    {
        int flag = 1;
        int temp = 0;
        if (*str == '-'){
            flag = -1;
            str++;
        }    
        while (*str != '\0'){
            if(*str == '0' || *str == '1' || \
               *str == '2' || *str == '3' || \
               *str == '4' || *str == '5' || \
               *str == '6' || *str == '7' || \
               *str == '8' || *str == '9')
            {
                temp = temp*10 + (*str - '0');
                str++;
            }else{
                break;
            }
        }
        if (flag == -1){
            return (-1)*temp;
        }else{
            return temp;
        }
    }

习题14:试着实现atof的函数功能

  1. 思路:多分支选择控制语句(条件制约)
    f1. 封装实现atof功能的函数: double my_atof(const char *str);
    	f1.1 判断str字符串的首个字符是否是'-',
        	f1.1.1 如果是,
            	f1.1.1.1 令记录数值正负号的变量flag为-1: flag = -1; // 一开始定义成 int flag = 1;
                f1.1.1.2 让指针str指向下一个字符: str++;
    	f1.2 while循环,控制循环的变量是*str,当指针str没有偏移到末尾 时,进入循环
        	f1.2.1 判断指针str当前指向的字符是否是'0'或'1'或'2'或'3'或'4'或'5'或'6'或'7'或'8'或'9'
            并且,小数点符号'.'是否没有出现过,在此之前需要定义:
            		1、代表表示小数点出现次数的变量: int periodOccur=0;
            	f1.2.1.1 如果是,我们开始处理整数部分
                	f1.2.1.1.1 修改保存整型部分数值的变量intPart,它等于自身×10,再加上本次读取数字:
    						intPart = intPart*10 + (*str-'0');	//intPart一开始初始化成0
    				f1.2.1.1.2 让指针str指向下一个字符: str++;
    			f1.2.1.2 否则,判断str当前指向的字符是否是小数点符合'.',并且'.'是否没有出现过
                	f1.2.1.2.1 如果是,
                    //内在逻辑:我们通过变量periodOccur制约分支条件1.2.1和1.2.1.2,第一个'.'有效
                    	f1.2.1.2.1.1 修改代表小数点'.'出现次数的变量: periodOccur = 1;
    					f1.2.1.2.1.2 让指针str指向下一个字符: str++;
    			f1.2.1.3 否则,判断指针str当前指向的字符是否是'0' ~ '9'
                    f1.2.1.3.1 如果是,我们开始处理小数部分,在此之前,需要定义:
    						1、代表小数数值的变量: double fracPart;
                        	2、代表当前小数点后位数的变量: int decimalPlace;
    					    3、根据当前位数,使用循环确定读取到的小数数值的循环变量: double currentFrac;
                    	f1.2.1.3.1.1 设置循环入口: currentFrac = (*str - '0');
    					f1.2.1.3.1.2 for循环,代表循环次数的循环变量i从0开始, <decimalPlace 时,进入循环
                        	f1.2.1.3.1.2.1 修改循环变量currentFrac: currentFrac = currentFrc / 10;
    					f1.2.1.3.1.3 修改保存小数部分数值的变量fracPart: fracPart += currentFrac;
    					f1.2.1.3.1.4 修改代表当前小数位数的变量: decimalPlace++;
    					f1.2.1.3.1.5 让指针str指向下一个字符: str++;
    			f1.2.1.4 否则,那么说明碰到别的字符,需要用break提前退出循环
    	f1.3 通过flag判断结果是否是负数,
        	f1.3.1 如果是,
            		那么,返回 (-1)*(intPart+fracPart);
    		f1.3.2 否则,
            		返回 (intPart+fracPart);

  2. 代码:
    #include <stdio.h>
    double my_atof(const char *str);
    int main(int argc, char const *argv[])
    {
        printf("%lf\n", my_atof("-01234.24a"));
        return 0;
    }
    double my_atof(const char *str)
    {
        int flag = 1;
        int intPart = 0;
        double fracPart = 0;
        int periodOccur = 0;
        int decimalPlace = 1;
        double currentFrac = 0;
        if (*str == '-'){
            flag = -1;
            str++;
        }    
        while (*str != '\0'){
            if( (*str == '0' || *str == '1' || \
                 *str == '2' || *str == '3' || \
                 *str == '4' || *str == '5' || \
                 *str == '6' || *str == '7' || \
                 *str == '8' || *str == '9') && periodOccur==0 ) //整数部分处理
            {
                intPart = intPart*10 + (*str - '0');
                str++;
            }else if(*str=='.' && periodOccur==0){              //小数部分处理
                periodOccur = 1;
                str++;
            }else if(*str == '0' || *str == '1' || \
                     *str == '2' || *str == '3' || \
                     *str == '4' || *str == '5' || \
                     *str == '6' || *str == '7' || \
                     *str == '8' || *str == '9')
            {
                currentFrac = (*str - '0');
                for(int i=0; i<decimalPlace; i++){
                    currentFrac = currentFrac / 10;
                }
                fracPart = fracPart + currentFrac;
                decimalPlace++;
                str++;
            }else{
                break;
            }
        }
        if (flag == -1){    
            return (-1)*(intPart+fracPart);
        }else{
            return (intPart+fracPart);
        }
    }

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值