06_函数

1 为什么要使用函数

        在实际应用或者项目设计过程中,对于某些功能需要多次使用,可以将其模块化设计,在后续需要使用该部分功能的时候,用户不需要再次编码实现,只需要直接调用已经设计好的接口访问。同时还可以将接口提供其它用户使用。提高项目设计的效率。

        也可以将复杂的项目通过模块化编程设计,降低项目的开发难度。

系统分层设计:

  1. 对于硬件驱动可以通过函数模块设计实现底层工作机制。将函数接口提供给上层应用服务;
  2. 对于上层应用程序可以根据实际应用需求的工作策略,直接调用底层函数接口完成整个功能设计。

2 函数的定义和使用

        所谓的函数,其实质指的是具有特点功能代码的有序集合,实现模块化的设计(模块数据的输入、处理和输出)。将函数模块提供给使用者调用,最终实现代码重用的效果。

2.1 函数的定义

   函数定义的基本语法:

数据类型 函数名称(形参列表)         /* 函数头:提供给使用者调用 */
{
    函数体:函数功能执行代码块;
}
数据类型:表示函数的返回值
    1) 如果函数没有返回值,则使用void表示;
    2) 如果函数有返回值,则使用函数返回值的数据类型,可以是基本数据类型、构造数据类型,还可以是指针
函数名称:表示的是整个模块的访问名称,同时也可以表示函数的地址,主要是提供用户访问。
形参列表:表示的函数的参数,可以作为函数运行数据的输入和输出
    1) 如果不需要输入和输出,表示没有参数直接使用void表示;
    2) 函数有参数:可以是一个或者多个参数
        a. 每一个参数包含两个部分: 数据类型 形参名称     数据类型可以是基本数据类型也可以是指针类型。
        b. 如果是多个参数,参数之间使用逗号分隔。
函数体:函数的功能实现代码,注意函数体的结束:
    1) 语句块执行完成;
    2) 在遇到return语句的时候结束:
        a.如果函数没有返回值,可以直接使用return结束;
        b.如果函数有返回值,在return语句后面跟上返回的数据值。

函数定义的实例

#include <stdio.h>
/* 定义一个返回值为int,没有参数的函数名为main的函数 */
int main(void)
{        
        return 0;
}
/* 定义一个没有返回值也没有参数的函数名为test的函数 */
void test(void)
{
        return ;
}
/* 定义一个有两个参数为int返回值为int的add函数 */
int add(int a, int b)
{
        return a+b;
}

2.2 函数的使用

2.2.1 函数的声明

        在函数调用之前,需要对函数进行声明(函数已经存在,使用者可以直接调用)

函数声明的实现:在函数调用文件中,函数调用之前

       extern 数据类型 函数名称(形参列表);

注意:在函数声明的时候

  •  形参列表中的形参可以省略形参的名称,不能省略形参的数据类型
  • 在做函数声明的时候可以省略关键字extern。

1.函数定义和使用不在同一个文件中,需要做函数的声明(特殊情况下编译器会做优化)。

  • 直接在函数使用的中直接声明;
  • 也可以将函数的声明添加到头文件中,在函数调用的文件中,使用#include包含头文件方式声明。

2.函数的定义和使用在同一个文件:

  1. 定义在使用之前,可以不用再次作声明(定义的同时完成函数声明);
  2. 定义在使用后,需要在函数调用之前做声明。
#include <stdio.h>            /* 对于系统函数、库函数、以及其它设计者设计的函数,可以使用头文件中做声明 */

//int printf (const char *__restrict __format, ...);    /* 声明其它文件中定义的函数 */
void test2(void);         /* 声明当前文件中定义的函数 */

void test1(void)         /* 函数调用之前定义,同时完成函数的声明和函数的定义 */
{
        printf("test1\n");
}

int main(void)
{
        printf("func extern\n");
        test1();
        test2();
}

void test2(void) 
{
        printf("test2\n");
}

2.2.2 函数头文件的实现

1.函数调用的时候,头文件的包含

#include <stdio.h>            /* 如果所需要包含的头文件是系统定义和库定义使用尖括号<>来包含 */
#include "func.h"             /* 如果所需要包含的头文件是用户自定义的头文件使用双引号""来包含 */

2.头文件内容中,有固定语句需要

#ifndef XXXXX        /* xxxxx是标识符号 */
#define XXXXX
            /* 之间:实现函数和全局数据变量的声明和数据类型的定义 */
#endif

整个三条语句作用是防止头文件被重复展开替换。避免函数或者数据类型被重复定义。

3.函数调用实例

        a. func.h文件内容

/* 头文件部分 */
#ifndef _FUNC_H_
#define _FUNC_H_

void func1(void);
void func2(void);

#endif

        b. func.c文件内容

#include <stdio.h>

void func1(void)
{
        printf("func1\n");
}

void func2(void)
{
        printf("func2\n");
}

c.  app.c文件内容

#include <stdio.h>

#include "func.h"

int main(void)
{
        func1();
        func2();
}

编译的时候gcc func.c app.c => a.out

4.函数定义和头文件分离意义

更方便的提供给使用者使用。对于使用者不需要单独的做函数的声明。直接包含头文件,就可以实现对函数的访问。

3 函数参数

3.1 函数的传参方式

     在C语言函数中,函数的参数传递有三种方式,分别是值传参、地址传参和全局变量传参。

3.1.1 值传参

     所谓的值传参,指的是在函数调用的时候,创建形参的存储空间,并将实参数据的值复制拷贝传递给形参,实参数据和形参数据的空间的是相互独立的空间,在函数调用的时候实参会改变形参值,但是形参不会改变实参的值。

    在函数调用结束的时候,通过return返回的数据值,将结果复制拷贝实现。

#include <stdio.h>

int add(int a, int b)
{
        int c;
        c = a+b;
    
        printf("&a = %p, &b = %p, &c = %p\n", &a, &b, &c);

        return c;        /* 函数返回:将c的值复制拷贝返回给主函数中接收变量z */
}

int main()
{           
        int x = 4;
        int y = 5;

        int z;

        z = add(x, y);     /* 函数的调用,将实参x和y的值复制拷贝给形参a和b; */
        printf("z = %d\n", z); 
        printf("x = %d, y = %d, z = %d\n", x, y, z); 
        printf("&x = %p, &y = %p, &z = %p\n", &x, &y, &z);

}

特征:在函数调用的时候,可以将实参的值复制拷贝给形参,但是在函数中对形参数值的修改,不会修改实际参数数据的值。

3.1.2 地址传参

        所谓的地址传参,指的是在函数调用的时候,将实参数数据的地址复制拷贝传递给形参变量接收,形参(地址)指向实参空间,可以在函数体中通过形参(作为地址值)通过指针引用访问实参变量的空间(可以实现数据的修改)。

#include <stdio.h>

void swap(int x, int y)
{
        printf("%d: x = %d, y = %d\n", __LINE__, x, y);
        int tmp = x;
        x = y;
        y = tmp;
        printf("%d: x = %d, y = %d\n", __LINE__, x, y);
}

void Swap(int *p, int *q) 
{
        printf("%d: *p = %d, *q = %d\n", __LINE__, *p, *q);
        int tmp = *p; 
        *p = *q; 
        *q = tmp;
        printf("%d: *p = %d, *q = %d\n", __LINE__, *p, *q);                     
}

int main()
{
        int a = 5;
        int b = 3;
        printf("%d: a = %d, b = %d\n", __LINE__, a, b);    /* a = 5, b = 3 */
        swap(a, b);
        printf("%d: a = %d, b = %d\n", __LINE__, a, b);    /* a = 5, b = 3 */
        Swap(&a, &b);
        printf("%d: a = %d, b = %d\n", __LINE__, a, b);    /* a = 3, b = 5 */
}

特征:将实参的地址复制拷贝给形参,形参(地址)指向实参空间,可以使用形参对实参数据的修改。

3.1.3 全局变量传参

        所谓的全局变量传参,是因为全局变量的作用域为整个程序域,可以在程序的任意模块中访问到全局变量,从而可以实现程序中任意模块之间数据的交互,从而实现数据传参的效果。

#include <stdio.h>

int val = 123;        /* 全局变量 */

void setVal(int value);
void prtVal(void);

int main()
{
        prtVal();
        setVal(12);
        prtVal();
}

void setVal(int value)
{
        val = value;        /* 修改全局变量的值 */
}

void prtVal(void)
{
        printf("val = %d\n", val);    /* 读取全局变量的值 */
}

特征:

        1.全局变量作为参数的传递,简化参数的设置;

        2.模块必须依赖于全局变量的存在,模块的可移植性较差。

3.2 数组作为参数传递

        数组作为形参的时候,表示的是数组元素类型的指针,在做参数传递的时候,可以用来传递数组首元素的地址。而对于数组元素的个数需要使用另外的参数来传递。

#include <stdio.h>

//void prt_arr(int p[10], int size);    /* 函数的声明 */
void prt_arr(int *p, int size);       /* 函数 void prt_arr(int p[10], int size) 的声明*/
/* 就是数组在作为形参的时候,表示的是数组元素类型的指针,数组中的常量表达式没有实际意义。 */
int main()
{
        int arr[10] = {0,1,2,3,4,5,6,7,8,9};

        prt_arr(arr, sizeof(arr)/sizeof(arr[0]));
        return 0;
}

void prt_arr(int p[10], int size)
{
        int i;

        for (i = 0; i < size; i++) {
                printf("p[%d] = %d\n", i, p[i]);
        }   
}

3.3 主函数的参数

        所谓的主函数的参数,指的是程序运行的时候,可以通过终端运行并传递参数值。具体的参数形式:

    1.主函数无参:

void main(void)        /* 参数列表void表示没有参数,可以省略void;如果有返回值,可以设置返回值的数据类型 */

   2.主函数有参数:

void main(int argc, char *argv[])    
参数:
    参数1:argc,统计在命令行终端运行时候传递参数字符串的个数;
    参数2:argv:是char *数据类型数组首元素地址,接收命令行终端输入的字符串数据。

3.主函数参数实例

#include <stdio.h>

int main(int argc, char *argv[])
{
        int i;

        for (i = 0; i < argc; i++) {
                printf("argv[%d] : %s\n", i, argv[i]);
        }
        return 0;
}

4 指针函数

       所谓的指针函数,指的是函数的返回值类型为指针的函数,称为指针函数;

1.指针函数的定义语法:

数据类型 * 函数名称(形参列表) 
{
    函数体:有序执行指令集合;
    return 指针;    
}

2.指针函数实例:

#include <stdio.h>

 /* 定义一个指针函数: */
char * my_strcpy(char *dest, const char *src)
{
        char *p = dest;

        while(*src != '\0') {
                *p++ = *src++;  
        }
        *p = '\0';

        return dest;
}
int main()
{
        char dest[32] = "1234567890-";
        char src[] = "ikun";

        my_strcpy(dest, src);
        printf("dest : %s\n", dest);
    
        printf("dest :%s\n", my_strcpy(dest, "abcdefg"));    /* 指针函数的返回值作为其它函数参数,实现数据的链式存储 */


}

3.指针函数的意义:

  • 作为指针函数的返回值可以作为其它函数的参数,实现数据的链式存储表达;
  • 在函数调用的时候,可以使用返回值来判断函数调用是否异常。在异常调用的情况下返回值为NULL。

5 函数指针

        所谓的函数指针,其实质是指针,指针指向的函数定义的存储空间。也就是指针存储的是函数存储空间的起始地址,表示为函数指针。

5.1 函数指针的定义和使用

        1.函数指针的定义语法:

数据类型 (*函数指针变量名称)(形参列表);    
    数据类型:函数指针所指向函数的返回值数据类型;
    形参列表:函数指针所指向函数的形参列表。

        2.函数指针实例

#include <stdio.h>

int add(int a, int b);

int main()
{
        int x = 12;
        int y = 3;
        int z;    
        int (*p)(int, int);        /* 函数指针的定义:函数指针所指向函数的返回值数据类型和形参数据类型 */

        printf("%d\n", add(x, y));
        p = &add;                  /* 将add函数的地址初始设置给函数指针变量p,设置函数指针指向的函数为add函数 */
        z = (*p)(x, y);            /* 通过函数指针引用访问所指向的函数 */ 
        printf("z = %d\n", z); 
        
        p = NULL;
        p = add;                  /* add函数名称也表示的函数本身的地址,将地址设置给函数指针变量p */
        printf("%d\n", p(x, y));
  /* 在对于函数的访问的时候,可以只用通过函数指针访问 */
        return 0;
}

int add(int a, int b)
{
        return a+b;
}

3.函数指针使用:

  • 在整个函数指针的初始化的时候,需要保证函数的参数和返回值类型一致。
  • 函数名也可以表示函数的地址。
  • 函数的访问,可以通过函数名访问,也可以通过函数的地址访问。

5.2 函数指针数组

        所谓的函数指针数组,其实质是数组,数组元素是函数指针

1.函数指针数组定义语法

数据类型 (*函数指针数组变量名[常量表达式])(形参列表);
    数据类型:数组元素(函数指针)所指向函数的返回值数据类型;
    形参列表:数组元素(函数指针)所指向函数的形参列表;
    函数指针数组变量名:数据集合名称;
    常量表达式:表示数组集合中元素的个数。

2.函数指针数组元素的访问

        由于实质是数组,在访问的时候,通过数组名称[下标]逐一元素访问,不能整体访问。

每次访问到数组元素是函数指针,需要对函数的引用。

3.函数指针数组实例

#include <stdio.h>

typedef int FUNC(int, int) ;    /* 将返回值类型为int,参数类型为int,int的函数取别名为FUNC类型 */
typedef int (*FUNCP)(int, int);    /* 重定义数据类型FUNCP,是函数指针类型,所指向函数的返回值类型为int,参数类型为int,int */

int add(int a, int b)
{
        return a+b;
}

int sub(int a, int b)
{
        return a-b;
}

int main()
{
#if 0
        int (*arr[2])(int, int) = {add, sub};    /* 定义函数指针数组并初始化数组元素 */
#else
        FUNC *arr[2] = {add, sub};            /* 定义函数指针数组并初始化数组元素 */
        FUNCP p[2] = {add, sub};              /* 定义函数指针数组并初始化数组元素 */
#endif                                                                   
        int i;
        int x = 12;
        int y = 3;

        for (i = 0; i < sizeof(arr)/sizeof(arr[0]); i++) {
                printf("%d\n", arr[i](x, y));    /* 数组元素的逐一访问,数组元素是函数指针,需要对函数的引用访问 */
        }   
}

5.3 函数指针作为函数的参数

5.3.1 函数指针作为参数的实现

        函数指针,可以存储函数的地址,可以通过地址来实现函数的访问功能。可以将函数指针作为函数的参数,在函数调用的时候可以传递不同函数的地址,从而到同一个函数去实现不同的功能。

        一般在具有相同逻辑功能,不同数据类型数据的处理的时候,可以通过函数指针来做函数的参数,实现代码的重复使用(实现相同的逻辑,不同功能的操作)。

#include <stdio.h>
int add(int a, int b)
{
        return a+b;
}

int sub(int a, int b)
{
        return a-b;
}

int operator(int m, int n, int (*funcp)(int, int))
{
        return funcp(m, n); 
}

int main()
{
        int x = 12;
        int y = 3;
        /* 通过函数指针作为参数:实现不同数据类型数据的相同逻辑运算处理 */
        printf("%d\n", operator(x, y, add));    /* 实现对add函数的调用 15 */     
        printf("%d\n", operator(x, y, sub));
    /* 实现对sub函数的调用 9 */
}

5.3.2 回调函数的理解

        函数的意义是功能的模块化设计,也是对设计者和使用者的分离。在某些特定的情况,对于设计者不明确具体功能的实现,明确功能实现的逻辑;对于使用者明确具体的功能需求,但是无法完成内部的逻辑。此时可以功能的实现的逻辑有设计者实现,功能的具体需求由使用者在调用设计者提供的接口的时候,告知设计者。此时就需要通过回调函数实现。也就是函数的参数为函数指针所指向的函数。

具体的应用:

  1. 需要实现不同数据类型的数组数据的排序,整体逻辑是一致,由于数据类型不同的比较功能实现算法不同。
  2. 信号处理方式实现;
  3. 多线程的实现:

回调的具体:

        使用者在调用由设计者设计的(系统接口或者库函数接口)的时候,需要由使用者传递具体功能实现函数给设计者,设计者在回调使用者的功能方法。

  1. 设计库函数(一般只需要调用就ok)
1. 编辑程序mytest.c文件:
int operator(int m, int n, int (*funcp)(int, int))
{
        return funcp(m, n); 
}

2. 编译生成库程序
    gcc -c mytest.c     //生产mytest.o二进制文件
    ar crv libmytest.a mytest.o    //将二级制文件mytest.o生产静态库文件:libmytest.a 库名称为mytest

2.使用者使用静态库

1. 使用者的main.c文件
#include <stdio.h>

int operator(int m, int n, int (*funcp)(int, int));        /* 一般情况下,设计者会设计头文件进行函数声明,只需要包含头文件 */

int add(int a, int b)
{
        return a+b;
}

int sub(int a, int b)
{
    return a-b;
}

int main()
{
        int x = 12;
        int y = 3;
        /* 通过函数指针作为参数:实现不同数据类型数据的相同逻辑运算处理 */
        printf("%d\n", operator(x, y, add));    /* 实现对add函数的调用 */
        printf("%d\n", operator(x, y, sub));
    /* 实现对sub函数的调用 */
}

编译应用程序:
    gcc main.c -L . -lmytest    /* -L .指定依赖库的路径为当前路径, -lmytest 链接依赖库mytest */

6 递归函数

6.1 递归函数的设计实现

        对于递归函数,主要分为两个阶段:递推阶段和回归阶段,主要用当前算法在满足递归算法思想。此时可以使用递归函数实现。

        所谓的递归算法,指的是在运算的过程中,当前数据运算和当前之外剩下的数据运算算法一致,就满足递归算法。应用比较多的地方:树形结构的遍历、图形结构的遍历。

在整个递归算法中注意:

  • 整个算法是否满足递归实现算法;
  • 递推算法的结束条件,决定算法是否能够处理结束。
#include <stdio.h>

unsigned int factorial(unsigned int num)
{
        if (num == 0)        /* 递推过程的结束条件:0! = 1 */
                return 1;

        return num * factorial(num-1);    /* 递归算法:num! = num * (num-1)! */
}

int main()
{
        printf("%d\n", factorial(5));
}

6.2 递归函数的实现

  1. 在函数内部调用函数自己,那么该函数存在递归实现。本身函数就是递归函数
  2. 在有多个函数的时候,如果函数之间相互调用,也会存在递归实现。例如函数A和函数B,在函数A中调用函数B,在函数B中调用函数A
  3. 在使用递归的时候,需要谨慎使用,注意结束条件,避免出现死循环。
  4. 所有的递归实现,都可以采用非递归方式实现。对于递归实现的逻辑更简单,所消耗的资源却更多。

7 字符串处理函数

7.1 字符串

        所谓字符串,指的是由多个连续字符构成的集合,以双引号("")包含,以字符'\0'结束;

1.字符串常量:

        不能被修改的字符串数据量,每一个字符数据都不可以被修改。

        "hello"、"test"

对于字符串,起始地址可以使用变量存储:

        char *p = "hello";

在内存中开辟两段连续存储空间:

        1. 字符串常量存储空间(位于常量区,空间大小由字符串中字符个数+1(6字节));

        2. 指针变量p存储空间,空间的大小(32位系统4字节,64位系统8字节)。使用指针变量p读字符串常量数据,不能写数据

2.字符串变量:

        字符串中的数据可以被修改,一般情况下可以是字符数组定义方式和动态内存管理的方式来开辟空间

        char str[] = "hello";

        在内存中开辟一段连续存储空间:将字符串数据赋值给存储空间。可以对str存储空间数据的读和写访问。

7.2 字符串处理函数

7.2.1 计算字符串长度-strlen

  1. 函数原型
#include <string.h>
size_t strlen(const char *s);
参数:s表示长度计算字符串空间的起始地址
返回值:返回字符串中字符个数,遇到'\0'结束

      2.函数自定义实现

size_t my_strlen(char *s)
{
        size_t len = 0;

        while(*s++ != '\0') {
                len++;
        }

        return len;
}

7.2.2 字符串拷贝-strcpy(strncpy)

        1.函数原型

#include <string.h>
char *strcpy(char *dest, const char *src);
功能:将src指向的源字符串数据拷贝到dest目标空间中
参数:
    参数1:dest指针指向的目标存储空间
    参数2:src指针指向的源字符串数据存储空间
返回值:返回的是目标存储空间起始地址            
char *strncpy(char *dest, const char *src, size_t n);
功能:将src指向的源字符串数据的前n个字符数据拷贝到dest目标空间中
参数:
    参数1:dest指针指向的目标存储空间
    参数2:src指针指向的源字符串数据存储空间
    参数3:n表示的需要拷贝的字符个数
        1) n > strlen(src) 等价于strcpy(dest, src);
        2) n <= strlen(src) 值拷贝前n个字符。
返回值:返回的是目标存储空间起始地址

        2.自定义实现

char *my_strcpy(char *dest, const char *src)
{
        size_t i;
        for (i = 0; src[i] != '\0'; i++) {
                dest[i] = src[i];
        }
        dest[i] = src[i];
        
        return dest;
}        

char *
my_strncpy(char *dest, const char *src, size_t n)
{
    size_t i;
    for (i = 0; i < n && src[i] != '\0'; i++)
        dest[i] = src[i];
    for ( ; i < n; i++)
        dest[i] = '\0';
        

    return dest;
}

7.2.3 字符串的连接函数-strcat/strncat

        1.函数的原型

#include <string.h>
char *strcat(char *dest, const char *src);
功能:将src指向字符串数据连接到dest字符串数据的后面,从dest字符串的'\0'位置开始;
参数:
    参数1:dest指针指向的目标存储空间
    参数2:src指针指向的源字符串数据存储空间
返回值:目标存储空间dest的起始地址
char *strncat(char *dest, const char *src, size_t n);
功能:将src指向字符串数据的前n个字符连接到dest字符串数据的后面,从dest字符串的'\0'位置开始;
参数:
    参数1:dest指针指向的目标存储空间
    参数2:src指针指向的源字符串数据存储空间
    参数3:n表示连接的字符个数:
        如果 n > strlen(src) 等价于 strcat(dest, src);
        如果 n <= strlen(src),只连接前n个字符。
返回值:目标存储空间dest的起始地址

        2.自定义实现

char * my_strcat(char *dest, const char *src)
{
    size_t dest_len = strlen(dest);
    size_t i;
    
    for (i = 0; src[i] != '\0'; i++) {
        dest[dest_len+i] = src[i];
    }
    dest[dest_len + i] = '\0';
    
    return dest;
}

char * my_strncat(char *dest, const char *src, size_t n)
{
    size_t dest_len = strlen(dest);
    size_t i;
    
    for (i = 0 ; i < n && src[i] != '\0' ; i++)
        dest[dest_len + i] = src[i];
    dest[dest_len + i] = '\0';

    return dest;
}

7.2.4 字符串比较函数-strcmp/strncmp

        1.函数原型

#include <string.h>
int strcmp(const char *s1, const char *s2);
功能:比较s1字符串和s2字符串内容的大小,在比较的过程是比较相同序号字符的ASCII编码值。
返回值:
    s1 > s2 返回1;
    s1 == s2 返回0;
    s1 < s2 返回-1;
int strncmp(const char *s1, const char *s2, size_t n);
功能:比较s1字符串和s2字符串的前n个字符内容的大小,在比较的过程是比较相同序号字符的ASCII编码值。

2.自定义函数

int my_strcmp(const char *s1, const char *s2)
{
        size_t i;

        for (i = 0; s1[i] != '\0' && s2[i] != '\0'; i++ )
        {
                if (s1[i] > s2[i])
                        return 1;
                if (s1[i] < s2[i])
                        return -1;
        }

        if (s1[i] == s2[i])
                return 0;

        if (s1[i] == '\0')
                return -1;
        if (s2[i] == '\0')
                return 1;
}

7.2.5 字符串中查找子串-strstr

        1.函数原型

#include <string.h>
char *strstr(const char *haystack, const char *needle);
功能:在haystack字符串中查找needle子字符串
返回值:成功返回needle子串在haystack字符串中第一次出现的起始地址;失败返回NULL

        2.自定义实现

char *my_strstr(const char *haystack, const char *needle)
{
        const char *p = haystack;
        const char *q;
        const char *sub;

        /* p是对父字符串数据的遍历 */
        while(*p != '\0') {        
                if (*p != *needle) {    /* 在父串中的字符和子串的起始字符不相同,直接遍历父串的下一个字符 */
                        p++;
                        continue;
                }
                /*在父串中的字符和子串的起始字符相同 */
                q = p;
                sub = needle;
                while(*sub != '\0') {    /* 遍历子串中的内容 */
                        if (*q != *sub)    /* 和父串内容不同,结束子串遍历 */
                                break;
                        /* 和父串内容相同,父串和子串向后遍历一个元素 */                                        
                        q++;
                        sub++;
                }
                /* 子串遍历结束,说明子串在父串中有出现 */
                if (*sub == '\0')
                        return p;
                p++;
        }

        return NULL;
}

7.2.6 数值型字符串数据转换数值数据-atoi

        1.函数原型

#include <stdlib.h>
int atoi(const char *nptr);
功能:将数值字符串数据转换为int类型数值
long atol(const char *nptr);
功能:将数值字符串数据转换为long类型数据
long long atoll(const char *nptr);
功能:将数值字符串数据转换为long long类型数据

注意:

1) 所谓的数值字符串数据,指的是字符由字符'0' ~ 字符'9'内的连续多个字符构成的数据。

2) 在转换实现的时候,遇到非该范围内的字符就结束。

3) 如果起始字符在非该范围内的其它字符,则转换后的返回值为0.

        2.自定义实现

int my_atoi(const char *nptr)
{
        const char *p = nptr;
        int num = 0;

        while(*p >= '0' && *p <= '9') {
                num = num * 10 + (*p - '0');
                p++; 
        }
    
        return num;
}

7.2.7 sprintf函数和sscanf函数

        1.sprintf函数

int sprintf(char *str, const char *format, ...);
功能:将数据格式化到str指针指向的缓存空间中
参数:
    参数1:str表示格式化输出缓存空间的起始地址;
    参数2:format是可变参数,进行数据的格式化输出,类似于printf函数,
        其中printf函数将数据格式化到标准输出,而sprintf将数据格式化到字符数组缓存空间中

        2.sscanf函数

#include <stdio.h>
int sscanf(const char *str, const char *format, ...);
功能:格式化输入数据
参数:
    参数1:str表示需要格式化输入缓存空间的起始地址;
    参数2:format是可变参数,进行数据的格式化输入,类似于scanf函数
        其中scanf函数从标准输入格式化数据,而sscanf函数是从字符数组缓存空间格式化输入。

        3.sprintf和sscanf函数的应用

int main()
{
        int tem = 23;
        int hum = 34;
        int light = 1234;
        char buf[64];
        /* 数据的组包 */
        sprintf(buf, "tem:%d,hum:%d,light:%d", tem, hum, light);    /* 格式输出到字符数组缓存 */
        printf("buf : %s\n", buf);

        /* 数据的拆包 */
        sscanf(buf, "tem:%d,hum:%d,light:%d", &light, &tem, &hum);    /* 从字符数组缓存格式化输入 */
        printf("tem = %d, hum = %d, light = %d\n", tem, hum, light);
}        

  • 27
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值