2019年7月9日星期二(C語言)


一、函數嵌套?

1、 什么是函數嵌套?

函數嵌套就是調用某個函數內部再調用另外一個函數。

 

2、 有函數嵌套程序在內存有什么特點?

如果嵌套的函數很多,就會在棧區累積非常多空間沒有被釋放。

3、 函數嵌套與遞歸函數有什么區別?

函數嵌套:自己調用別人的函數。

例子:

void fun()

{

       my_fun();

}

 

遞歸函數:自己調用自己的函數。

例子:

void fun()

{

       fun();

}

二、遞歸函數?

1、 特點?

遞歸函數特點自身調用自身,無限遞歸,沒有終止的時刻。

例子:

void fun()

{

       int a;

       fun();  -> 無限遞歸,沒有終止條件  -> 導致棧空間溢出!

       return;

}

 

int main()

{

       fun();

       return 0;

}

 

結論: 為了防止棧空間溢出,所以遞歸函數一般都會攜帶終止條件,也就是說到達某個條件時,函數返回!

2、題型1: 給程序,求結果。 

例題,求出下面程序的結果?

void fun(int n)

{

       if(n > 1) 

       {

              fun(n-1);

       }

       printf("n = %d\n",n);

       return;

}

 

int main()

{

       fun(100);

       return 0;

}

 

答案:1~100

思路: 找出終止條件,列出幾項,找規律,再畫圖分析!

 

3、 題型2: 給規則,寫程序。

例題:寫出下列程序。

5個學生坐在一起,問第5個學生多少歲,他說比第4個學生大2歲。問第4個學生多少歲,他說比第3個學生大2歲。問第3個學生多少歲,他說比第2個學生大2歲。問第2個學生多少歲,他說比第1個學生大2歲。最后問第1個學生多少歲,他說他是10歲,請問第5個同學多少歲?使用遞歸函數來完成。

#include <stdio.h>

int get_child_age(int n)

{

       int age;

       if(n <= 0)  //  -> 搞事情

              return -1;

      

       if(n == 1)  //  -> 終止條件

              age = 10;

             

       if(n > 1)   //  -> 正常

              age = get_child_age(n-1) + 2;

      

       return age;

}

 

int main(int argc,char *argv[])

{

       int ret;

       ret = get_child_age(5);

       printf("ret = %d\n",ret);

       return 0;

}

 

思路: 寫框架,分析情況 -> 最終目標與終止條件之間的關系。

  練習1: 畫出以上例子的內存圖。

  練習2: 有以下程序,求出結果?

int fun(int n)  10   9

{

       if(n==1)  return 1;

       else return (n+fun(n-1));  10+9+8+7+6+5+4+3+2+1

}

 

int main()

{

       int x;

       scanf("%d",&x);  //若輸入一個數字10。

       x = fun(x); 10

       printf("%d\n",x);  //55

}

 

  練習3:第1天存100塊,后面每一天都比昨天多存10塊,第312天存了多少?

#include <stdio.h> 

int get_money(int n)

{

       int money;

       if(n <= 0)

              return -1;

       if(n == 1)

              money = 100;

       if(n > 1)

              money = 10 + get_money(n-1);

      

       return money;

}

 

int main()

{

       int ret;

       ret = get_money(10);

       printf("ret = %d\n",ret);

       return 0;

}

 

   練習4: 使用循環方式完成練習3,畫內存圖分析兩種方式差別。

 

循環  -> 多次使用一個變量

遞歸  -> 每次都開辟一個新的變量空間   --> 容易造成棧空間溢出!

三、回調函數

1、 什么是回調函數?

先定義好函數A(喝水),再將這個函數A作為另外一個函數B(爬山)的參數,在函數B(爬山)可以回過頭調用這個參數(喝水),這個函數A就稱之為回調函數。

2、 基本框架?

void funB(funA)

{

       funA.....  在函數B中調用函數A

}

 

void funA()  -> 回調函數

{

 

}

 

int main()

{

       funB(funA);

}

   例題: 從鍵盤中獲取兩個數值,求出兩個值的和,要求使用回調函數完成。

int get_result(int a,int b,int(*p)(int,int))  -> 就把這個p當成函數名字 

{

       int ret;

       ret = p(a,b);

       return ret;

}

 

int add_fun(int x,int y)

{

       int z;

       z = x + y;

      return z;

}

int main()

{

       int a,b,ret;

       scanf("%d %d",&a,&b);

 

       ret = get_result(a,b,add_fun);  -> 函數名字作為參數,其實是傳遞了函數的地址過來。

       printf("ret = %d\n",ret);

}

補充:

函數指針參數怎么寫?

1)先寫一個 *

2)在*后面寫一個變量名,使用圓括號括住  (*p)

3)確定函數是哪個? int add_fun(int x,int y)

4)把第3步函數名與形參的變量名都去掉  int  (int,int) 

5)把第2步結果寫在第4步結果的返回值類型與形式參數列表之間    int(*p)(int,int

3、 回調函數使用場景?

在數據庫、系統編程信號通信中常常看到回調函數。

   練習4: 連續從鍵盤中獲取4個數字,求出其中的最大值,要求回調函數來完成。

int max4(int a,int b,int c,int d,int(*p)(int,int))

{

       int m;

       m = p(a,b);

       m = p(m,c);

       m = p(m,d);

       return m;

}

 

int max2(int x,int y)

{

       int z;

       z = (x > y ? x : y);

       return z;

}

 

int main()

{

       int a,b,c,d,ret;

       ret = max4(a,b,c,d,max2);

       return 0;

}

四、變參函數

1、 什么是變參函數?

變參函數是指該函數的參數不固定,例如:printf()函數。

 

  printf("helloworld!\n");  -> 參數為1

  printf("a = %d\n",a);     -> 參數為2

  printf("(%d %d)\n",x,y);  -> 參數為3

 結論: printf()參數為"1+x"

2、如何判斷一個函數是不是變參函數?

  #include <stdio.h>

  int printf(const char *format, ...);

 

   const char *format: 輸出字符串格式

   ... :  參數不定

 

3、 類似的變參函數有很多: printf() scanf()  ioctl()  fcntl()

五、內聯函數?

1、 什么是內聯函數?

在程序中調用函數時,需要花費一定的時間進行保護現場與恢復現場,但是使用了內聯函數,就既可以使用函數,又不需要花費時間來進行保護現場與恢復現場。

封裝:test.c

#include <stdio.h>

void fun()

{

       printf("hello!\n");

}

int main()

{

       fun();

       fun();

       fun();

       return 0;

}

 

不封裝:test.c

#include <stdio.h>

int main()

{

       printf("hello!\n");

       printf("hello!\n");

       printf("hello!\n");

       return 0;

} 

2、 如何實現內聯函數?

test.c

int main()

{

       fun();

       fun();

       fun();

       return 0;

} 

head.h

#include <stdio.h>

inline void fun()

{

       printf("hello!\n");

}

3、如何解決保護恢復現場問題?

1)使用內聯函數  -> inline

2)不要過多封裝成函數,當某些表達式組成一個功能時,才封裝為一個函數。

 

4、在嵌入式中哪里看到內聯函數?

在內核鏈表時看到內聯函數。

六、數組的概念?

1、 什么是數組?數組與普通變量有什么關系?

數組其實是集合來的,它是由多個相同類型的普通變量組合而成。當用戶需要同時定義多個相同變量時,就可以使用數組。

2、 定義數組時,需要交代什么東西?

1)數組元素的個數?

2)數組中每一個元素的數據類型? -> char short int long float double

 

定義公式:

數據類型 數組名字[元素的個數]

例子:定義一個具有100個int類型變量的數組

      int A[100];

3、從內存空間分析數組特點:

int main()

{

       int a; //在內存空間中連續申請4個字節,使用變量a間接訪問這片空間。

       int A[100];  //在內存空間中連續申請400個字節,使用變量A間接訪問這片空間。

       printf("sizeof(a) = %d\n",sizeof(a));//4

       printf("sizeof(a) = %d\n",sizeof(A));//400

       return 0;

}

4、定義了數組,編譯器如何處理數組?

例如: int A[100]

其實分開兩個部分進行處理, “int”為第二部分, "A[100]"作為第一部分。

第一部分  -> 決定內存空間中元素的個數。

第二部分  -> 決定每一個元素的數據類型

七、數組的賦值

1、 定義同時初始化

    數據類型 數組名字[元素的個數] = {初始化列表,每一個成員的值需要使用","分開}。

 

    int A[3] = {100,200,300}; //編譯通過

    int A[3] = {100,200};     //編譯通過

#include <stdio.h>

int main()

{

       int A[3] = {100,200};

       printf("A[0] = %d\n",A[0]); //100

       printf("A[1] = %d\n",A[1]); //200

       printf("A[2] = %d\n",A[2]); //0

       return 0;

}

   結論: 當初始化列表元素個數小於數組元素的個數時,沒有賦值的元素都會賦值為0。

 

    int A[3] = {100,200,300,400};  //編譯警告 

 

   warning: excess elements in array initializer

   warning: (near initialization for ‘A’)

 

編譯警告warning: 在內存空間中,可能有問題,但是還是可以生成可執行文件。

編譯出錯error: C語言語法有誤,不能生成可執行文件。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM