【0007】函数

函数的结构

#include <stdio.h>

// 函数的别称是方法,函数是完成某一特定功能的模块
void print()        // 自定义函数
{
    printf("锄禾日当午 
");        // printf是系统函数
    /*
        函数名            printf
        函数的参数        "锄禾日当午 
"
        ()                紧挨着函数名之后,包含参数
    */

    getchar();        // 等待输入一个字符
}

void printadd(int a, int b)
{
    printf("a+b=%d", a+b);    // 将a+b的值转换为字符串,映射到字符串“a+b=%d”
}

// 函数就是加工厂,给你输入,你给我输出;但是函数的输入与输出可以为空

void main1()        //C程序的入口,有且仅有一个main函数
{
    //print();                    // 自定义函数
    //printf("锄禾日当午 
");        // printf是系统函数
    printadd(4, 8);
    getchar();
}
自定义函数的结构

函数名与函数表

#include <stdlib.h>    
#include <stdio.h>

// int getmax中的int是函数的返回值类型
// getmax即函数名,是一个指向常量的指针,指向代码区的函数表某个地址;记录了函数名对应的函数体的入口地址(每一个应用程序都有一个函数表,该表存储了该程序所有函数的入口地址——即函数名对应的地址,改变函数入口地址可以改变函数的行为)
// int a, int b函数的形式参数
// {...} 块语句,不允许省略
// return a > b ? a : b; 返回值
// int getmax(int a, int b) 函数声明,别称函数头

int getmax(int a, int b)
{
    /*int a;    */    // error C2082 : redefinition of formal parameter 'a'  , 函数体内的变量和函数参数是同级别的同属函数体
    return a > b ? a : b;
}

void main02()
{

    printf("%p", getmax);

    getchar();
}

/*
    在下面语句的下方打一断点:
    printf("%p", getmax);    0017133E
    查找反汇编:地址栏输入0x0017133E
    跳转到该程序的函数表对应的:
        _getmax:
        0017133E  jmp         getmax (01717A0h)                    在函数表中
    即函数名getmax映射到getmax()函数体的位置0x01717A0
    地址栏输入0x01717A0
    跳转到了函数体的内存位置:                                        函数实体
    int getmax(int a, int b)
    {
    001717A0  push        ebp
    001717A1  mov         ebp,esp
    001717A3  sub         esp,0C4h
    ...
*/
函数名与函数表

函数的定义与声明

#include <stdlib.h>    // std标准库,C语言标准库是跨平台的
#include <stdio.h>        
#include <Windows.h> // 仅适用于Windows系统(第三方库函数)

// C++属于严格的编程语言,函数的声明必须在调用之前
int getres(int a, int b, int c);    // 函数的声明

/*
    调用一个函数,首先必须知道这个函数是存在的C语言自上而下编译
    被调函数没有定义或者其在主调函数之后时,编译会出错
    故解决的方法有:
        首先要定义这个被调函数
        其次被调函数如果在主调函数之后,则需要在主调函数之前对被调函数加以声明;如果被调函数在主调函数之前则不需另加声明
*/

// 函数的声明
void msg();        // 函数的声明,只是说明函数的存在,因此也可出现重复(傻子才这么干)
void msg();
void msg();
void msg();
int cheng(int a, int b);

void main123()
{
    msg();
    printf("%d 
", cheng(12, 3));

    system("pause");
}

// 函数的定义
void msg()
{
    MessageBoxA(0, "锄禾日当午", "学Cztm刺激", 0);
}

//void msg()     error C2084: function 'void msg()' already has a body; note: see previous definition of 'msg'
//{
//     函数的定义则只能出现一次
//}


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


void main124()
{
    // 代码重用
    int x = 3, y = 6, z = 9;
    x = x*x*x;
    y = y*y*y;
    z = z*z*z;
    int res = x + y + z;
    res = getres(x, y, z);
    /*
        getres(x, y, z);调用在定义之前时
        C程序:
        warning C4013: 'getres' undefined; assuming extern returning int
        1>LINK : fatal error LNK1561: entry point must be defined       (C中能编译——仅是警告,但是链接不行——严重错误:实例处必须定义)
        CPP程序:
        1>LINK : fatal error LNK1561: entry point must be defined
        2>error C3861: 'getres': identifier not found
    */


    int a = 10, b = 13, c = 14;
    a = a*a*a;
    b = b*b*b;
    c = c*c*c;
    int res1 = a + b + c;
    res1 = getres(a, b, c);

    system("pause");
}

int getres(int a, int b, int c)
{
    return a*a*a + b*b*b + c*c*c;
}


void run(char *path)    // 外部函数,C语言功能的实现(代码重用)主要靠函数
{
    ShellExecuteA(0, "open", path, 0, 0, 1);
}

void main01()
{
    run("E:\Thunder Network\Thunder\Program\Thunder.exe");


    getchar();    
    /*
        库函数:
            由C语言系统提供,用户无需定义,也不必在程序中做类型说明,只需在程序前包含有该函数定义的头文件即可
    */
}
函数的调用与声明
#include <stdlib.h>    
#include <stdio.h>    

int add05(int, int);        // 函数声明
/*
    声明时,变量名可省略,可以多次声明
    函数的类型声明应该和定义的函数的类型保持一致,否则编译器会报错
    当被调函数定义在主调函数之前,声明就没必要了
*/

void main005()
{
    add05(3, 2);

    system("pause");
}

int add05(int a, int b)
{
    return a + b;
}
声明时,形参变量名可省略,可以多次声明

函数的参数

#include <stdio.h>
#include <stdlib.h>

/*
    形参(形式参数)与实参(实际参数)的总结:
        形参:
        1、函数调用之前,形参即函数定义时()里的参数,值是不确定的;
        2、不确定的值,不会分配内存,只有调用的时候,才会分配内存并新建一个变量,新建的这个变量(形参)会接收实参(实际参数)的值,当函数调用结束之后,形参所占据的内存会被回收
        
        实参:
        1、函数调用时,主调函数传递给被调函数的确切值就是实际参数,实参可以是常量、变量或者表达式

        形参与实参内存地址不一样,占用的是不同的内存空间
*/


// 地址不一样,说明change(int num)与main()函数中的num是2个不同的变量
// 函数定义的时候是形参
void change(int num)    // 此处的int num,是int类型的变量,是形参,只有在函数change被调用的时候才会分配内存,change运行完毕,num的内存即被回收;函数调用的时候,形参分配内存,新建一个num的变量,用于存储传递过来的实际参数的值
{
    
    printf("change=%x, num=%d 
", &num, num);    // change = 17824956, num=10

    num = 100;

    printf("change=%x, num=%d 
", &num, num);    // change = 17824956, num=100
}

void main11()
{
    int num = 10;

    printf("main=%x, num=%d 
", &num, num);    // main = 17825168, num=10
    
    change(num);        // 此处的num是一个实参,函数调用的时候传递的是实参;实参可以是变量、常量或表达式
    
    printf("%d 
", num);        // 10
    
    system("pause");
}
形参与实参
#include <stdlib.h>
#include <stdio.h>

/*
函数调用的时候,形参分配内存
新建一个变量,存储传递过来的实参的值,等价于实参给形参赋值,赋值会自动完成数据类型转换
*/
void print_1(int num)
{
    printf("num=%d 
", num);
}

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

void main12()
{
    // 调用函数的时候,尽量做到类型匹配,否则可能导致误差或错误
    //print_1(10.8);
    double db = adddata(10.9, 9.8);
    printf("%lf 
", db);        // 19.000000

    double db2 = adddata("Ahg", "fgB");    // Warning    C4047    'function': 'int' differs in levels of indirection from 'char [4]'    ----> C
    // Warning    C4024    'adddata': different types for formal and actual parameter 1    ----> CPP

    printf("%lf 
", db2);        // 11990992.000000 意想不到的结果
    
    system("pause");
}
对应的形参与实参类型应一致

函数的可变参数

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>

void myprintf(char *ptstr, ...)        // 可变参数
{
    va_list ap;                    // 定义起始点    (char *类型的指针)
    va_start(ap, ptstr);        
    /*
        固定参数pstr,是函数myprintf()的第一个参数,存储于栈中,位于栈底

        typedef char * va_list;

        把 n 圆整到 sizeof(int) 的倍数
        #define _INTSIZEOF(n)       ( (sizeof(n)+sizeof(int)-1) & ~(sizeof(int)-1) )

        初始化 ap 指针,使其指向第一个可变参数。v 是变参列表的前一个参数    即 此处的ptstr
        #define va_start(ap,v)      ( ap = (va_list)&v + _INTSIZEOF(v) )

        该宏返回当前变参值,并使 ap 指向列表中的下个变参(ap指向的数据以type类型的方式加以解析)
        #define va_arg(ap, type)    ( *(type *)((ap += _INTSIZEOF(type)) - _INTSIZEOF(type)) )

        将指针 ap 置为无效,结束变参的获取
        #define va_end(ap)             ( ap = (va_list)0 )

        原文链接:https://blog.csdn.net/u013490896/java/article/details/85490103
        参考链接:https://www.cnblogs.com/clover-toeic/p/3736748.html
    */


    char flag;
    while (*ptstr)
    {

        flag = *ptstr;
        if (flag != '%')
        {
            putchar(flag);
            ptstr++;
        }
        else
        {
            flag = *++ptstr;
            switch (flag)
            {
            case 'd':
                printf("%d", va_arg(ap, int));
                ptstr++;
                break;
            case 's':
                printf("%s", va_arg(ap, char *));
                ptstr++;
                break;
            case 'c':
                //printf("%c", va_arg(ap, char));
                putchar(va_arg(ap, char));
                ptstr++;
                break;
            case 'f':
                printf("%f", va_arg(ap, double));
                ptstr++;
                break;
            default:
                break;
            }
        }
    }

    va_end(ap);        // 结束读取
}

void main003()
{
    myprintf("niha
");
    myprintf("niha%d
", 10);
    myprintf("niha%d%s
", 10, "Hello");
    myprintf("niha%d%s%c
", 10, "Hello", 'A');
    myprintf("niha%d%s%f%c
", 10, "Hello", 3.1415926, 'A');

    puts("
");

    system("pause");
}
函数可变参数宏的结构
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>    
#include <stdio.h>
#include <stdarg.h>        // stand argument

// 已知参数的个数为num
int add3(int num, ...)    // ...代表可变参数,num代表参数个数
{
    int res = 0;         //结果
    va_list  argp;        // 存储参数开始的地址(参数列表首地址)
    va_start(argp, num);// 从首地址开始,读入num后面的数据到地址
    for (int i = 0; i < num; ++i)
    {
        res += va_arg(argp, int); // 按照int类型读取一个数据单元
    }
    va_end(argp);

    return res;
}
void go(int num, ...)
{
    va_list argp;
    va_start(argp, num);
    for (int i = 0; i < num; ++i)
    {
        /*char str[50];
        sprintf(str, "%s", va_arg(argp, char *));
        system(str);*/
        system(va_arg(argp, char *));
    }
    va_end(argp);
}

void showint(int start, ...)
{
    va_list argp;
    va_start(argp, start);
    int argvalue = start;    // 第一步初始化
    do
    {
        printf("%d ", argvalue);
        argvalue = va_arg(argp, int);
    } while (argvalue != -1);
    va_end(argp);
}

void main08()
{
    //printf("%d 
", add3(3, 1, 2, 3));         //6    
    //printf("%d 
", add3(4,3, 1, 2, 3));    // 9
    //printf("%d 
", add3(5, 4, 7, 1, 2, 3)); // 17

    //go(3, "notepad", "calc", "tasklist&pause");

    showint(1, 2, 3, 4, 5, -1);

    getchar();
}
函数可变参数的应用
#include <stdio.h>
#include <stdlib.h>


// void main(void)            返回空类型,参数为空
// main()                    默认的返回值类型为int类型,参数为空


main(int argc, char *args[])
{
    for (int i = 0; i < argc; i++)
    {
        puts(args[i]);
    }

    // argc是参数的个数
    // args[]是一个指针数组,存储的是常量字符串的地址,args[0]第一个参数是主程序的路径,第二个是附加参数

    system("pause");
}


void main020(int argc, char *args[], char *envr[])    // 第三个参数:环境变量字符串的地址
{
    char **pp = envr;
    while (*pp != NULL)
    {
        puts(*pp);
        pp++;
    }

    system("pause");
}
main函数的参数列表

函数参数的运算与入栈顺序

#include <stdlib.h>    
#include <stdio.h>

void add2(int a, int b)
{
    printf("a=%d, b=%d 
", a, b);
}

void main07()
{
    int a = 5;
    int b = 5;
    int c = 5;
    int d = 5;
    int e = 5;
    int f = 5;

    /*
        函数参数与函数一样,遵循栈先进后出的原则,即先执行的代码后入栈,后执行的代码先入栈
    */
    add2(a, a++);        // a=6, b=5            
    /*
        入栈:
            1、a++是一个表达式,保存在栈中的结果为5,a自增1等于6
            2、a是一个变量,保存在栈中的结果为a
        出栈:
            2、a        ---> 6
            1、a++    ---> 5 ----> 5
    */

    add2(b, b += 1);    // a=6, b=6            
    /*
        入栈:
            1、b+=1是一个赋值表达式,保存在栈中的结果为b,b自增1等于6
            2、b是一个变量,保存在栈中的结果为b
        出栈:
            2、b        ---> 6
            1、b+=1    ---> b ---> 6
    */

    add2(c++, c = c + 2);    // a=7, b=8
    add2(d++, d += 2);    // a=7, b=8
    /*
        入栈:
            1、d+=2是一个赋值表达式,保存在栈中的结果为d,d自增2等于7
            2、d++是一个表达式,保存在栈中的结果为7,d自增1等于8
        出栈:
            2、d++    ---> 7
            1、d+=2    ---> d ---> 8
    */

    add2(e, e++, e += 2);    // a=8, b=7
    /*
        入栈:
            1、e += 2是一个赋值表达式,保存在栈中的结果为e,e自增2等于7
            2、e++是一个表达式,保存在栈中的结果为7,f自增1等于8
            3、e是一个变量,保存在栈中的结果为e
        出栈:
            3、e        ---> 8
            2、e++    ---> 7
        因为add2()函数定义中只有2个形参,因此1、e += 2不会作为函数的参数出栈
    */

    add2(f += 2, f++, f += 2);    // a=10, b=7
    /*
        入栈:
            1、f += 2是一个赋值表达式,保存在栈中的结果为f,f自增2等于7
            2、f++是一个表达式,保存在栈中的结果为7,f自增1等于8
            3、f += 2是一个赋值表达式,保存在栈中的结果为f,f自增2等于10
        出栈:
            3、f += 2   ---> f ---> 10
            2、f++    ---> 7
        因为add2()函数定义中只有2个形参,因此1、f+= 2不会作为函数的参数出栈
    */

    getchar();
}
函数的参数的入栈顺序

函数的执行过程

#include <stdlib.h>    
#include <stdio.h>

// C语言通过栈来控制程序的执行顺序,后执行的语句或函数先进栈,最后压栈的语句或函数先出栈执行

void main06()
{
    printf("main 上 
");
    //void print1();    // C语言理论上是要加声明的
    //print1();        没有声明时,该函数无参数,会到系统中去找匹配的函数,结果没有,就发生链接错误
    add1(3, 4);                // 而此时add1函数未加声明,C编译器却编译并链接成功,还能正确执行,只能说C编译器太宽泛了,此处编译器根据add1(3,4);有参数,而且返回值是整数,因此找到了被调函数

    printf("%d 
", add1(3));    // 15274067(执行第二次结果就不一样了)  参数多了或少了,结果不可预测(居然编译并成功执行了,C编译器真是大条了!)
    printf("main 下 
");

    system("pause");
}

/*
    被调函数的定义在主调函数之后,C语言编译器可能找到了,则会通过编译并链接成功;如果没找到则会出现以下错误:
(10): warning C4013: 'print1' undefined; assuming extern returning int
(17): error C2371: 'print1': redefinition; different basic types
    但是在被调之前加以声明,则一定能找到已定义的被调函数
*/
void print1()
{
    printf("main 上 
");

    printf("main 下 
");
}

int add1(int a, int b)
{
    return a + b;
}
函数的调用执行过程注意事项

函数调用的注意事项

#include <stdio.h>

void main09()
{
    //printf("Hello China. 
");
    printf;        // 引用函数名必须声明
        // warning C4550: expression evaluates to a function which is missing an argument list   加入#include <stdio.h>时
}

/*
    C语言函数可以自动定位,没有头文件,没有函数声明,自动寻找,参数多了、少了都能执行;如果不是函数调用,无法自动定位
*/

void main001()
{
    //printf("%d 
", add4(2, 3));
    //add4;        
    /*
        当上面一句没有被注释时:[compile] warning C4013: 'add4' undefined; assuming extern returning int,而且能够链接并且程序能够运行

        当上面一句被注释时:[compile]error C2065: 'add4': undeclared identifier

        说明C编译器查找add4(2, 3); 时,因为有函数调用而且是有参调用,能够精确定位到本程序内部的函数定义,接着下面的add4就在上一步的基础上找到了。   可见C语言的编译器太灵活了。
    */
    print11();
    system("pause");
}

int print11()
{

}

int add4(int a, int b)
{
    return a + b;
}
函数调用的错误示范

函数的返回值

#include <stdlib.h>
#include <stdio.h>

int go()    
{

}
/*
    Warning C4716 'go': must return a value
    非main函数如果函数类型不是void,编译时会出现一个警告,如果是CPP文件,则会是一个错误
        函数的返回值类型必须与函数名前的类型保持一致,当函数的类型为void时,可以没有return语句
    void main(void)            返回空类型,参数为空
    main()                    默认的返回值类型为int类型,参数为空
*/

int main6()    // 无论main函数是否是void类型,还是其他类型的函数,main函数可以没有返回值
{
    //printf("
1");
    //printf("
2");
    //return 1;        // return 之后的语句不会接着执行,main函数中的return意味着整个程序的退出和结束
    //printf("
3");
    //printf("
4");
    //printf("
5");


    godata();

    getchar();

    // main函数或其他函数中没有return语句,执行完所有的语句后,函数会自动退出
}

/*
    函数内部定义的变量或参数(局部变量),在函数调用结束之后(函数返回后),变量即刻被销毁,内存被回收
    函数返回值有副本机制,返回的时候,另外再存一份

    如果返回值是全局变量,则该变量一直存在,直到程序运行结束,全局变量才会被销毁,内存这时才被回收
*/
int addpp(int a, int b)
{

    int z = a + b;

    printf("%p", &z);

    return z;
}

int addbb(int a, int b)
{
    int z = 0;

    z = a + 1;

    z = a + 2;

    z = a + b;

    return a + b;
    // a+b是临时变量,在寄存器中保存其计算结果
    // 返回临时变量时,临时变量会从寄存器中即刻销毁
}

void main()
{
    // 此处打印的是副本,原来内存中的数据已被销毁
    //printf("%d 
", addpp(3, 7));

    printf("%d 
", addbb(3, 7));

    printf("

");

    getchar();
}
函数的返回值

局部变量与全局变量

#include <stdio.h>
#include <stdlib.h>

/*
    总结:
        块语句内部的变量,其作用域是变量所在块语句定义的起始处至该块语句的结束,也可作用于内部包含的块语句(前提是没有定义与该变量的名称相同的变量——存在内层变量屏蔽外层变量的情况)
        同一个块语句不能重复定义一个变量
        
        局部变量调用完成以后会被回收,该局部变量的位置出现垃圾数据
        局部变量是为块语句服务的,块语句执行结束,局部变量就被回收
        函数内部定义的变量以及函数的参数均是局部变量
*/

int a = 100;    // 变量名相同的全局变量和局部变量,是两个(内存地址)不同的变量,因此可以重复定义

void main16()
{
    int a = 10;    // 在同一块语句内,变量不能重复定义
    //int a = 11;    // Error    C2374    'a':redefinition
    int b = 99;
    printf("%x, %x, %d, %d 
", &a, &b, a, b);        // 5bfd44, 5bfd38, 10, 99
    {
        int a = 11;                    // 不同的块语句,不同的作用域
        printf("%x, %x, %d, %d 
", &a, &b, a, b);    // 5bfd2c, 5bfd38, 11, 99   块语句中可以包含块语句,局部变量b的作用范围包括子块,而在子块中定义的变量a屏蔽了母块中的变量a(其效果与块语句中的变量a屏蔽全局变量a一样)
    }
        
    system("pause");
}
块语句与局部变量
#include <stdlib.h>
#include <stdio.h>

// 全局变量不属于任何一个函数,可以被任何一个函数调用
// 创建的全局变量比main函数还早,全局变量的生存期是整个程序的生存期
// 全局变量在程序生命周期内一直存储于内存,而局部变量在所属的函数调用完毕之后生命周期结束,同时其所占据的内存也将被回收
// 需要任意函数调用的场合就需要全局变量,全局变量可用于函数间的通信
int num = 4298;        // 全局变量  -- 整个公司的RMB
int data = 10;

void 事业部A()
{
    num = num - 800;    // 支出800
    num = num + 900;    // 营收900
}

void 事业部B()
{
    num = num - 1800;    // 支出1800
    num = num + 900;    // 营收900
}

void printdata()
{
    printf("%d 
", data);
}

void main14()
{
    data = 100;
    printdata();        // 100

    //num += 1000;        // 科技创新基金
    //事业部A();
    //事业部B();

    //printf("num=%d 
", num);

    system("pause");
}
全局变量
#include <stdio.h>
#include <stdlib.h>

/*
    全局变量特点/缺点:
        生存周期一直持续到程序退出
        内存也在程序退出之后才释放
        容易与局部变量重名,容易被屏蔽失效
        值容易被修改——例如游戏,遇到外挂一类的程序、注入的黑客技术等技术,值容易被修改
*/ 

/*
    全局变量可以被本文件中所有的函数所共享
    使用全局变量要做到:
        1、变量名要容易理解,尽可能不与局部变量重名
        2、避免占内存较大的变量作为全局变量
        3、避免全局变量被错误的修改(正规的软件工程,写一个函数需要修改全局变量时,一定要注释为什么修改,修改的目的是什么,修改值是多少)
*/

int RMB = 3000;        

void addRMB(int num)
{
    RMB += num;
}

void mulRMB(int num)
{
    RMB -= num;
}

void printRMB()
{
    printf("你的RMB还有%d 
", RMB);
}

void main17()
{
    addRMB(1000);
    mulRMB(5);
    mulRMB(500);
    mulRMB(5);
    printRMB();

    system("pause");
}
全局变量的特点
#include <stdio.h>
#include <stdlib.h>

int x = 1000;

void main15()
{
    int x = 8;    // 变量名称相同的时候,在局部变量的作用域中,局部变量会屏蔽全局变量(不是改变)
    printf("%d 
", x);        // 8
    //printf("%d 
", ::x);    // C++可以用::x来访问全局变量x, C则不可以

    system("pause");
}

/*
    全局变量很容易被内部变量屏蔽,很容易被引用或修改
*/

int a;

void maina()
{
    printf("%d 
", a);        
    /*
        本文件未声明变量a时:int a
        error C2065: 'a': undeclared identifier

        声明a时:int a;
        输出结果为9,int a =9;是在同工程中的“全局变量和局部变量.c”文件中声明的——说明全局变量是可跨文件调用的
    */

    getchar();
}

void main006()    // 这里说的屏蔽是指变量名相同的变量
{
    int a = 10;
    printf("%d 
", a);        // 局部变量会屏蔽全局变量
    {
        printf("%d 
", a);    // 10 此处是引用的上一层局部变量
        char a = 'B';
        //int  a = 66;    // error C2371: 'a': redefinition; different basic types
        printf("%d,%c 
", a, a);    // 内部块语句局部变量会屏蔽内部块语句以外的变量
    }
    printf("%d 
", a);        // 10

    getchar();
}
全局变量与局部变量之间的冲突
#include <stdlib.h>    
#include <stdio.h>

/*
    局部变量只有定义和初始化的概念,不存在声明
    全局变量可以多次声明,定义的时候相当于声明+初始化
*/

//int a = 10;            // 全局变量

int a;    // 全局变量被当成声明来看待
int a;    // 全局变量未定义默认为ASCII为0,字符为空
int a;
int a = 9;    // 全局变量的定义
//int a = 3;    // error C2374: 'a': redefinition; multiple initialization


void main004()
{
    printf("%d,%c 
", a, a);        // 0,' '
    int b;
    //printf("%d 
", b);        // error C4700: uninitialized local variable 'b' used

    printf("%c,%d 
", c, c);        // ' ',0

    system("pause");
}

void main003()
{

    int a;        // 局部变量
    int a=13;            // error C2086: 'int a': redefinition
}

void go001()
{
    a = 13;            //(无全局变量时) error C2065: 'a': undeclared identifier
}
全局变量与局部变量的定义及全局变量的声明问题

函数与递归

#include <stdio.h>
#include <stdlib.h>

//void main()
//{
//    main();        // 死循环
//}


// f(n)=f(n+1)+1
// f(5) return
void go_3(int num)
{
    if (num >= 5)
        return;
    else
    {
        system("notepad");
        go_3(num + 1);
    }
}

/*
    1+2+3+...+100    f(100)
    1+2+3+...+99    f(99)
    f(100)=f(99)+100
    f(n)=f(n-1)+n
*/
int goadd_1(int num)
{
    if (num == 0)
        return 0;
    else
        return num + goadd_1(num - 1);
}

/*
    10 % 2  0
    5  % 2  1
    2  % 2  0
    1  % 2  1
    (10)10=(1010)2
*/
void to2(int num)
{
    if (num == 0)
        return;
    else
    {
        //printf("%d", num % 2);    // 0101    调用之前顺序
        to2(num / 2);
        printf("%d", num % 2);        // 1010    调用之后逆序
    }
}

void main009()
{
    //go_3(0);
    //printf("%d 
", goadd_1(100));
    to2(10);

    getchar();
}
线性递归
#include <stdio.h>
#include <stdlib.h>

/*
    斐波那契数列:
    有一对兔子,每个月可以生育一对兔子,刚出生的兔子2个月后可以生育一对兔子,问N个月之后又多少对兔子
    1    
    1
    1   1
    1   1   1
    1   1   1   1    1

    g(n)=g(n-1)+g(n-2)
*/

int getRabbitTreeRecursive(int num)
{
    if (num == 1 || num == 2)
        return 1;
    else
        return getRabbitTreeRecursive(num - 1) + getRabbitTreeRecursive(num - 2);
}

int getRabitForCycle(int num)
{
    int f1 = 1, f2 = 1;
    int f3;
    for (int i = 3; i <= num; ++i)
    {
        f3 = f2 + f1;
        f1 = f2;
        f2 = f3;            // 轮替注意值的覆盖顺序
    }
    return f2;
}

void main()
{
    printf("%d 
", getRabitForCycle(40));
    printf("%d 
", getRabbitTreeRecursive(40));
    printf("%d 
", getRabitForCycle(40));

    /*
        树状递归速度较慢,函数的循环调用需要消耗时间,但是算法简单;

        树状递归可以分解为:循环+栈
    */

    getchar();
}
树状递归_斐波那契数列
原文地址:https://www.cnblogs.com/ant-colonies/p/13398766.html