• C语言-函数


    参考

    https://cplusplus.com/reference/clibrary/

    1. 概念

    函数必须先定义后调用,所以main要放在最后
    函数不能嵌套定义,没有闭包函数的概念

    1.1 基础概念

    1.1.1 形参

    形参变量只有在函数被调用时才会分配内存,调用结束后,立即释放内存,所以形参变量之后在函数内部有效
    中分配内存

    1.2.1 实参

    实参可以是常量、变量、表达式(表达式必须有返回值)、函数等,无论实参是何种类型的数据,在调用函数时都必须有确定的值
    实参和形参在数量上、类型上,顺序上必须严格一致,可以自动进行类型转换
    实参、形参可以同名,但他们之间是相互独立的,互不影响。(实参在函数外部有效,形参在函数内部有效)

    1.3.1 函数返回值

    有返回值 typename func(){return val};
    无返回值 void func(){}; 可以不写return

    1.3.1 回调

    函数f1调用函数f2的时候,函数f1通过参数给函数f2传递了另一个函数f3的指针,在函数f2执行的过程中,函数f2调用了函数f3,这个过程就叫做callback
    这个先被当做指针传入,后面又被回调的函数f3就是回调函数

    1.4.1 函数声明

    type func(type1 param1,type2 param2);
    type func(type1,type2); // 声明时可以不写形参,只写数据类型
    
    • 1
    • 2

    1.4.1.1 多文件定义

    函数声明写在头文件中,链接阶段 找到函数体
    一般函数都放在源文件中,这些源文件都已经提前编译好了,并以静态链接库或动态链接库的形式存在

    1.2 变量

    数据是放在内存中的,变量(以及指针、引用)是给这块内存起的名字,有了变量就可以找到并使用这份数据

    1.2.1 左值/右值

    左值:指向内存位置的表达式, 叫做左值表达式(存储在内存中,有明确存储地址(可寻址)的数据)
    右值:内存中某些地址的数值(可以提供数据值的数据(不一定可以寻址,例如存储于寄存器中的数据))

    1.2.2 全局变量

    全局变量存储在内存分区中的全局数据区,这个区域中的数据在程序载入内存后被初始化为0,也就是全局变量默认的初始值为0
    全局变量修改后,会影响其他函数
    全局变量的作用范围不是从变量定义到该文件结束,在其他文件中也有效

    • static

    静态变量,仅限于当前文件内部调用
    内部链接

    • extern

    外部存储变量,用于声明在当前文件中将要用到的其他文件中的变量
    外部链接

    1.2.3 局部变量

    局部变量根据C++标准的不同,可能是随机值,也可能是0
    main()函数中定义的变量也是局部变量
    空链接

    • auto

    自动变量,离开定义的函数立即消失

    • register

    寄存器变量,离开定义的函数立即消失

    • static

    静态变量,离开定义的函数仍然存在

    1.2.4 作用域

    全局变量和局部变量
    全局变量:的作用域是全部的文件,包括.c和.h文件
    全局变量的作用范围不是从变量定义到该文件结束,在其他文件中也有效

    {code...} 单独的代码块也是一个作用域

    static 全局变量 作用域变成了当前文件

    for,while会引入新的作用域,(块级作用域,C/cpp的特性)

    注意!!!

    int main()
    {
        printf("%d\n",c); // 这里是访问不到全局变量c的
    }
    
    int c=10;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    注意2

    #include 
    using namespace std;
    
    int a;
    
    void func()
    {
    	a = 10;
    }
    
    int main()
    {
    	std::cout << a << endl; // 0
    	func();
    	std::cout << a << endl; // 10
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    1.3 常量

    • 数值常量
    • 字符常量/字符串常量
    • 枚举常量

    常量可以是任何基本的数据类型,可以理解为字面量
    常量的值在定义后 不能进行修改

    const 修饰的变量,也可以是常量,实际上是只读变量(不能用于switch case)

    定义常量

    • #define 预处理器
    • const 关键字
    // #define
    #define Variable value // 注意没有分号 可以定义字符型、字符串型的常量
    
    // const
    const type variable=value; // 注意有分号,且必须直接赋值
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1.4 存储类进阶

    https://www.runoob.com/cprogramming/c-storage-classes.html

    作用域:块作用域、文件作用域
    连接:内部链接、外部链接、空链接
    存储时期:静态存储时期、动态存储时期

    块作用域:局部变量
    文件作用域:全局变量
    
    空链接+块作用域:局部变量
    外部链接+文件作用域:全局变量 (其他文件也可以使用)
    内部链接+文件作用域:静态全局变量 (只在当前文件使用)
    
    具有文件作用域的变量 --> 具有静态存储时期 (全局变量、静态全局变量)
    具有块作用域的静态变量 --> 具有静态存储时期 (静态局部变量)
    具有块作用域的普通变量 --> 具有动态存储时期 (普通变量)
    
    static 表明的是内部链接,而不是表明存储时期
    
    // main.cpp
    int a=1; // 外部链接+文件作用域:全局变量 (其他文件也可以使用)
    static b=2; // 内部链接+文件作用域:静态全局变量 (只在当前文件使用)
    
    int main()
    {
    	int c=3; // 空链接+块作用域:局部变量
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 自动存储类 (普通局部变量) (块作用域、空链接)
    • 寄存器存储类 (register 局部变量) (块作用域、空链接)
    • 具有块作用域的静态存储类 (static 局部变量)(块作用域、内部链接)
    • 具有外部链接的静态存储类 (普通全局变量)(文件作用域、外部链接)
    • 具有内部链接的静态存储类 (static 全局变量)(文件作用域、内部链接)

    1.4.1 初始化注意

    https://www.cnblogs.com/iBoundary/p/15014843.html
    https://blog.csdn.net/weixin_43609874/article/details/123903650
    https://blog.csdn.net/u014552102/article/details/126494557

    c语言中,全局变量、静态变量(全局静态变量、局部静态变量)不能用变量赋值,只能用常量赋值
    注意:是在C语言中,在C++中是可以的!!!

    例子1

    int a=10;
    int b=a; // initializer element is not constant 表达式必须含有常量值
    
    int main()
    {
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    例子2

    int a=10;
    static int b=a; // initializer element is not constant 表达式必须含有常量值
    
    int main()
    {
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    例子3

    #include 
    #include 
    
    int main()
    {
        int a=10;
        static int b=a; // ERROR
    
        printf("%d\n",b);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    例子4

    下面这个是对的,&a 是常量,a是变量

    #include 
    #include 
    
    int a=10;
    int *b=&a; // initializer element is not constant 表达式必须含有常量值
    
    int main()
    {
        printf("%d\n",*b);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2. 赋值与参数引用机制

    形参-实参相当于赋值操作

    2.1 赋值

    不同于下面的参数引用机制(地址传递)

    对于数值型

    赋值修改,并不会改变内存地址
    inplace 操作不会改变内存地址(inplace操作也包括自增自减)

    #include 
    int main()
    {
        int a=1;
        printf("%p\n",&a); // 000000000061FE1C
        a=100;
        printf("%p\n",&a); // 000000000061FE1C,地址不变
    	
    	a+=100
    	printf("%p\n",&a); // 000000000061FE1C,地址不变
    	
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2.2 参数引用机制/copy

    形参与函数内的其他局部变量一样,在进入函数时被创建,退出函数时被销毁

    函数内的局部参数、形参,只有在 上分配内存,也就是在函数声明和定义的时候参数没有被创建

    2.2.1 值传递

    把实际的值复制给函数的形参,不共享内存

    值传递只传值 地址互相不干扰、永远不变

    #include 
    int main()
    {
        int a=20;
        int b=10;
        int c=a;
        c+=1;
        printf("a %d\n",a); // 20
        printf("c %d\n",c); // 21
        printf("a %d\n",a); // 20
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    进阶

    想当于复制,内存地址不一样
    且修改B不会影响A

    #include 
    int main()
    {
        int a=20;
        int b=10;
        int c=a;
        printf("%p\n",&a); // 000000000061FE18
        printf("%p\n",&c); // 000000000061FE14
        c+=1;
        printf("a %p\n",&a); // 000000000061FE18
        printf("c %p\n",&c); // 000000000061FE14
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.2.2 引用传递 (地址传递、指针传递)

    形参为指向 实参地址 的指针,当对 形参的指向操作时,就相当于对 实参 本身进行的操作

    通过指针传递地址到函数,实现在函数内部操作函数外部的数据
    数组、字符串、动态分配的内存,不能通过一个参数直接传递,也就是不能进行值传递,必须通过地址指针传递,也不能进行引用传递
    将指针作为形参,地址作为实参进行传递

    #include 
    #include 
    
    // 要是返回一个指针,则要用dataType* 的方式进行定义
    char *strlong(char* s1,char* s2){
        if (strlen(s1)>=strlen(s2)){
            return s1;
        }
        else{
            return s2;    
        }
    }
    
    int main(){
        char s1[30]="hello";
        char s2[30]="world";
        char *s3;
        s3="ex";
    
        s3=strlong(s1,s2);
        printf("%s\n",s3);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    注意事项: 地址传递的地址(地址)依然是副本(深拷贝、地址传递)
    https://blog.csdn.net/llm_hao/article/details/108432323 进阶例子,看算法与数据结构dsa-单链表
    通过地址(指针)传递可以修改指向的内容,但是指针本身是值传递
    (当把一个指针作为参数传递给一个函数或方法时,其实是把指针的副本copy传递给了函数,即把指针的值本身传递到ptr,因此当在函数或方法内部修改指针(注意,不是修改指针所指向的值)时,其实修改的是函数内部指针的副本,而非外部的指针本身)

    void func(int *arr1)
    {
    	
    }
    
    int arr[3]={1,2,3};
    func(a);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.2.3 引用传递和引用符号&的区别

    https://blog.csdn.net/llm_hao/article/details/108432323
    https://blog.csdn.net/L_fengzifei/article/details/128313713!!!

    c语言中没有引用传递
    引用传递是基于指针的
    引用符号&相当于别名,并不是取地址,即共享一段内存(引用传递数值,会修改原始的数据的

    2.3.4 参数传递扩展 c/python

    python的传值方式是按照c++中传指针的方式传值的,即不是引用也不是值。如果对象是可变的,那么操作是在传入对象上操作的,如果是不可变的,那么操作后相当于这个标识符指向了另一个对象
    (c++传指针本质上也是值传递,只不过传的是地址值,这时候由于是值传递地址值不会被修改,但是存放在地址里的实参的值是可修改的,这时候对应于python传入对象可变的话相当于就在原对象地址上修改这个对象。如果对象本身不可变,相当于传递的是指针常量,自然不能修改这个对象)

    python的赋值

    a=1
    a=2
    # 前后两次id(a)不同
    
    • 1
    • 2
    • 3

    2.3 返回值

    注意函数的return的值,与函数声明返回值的类型要对应,否则会发生类型转换

    对于返回值是结构体和类对象时(值传递),为了防止局部对象被销毁,也为了防止通过返回值修改原来的局部对象,编译器并不会返回这个对象,而是根据这个对象先创建一个临时对象(匿名对象)以拷贝的方式进行,然后再将这个临时对象返回

    2.3.1 返回值与局部变量!!!

    https://blog.csdn.net/szm1234/article/details/120864801
    https://www.cnblogs.com/xuhj001/p/3436175.html

    一般的来说,函数是可以返回局部变量的。 局部变量的作用域只在函数内部,在函数返回后,局部变量的内存已经释放了。因此,如果函数返回的是局部变量的值,不涉及地址,程序不会出错。但是如果返回的是局部变量的地址(指针)的话,程序运行后会出错。因为函数只是把指针复制后返回了,但是指针指向的内容已经被释放了,这样指针指向的内容就是不可预料的内容,调用就会出错。准确的来说,函数不能通过返回指向栈内存的指针(注意这里指的是栈,返回指向堆内存的指针,或指向全局数据区、常量区的地址是可以的)

    错误使用局部变量

    数组定义在栈上,是局部变量,函数要返回一个数组的地址,但是当函数结束时,局部变量在内存中的数据被释放了,所以是错误的

    int* func()
    {
        int a[3]={1,2,3};
    	return a;
    }
    
    int main()
    {
        int *b=func();
        
        for (int i=0;i<3;i++)
        {
            cout<<b[i]<<endl;
        }
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    解决方法

    • 外部初始化数组,然后再传递
    • 定义静态局部变量,存储在全局静态数据区的内存,不会因为函数的结束而被释放
    • 使用结构体包裹,返回结构体,返回时结构体进行了深拷贝(但是如果是返回的结构体的指针,依然是不行的!!!)
    #include 
    using namespace std;
    
    struct Arr
    {
        int arr[3];
    };
    
    int* func1(int *arr)
    {
        arr[0]=1;
        arr[1]=2;
        arr[2]=3;
        // static int a[3]={1,2,3};
    	return arr;
    }
    int* func2()
    {
        // int a[3]={1,2,3};
        static int arr[3]={1,2,3}; // 虽然是静态变量,但是变量arr本身只能在func2中使用
    	return arr;
    }
    
    struct Arr func3()
    {
        struct Arr arr;
        arr.arr[0]=1;
        arr.arr[1]=2;
        arr.arr[2]=3;
    
        return arr; // 结构体变量arr本身,只能在func中使用
    }
    
    int main()
    {
        // version1 函数外初始化
        int arr[3];  
        int *b1=func1(arr);
    
        // version2 使用静态局部数据 
        int *b2=func2();
    
        struct Arr b3=func3();
        
        for (int i=0;i<3;i++)
        {
            cout<<b1[i]<<" "<<b2[i]<<" "<<b3.arr[i]<<endl;
        }
    	return 0;
    }
    
    • 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
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    例子2

    这个是可以的,因为字符串属于常量,存在常量区,不会因为是局部变量而释放!!!

    char *func()
    {
        char *s="hello world";
        return s;
    }
    
    int main()
    {
        char *str=func();
        cout<<str<<endl;
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    下面这个是不可以的,因为字符串被当成了字符数组,所以是局部变量被放在了栈中,函数结束,内存会被释放掉!!!

    char *func()
    {
        char s[]="hello world";
        return s;
    }
    
    int main()
    {
        char *str=func();
        cout<<str<<endl;
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    例子3

    堆内存中的数据由程序员释放,函数结束不会被释放,除非整个程序结束

    char *func()
    {
        char *p=(char *)malloc(sizeof(char)*10);
        memset(p,0,10);
        for (int i=0;i<10;i++)
        {
            p[i]=0x20+i;
        }
        return p;
    }
    
    int main()
    {
        char *p=func();
        for (int i=0;i<10;i++)
        {
            cout<<p[i]<<endl;
        }
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    例子补充

    void func(int *arr, int n) {
        arr = (int*)malloc(n * sizeof(int)); // 动态分配内存空间
        for (int i = 0; i < n; i++) {
            arr[i] = i; // 对内存空间进行赋值
        }
    }
    
    int main() {
        int *arr = NULL;
        func(arr, 5); // 调用函数分配内存空间
        for (int i = 0; i < 5; i++) {
            printf("%d ", arr[i]); // 输出内存空间中的值
        }
        free(arr); // 释放内存空间
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    3. 函数范例

    返回类型(return_type):一个函数可以返回一个值;不返回值得时候return_typevoid
    参数(parameter_list):形参列表

    return_type function_name(parmeter_list)
    {
    	...;
    }
    
    • 1
    • 2
    • 3
    • 4

    3. 函数声明与定义

    函数声明会告诉 编译器 函数名称 及 如何调用函数
    函数的实际主体可以单独定义
    形参的名称并不重要,只有参数的类型是必须的

    // 方法1
    int max(int num1,int num2)
    {
    	...
    }
    
    // 方法2
    int max(int num1,int num2); //注意是分号
    
    // 方法3
    int max(int,int)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    3.1 多文件的函数调用与声明

    在A文件中声明和定义,在B文件中调用,则需要在调用函数的文件B的顶部声明函数

    static/extern

    跨文件调用的时候,要加上extern,虽然有些情况下不写extern仍然可以执行(函数不用extern可以,变量不可以)(因为对于函数声明的extern 加不加都是等价的)
    static修饰静态函数,表示该函数只能在当前源文件中被调用去

    4. 函数传递

    4.1 函数传递数组

    #include 
    #include 
    
    // version 1 这里a变成了指针
    void func(int a[],int n)
    {
        for (int i=0;i<n;i++)
        {
            printf("%d\n",a[i]);
        }
    }
    
    
    // version 2 直接使用指针
    void func2(int *a,int n)
    {
        for (int i=0;i<n;i++)
        {
            printf("%d\n",a[i]);
        }
    }
    
    int main()
    {
        int a[10]={1,2,3};
        func(a,sizeof(a)/sizeof(int));
        func2(a,sizeof(a)/sizeof(int));
    }
    
    • 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

    二维数组

    // version 1
    void func(int a[][3],int m,int n)
    {
        for (int i=0;i<m/n;i++)
        {
            
            for (int j=0;j<n;j++)
            {
                printf("%d,%d,%d\n",a[i][j],*(a[i]+j),*(*(a+i)+j));
            }
        }
    }
    
    // version 2
    void func(int (*a)[3],int m,int n)
    {
        for (int i=0;i<m/n;i++)
        {
            
            for (int j=0;j<n;j++)
            {
                printf("%d,%d,%d\n",a[i][j],*(a[i]+j),*(*(a+i)+j));
            }
        }
    }
    
    int main()
    {
        int a[2][3]={1,2,3,4,5,6};
        func(a,sizeof(a)/sizeof(int),sizeof(a[0])/sizeof(int));
        // func2(a,sizeof(a)/sizeof(int));
    }
    
    • 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

    数组与局部变量

    注意局部变量作为指针函数的返回值情况 https://www.cnblogs.com/xuhj001/p/3436175.html

    函数运行结束后会销毁在他内部定义的所有局部数据,包括局部变量、局部数组和形式参数,函数返回的指针请尽量不要指向这些数据,C语言没有任何机制来保证这些数据会一直有效,它们会在后续使用过程中可能引发运行时错误

    // 下面的例子,有的编译器直接报错,提示 返回局部变量的指针
    int *func()
    {
        int n=100;
        return &n;
    }
    
    int main()
    {
        int *p=func(),n;
        n=*p;
        printf("%d\n",n);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这个例子很重要https://blog.csdn.net/szm1234/article/details/120864801

    4.2 函数传递结构体

    // 值传递
    // strcut1=struct2 可以进行赋值,但是相当于副本,二者没有任何关系
    void func(struct struct_name struct_param)
    {
        /*code*/
    }
    struct struct_name struct_param;
    func(strct_param); // 只是传递的副本,并不会修改外部参数,相当于值传递
    
    //地址传递
    void func(struct struct_name* struct_param)
    {
        /*code*/
    }
    struct struct_name struct_param;
    func(&strct_param); // 会修改外部参数,相当于地址传递
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    #include 
    
    #define MaxSize 10
    typedef struct
    {
        int data[MaxSize];
        int length;
    }SqList;
    
    void InitList(SqList *L)
    {
        for (int i=0;i<MaxSize;i++)
        {
            L->data[i]=0;
        }
        L->length=0;
    }
    
    int main()
    {
        SqList L; //等价于Struct L
        InitList(&L);
    
        for (int i=0;i<MaxSize;i++)
        {
           printf("%d\t",L.data[i]);
        }
        printf("\n%d\n",L.length);
        return 0;
    }
    
    • 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

    例子2

    struct Arr
    {
        int arr[3];
    };
    
    struct Arr func3()
    {
        struct Arr arr;
        arr.arr[0]=1;
        arr.arr[1]=2;
        arr.arr[2]=3;
    
        return arr;
    }
    
    int main()
    {
        struct Arr b3=func3();
        
        for (int i=0;i<3;i++)
        {
            cout<<b3.arr[i]<<endl;
        }
    	return 0;
    }
    
    • 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

    例子3

    #include 
    using namespace std;
    
    #define FUNDLEN 50
    
    double sum(double x,double y);
    double sum2(const struct funds *stan);
    double sum3(struct funds stan);
    
    struct funds
    {
        char bank[FUNDLEN];
        double bankfund;
        char save[FUNDLEN];
        double savefund;
    };
    
    double sum(double x,double y)
    {
        return (x+y);
    }
    
    double sum2(const struct funds *stan)
    {
        return (stan->bankfund+stan->savefund);
    }
    
    double sum3(struct funds stan)
    {
        return (stan.bankfund+stan.savefund);
    }
    
    int main()
    {
        struct funds stan={
            "l bank",
            20.1,
            "w save",
            20.2
        };
    
        cout<<sum(stan.bankfund,stan.savefund)<<endl;
        cout<<sum2(&stan)<<endl;
        cout<<sum3(stan)<<endl;
    
        return 0;
    }
    
    • 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
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    5.指针与函数

    5.1 函数名与函数名地址 – 5.3 函数指针

    函数名:可以理解为地址,但是实际上不是地址。函数的首地址,是void()类型
    &函数名:表示以一个指向该函数这个对象的地址 相当于一个指针(指向一个整体)void(*)()类型
    这两个地址值是相等的

    指针函数

    指针作为函数的返回值,指针就是一个地址
    引用传递地址传递
    注意局部变量作为指针函数的返回值情况 https://www.cnblogs.com/xuhj001/p/3436175.html

    函数运行结束后会销毁在他内部定义的所有局部数据,包括局部变量、局部数组和形式参数,函数返回的指针请尽量不要指向这些数据,C语言没有任何机制来保证这些数据会一直有效,它们会在后续使用过程中可能引发运行时错误

    // 下面的例子,有的编译器直接报错,提示 返回局部变量的指针
    int *func()
    {
        int n=100;
        return &n;
    }
    
    int main()
    {
        int *p=func(),n;
        n=*p;
        printf("%d\n",n);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这个例子很重要https://blog.csdn.net/szm1234/article/details/120864801

    5.3 函数指针 – 还要看

    https://stackoverflow.com/questions/9552663/function-pointers-and-address-of-a-function!!!
    https://stackoverflow.com/questions/6893285/why-do-function-pointer-definitions-work-with-any-number-of-ampersands-or-as!!!
    https://stackoverflow.com/questions/37501982/address-operator-with-pointer-to-member-function!!!
    https://blog.csdn.net/L_fengzifei/article/details/128118034

    在编译时,每个函数都有一个入口地址,该入口地址就是函数指针所指向的地址,可以利用该指针变量调用函数
    函数在内存中具有物理地址,该地址能够赋给指针变量

    函数名:可以理解为地址,但是实际上不是地址。函数的首地址,是void()类型
    &函数名:表示以一个指向该函数这个对象的地址 相当于一个指针(指向一个整体)void(*)()类型
    这两个地址值是相等的
    函数名在 非&函数名 的 表达式中被隐式转换成函数指针void(*)()类型

    实际上 下面两种C标准都是可以的:

    // 标准1
    void func1(char *);
    void (*ptr1)(char*);
    ptr1=func1;
    (*ptr1)("li")
    
    // 标准2
    void func2(char *);
    void (*ptr2)(char*);
    ptr2=func2;
    ptr2("li")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    例子重要!!!

    #include 
    using namespace std;
    
    int func(int x)
    {
        return x*x;
    }
    
    int main()
    {
        // cout<<"hello world"<
    
        int (*pfunc1)(int)=func;
        int (*pfunc2)(int)=&func;
    
        printf("0x func %#p\n",func); // 0x func 0x4015a4
        printf("0x &func %#p\n",&func); // 0x &func 0x4015a4
        printf("0x pfunc1 %#p\n",pfunc1); // 0x pfunc1 0x4015a4
        printf("0x *pfunc1 %#p\n",*pfunc1); // 0x *pfunc1 0x4015a4
        printf("0x func2 %#p\n",pfunc2); // 0x func2 0x4015a4
        printf("0x *func2 %#p\n",*pfunc2); // 0x *func2 0x4015a4
    
        cout<<"func:"<<func(10)<<endl; // func:100
        cout<<"*func:"<<(*func)(10)<<endl; // *func:100
        cout<<"*pfunc1:"<<(*pfunc1)(10)<<endl; // *pfunc1:100
        cout<<"pfunc1:"<<pfunc1(10)<<endl; // pfunc2:100
        cout<<"*pfunc2:"<<(*pfunc2)(10)<<endl; // *pfunc2:100
        cout<<"pfunc2:"<<pfunc2(10)<<endl; // pfunc2:100
    
        return 0;
    }
    
    • 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

    函数指针数组
    https://blog.csdn.net/lzh201864031/article/details/129785640

    char (*ptr())[5]; // ptr是一个函数指针,指向的函数 返回值 是一个由五个char元素构成的数组 ???
    
    // 区别
    char (*ptr)[5]; // ptr是一个普通的指针,指向一个由五个char元素构成的数组
    
    • 1
    • 2
    • 3
    • 4

    常用函数

    字符串/数值型转换

    把数字存储为字符串就是存储数字字符
    123 -> ‘1’ ‘2’ ‘3’ ‘\0’

    字符串转数值型

    • atoi

    字符串转整型

    // 下面两种字符串都可以
    char s[]="123";
    char *s="123";
    int var=atoi(s);
    printf("%d\n",var);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • atof

    字符串转整型

    // 下面两种字符串都可以
    // char s[]="123";
    char *s="123.456";
    double var=atof(s);
    printf("%f\n",var);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    数值型转字符串

    • itoa()
    #include
    int a=10;
    char s[10]={0};
    itoa(a,s);
    printf("%s\n",s)
    
    char a='a';
    char buffer[10];
    itoa(a,buffer,2); // 2表示转换基数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • sprintf
    // 整型转字符串
    int a=120;
    char str[20];
    memset(str,0,sizeof(str));
    sprintf(str,"%d",a);
    printf("%s\n",str);
    printf("%d\n",sizeof(str));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    exit

    exit(0); // 正常退出
    exit(1); // 异常退出,里边的参数 1 会传给操作系统,主要不为0就表示异常退出
    
    • 1
    • 2
  • 相关阅读:
    【LeetCode周赛】LeetCode第365场周赛
    《代码随想录》三刷,与诸君共勉,秋招见!
    第六章:跨域和JSONP
    Opencv Python图像处理笔记一:图像、窗口基本操作
    Clickhouse分布式表式对本地表的对比
    智能合约之路:Web3时代的商业革新之道
    java毕业生设计重工教师职称管理系统计算机源码+系统+mysql+调试部署+lw
    git常见问题
    Python数据采集:抓取和解析XML数据
    [山东科技大学OJ]2677 Problem D: 求e的近似值
  • 原文地址:https://blog.csdn.net/L_fengzifei/article/details/127921678