一、函數嵌套?
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語言語法有誤,不能生成可執行文件。