1. 程式人生 > 實用技巧 >【0007】函式

【0007】函式

函式的結構

#include <stdio.h>

// 函式的別稱是方法,函式是完成某一特定功能的模組
void print()        // 自定義函式
{
    printf("鋤禾日當午 \n");        // printf是系統函式
    /*
        函式名            printf
        函式的引數        "鋤禾日當午 \n"
        ()                緊挨著函式名之後,包含引數
    */

    getchar();        // 等待輸入一個字元
}

void printadd(int
a, int b) { printf("a+b=%d", a+b); // 將a+b的值轉換為字串,對映到字串“a+b=%d” } // 函式就是加工廠,給你輸入,你給我輸出;但是函式的輸入與輸出可以為空 void main1() //C程式的入口,有且僅有一個main函式 { //print(); // 自定義函式 //printf("鋤禾日當午 \n"); // 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 \n", 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 \n", &num, num);    // change = 17824956, num=10

    num = 100;

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

void main11()
{
    int num = 10;

    printf("main=%x, num=%d \n", &num, num);    // main = 17825168, num=10
    
    change(num);        // 此處的num是一個實參,函式呼叫的時候傳遞的是實參;實參可以是變數、常量或表示式
    
    printf("%d \n", num);        // 10
    
    system("pause");
}
形參與實參
#include <stdlib.h>
#include <stdio.h>

/*
函式呼叫的時候,形參分配記憶體
新建一個變數,儲存傳遞過來的實參的值,等價於實參給形參賦值,賦值會自動完成資料型別轉換
*/
void print_1(int num)
{
    printf("num=%d \n", 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 \n", 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 \n", 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\n");
    myprintf("niha%d\n", 10);
    myprintf("niha%d%s\n", 10, "Hello");
    myprintf("niha%d%s%c\n", 10, "Hello", 'A');
    myprintf("niha%d%s%f%c\n", 10, "Hello", 3.1415926, 'A');

    puts("\n");

    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 \n", add3(3, 1, 2, 3));         //6    
    //printf("%d \n", add3(4,3, 1, 2, 3));    // 9
    //printf("%d \n", 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 \n", 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 上 \n");
    //void print1();    // C語言理論上是要加宣告的
    //print1();        沒有宣告時,該函式無引數,會到系統中去找匹配的函式,結果沒有,就發生連結錯誤
    add1(3, 4);                // 而此時add1函式未加宣告,C編譯器卻編譯並連結成功,還能正確執行,只能說C編譯器太寬泛了,此處編譯器根據add1(3,4);有引數,而且返回值是整數,因此找到了被調函式

    printf("%d \n", add1(3));    // 15274067(執行第二次結果就不一樣了)  引數多了或少了,結果不可預測(居然編譯併成功執行了,C編譯器真是大條了!)
    printf("main 下 \n");

    system("pause");
}

/*
    被調函式的定義在主調函式之後,C語言編譯器可能找到了,則會通過編譯並連結成功;如果沒找到則會出現以下錯誤:
(10): warning C4013: 'print1' undefined; assuming extern returning int
(17): error C2371: 'print1': redefinition; different basic types
    但是在被調之前加以宣告,則一定能找到已定義的被調函式
*/
void print1()
{
    printf("main 上 \n");

    printf("main 下 \n");
}

int add1(int a, int b)
{
    return a + b;
}
函式的呼叫執行過程注意事項

函式呼叫的注意事項

#include <stdio.h>

void main09()
{
    //printf("Hello China. \n");
    printf;        // 引用函式名必須宣告
        // warning C4550: expression evaluates to a function which is missing an argument list   加入#include <stdio.h>時
}

/*
    C語言函式可以自動定位,沒有標頭檔案,沒有函式宣告,自動尋找,引數多了、少了都能執行;如果不是函式呼叫,無法自動定位
*/

void main001()
{
    //printf("%d \n", 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("\n1");
    //printf("\n2");
    //return 1;        // return 之後的語句不會接著執行,main函式中的return意味著整個程式的退出和結束
    //printf("\n3");
    //printf("\n4");
    //printf("\n5");


    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 \n", addpp(3, 7));

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

    printf("\n\n");

    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 \n", &a, &b, a, b);        // 5bfd44, 5bfd38, 10, 99
    {
        int a = 11;                    // 不同的塊語句,不同的作用域
        printf("%x, %x, %d, %d \n", &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 \n", data);
}

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

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

    //printf("num=%d \n", 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 \n", 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 \n", x);        // 8
    //printf("%d \n", ::x);    // C++可以用::x來訪問全域性變數x, C則不可以

    system("pause");
}

/*
    全域性變數很容易被內部變數遮蔽,很容易被引用或修改
*/

int a;

void maina()
{
    printf("%d \n", 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 \n", a);        // 區域性變數會遮蔽全域性變數
    {
        printf("%d \n", a);    // 10 此處是引用的上一層區域性變數
        char a = 'B';
        //int  a = 66;    // error C2371: 'a': redefinition; different basic types
        printf("%d,%c \n", a, a);    // 內部塊語句區域性變數會遮蔽內部塊語句以外的變數
    }
    printf("%d \n", 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 \n", a, a);        // 0,' '
    int b;
    //printf("%d \n", b);        // error C4700: uninitialized local variable 'b' used

    printf("%c,%d \n", 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 \n", 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 \n", getRabitForCycle(40));
    printf("%d \n", getRabbitTreeRecursive(40));
    printf("%d \n", getRabitForCycle(40));

    /*
        樹狀遞迴速度較慢,函式的迴圈呼叫需要消耗時間,但是演算法簡單;

        樹狀遞迴可以分解為:迴圈+棧
    */

    getchar();
}
樹狀遞迴_斐波那契數列