對於模塊化程序的初步瞭解
一個真正的程序裏面白喊自定義函數,也包含對應的頭文件,還有函數定義界面。於是創建了個簡單的加法函數。
函數的聲明
#ifndef __ADD_H__
#define __ADD_H__
//函數的聲明
int Add(int x, int y);
#endif
我將這個聲明放在目錄爲add.h的文件下,其中的
#ifndef __ADD_H__
#define __ADD_H__
#endif
作用是爲了防止函數連續被調用時整個函數的定義段被連續搬運,導致主程序的代碼量過大。函數的調用原理是,講函數定義目錄下的代碼經過頭文件搬運到需要用的地方,然後執行。
函數的定義
int Add(int x, int y)
{
return (x + y);
}
放在目錄爲add.c的目錄下,將對應功能的函數放在具有提示意義的目錄下面,能夠與他人更好的配合,具有可讀性。
主函數
#include <stdio.h>
#include "add.h"
int main()
{
int a = 15;
int b = 25;
printf("sum=%d\n",Add(a, b));
return 0;
}
引用自定義函數的頭文件時,用的是雙引號 "" 。
最簡單的遞歸
#include <stdio.h>
int main()
{
printf("haha\n");
main();
return 0;
}//遞歸常見的錯誤,棧溢出,stack overflow
這是一個沒有限制條件的遞歸,程序運行一會兒就會自己停止,並且彈出警告窗口,原因是,程序運行的時候,會將運行時產生的局部變量存在名爲堆棧的一個內存區域,這個沒有限制的遞歸,不斷的調用自身,不斷的打印“haha”,就會將堆棧佔滿。
而這個程序也讓人能理解,遞歸,就是函數對於自身的調用,用現在的網絡流行語,俗稱套娃。
設計一個遞歸函數 分別打印出1234裏的1 2 3 4
void print(int n)
{
if (n > 9)
{
print(n / 10);
}
printf("%d ", n % 10);
}
#include <stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
print(num);
return 0;
}
第一次要使用遞歸進行實現某一個功能其實是 很沒有頭緒的,儘管聽老師講完了 ,程序也跟着打了出來,也看着調試一步步的調了,眼看着代碼一行一行的走,尤其是函數裏面的調用,到了最後一層,不滿足調用條件的時候,程序運行窗口就一個一個的把字符打印出來了,我前期是比較沒有理解,程序執行到這個地方是怎麼一層一層的返回去的,就是忘記了程序執行到了哪裏,老師畫圖講解之後,纔算是明白,一層一層的進來,也是要一層一層的出去。當最後一層不再滿足條件執行完畢彈出之後,彈出到上一層之後,繼續執行下一條語句,以此類推。
不創建臨時變量,求字符串長度
先是寫一個能實現求字符串從長度的函數,不考慮遞歸
最簡單的當然是直接使用庫函數計算字符串長度
#include <string.h>
int main()
{
strlen();
}
接下來用自定義函數
int my_strlen(char* str)
{
int count = 0;
while (*str != '\0')
{
count++;
str = str + 1;
}
return count;
}
整個字符串是無法直接被函數調用的,只能講字符串的地址作爲指針變量,指向字符串的第一個字符,調用進入函數之中,每次用完之後+1,直到看到‘\0’字符,字符串的長度計算停止。count就作爲一個計數,記錄字符串的長度。可是我們需要的是一個不用臨時變量的函數實現這一功能。
#include <stdio.h>
int my_strlen(char* str)
{
if (*str != '\0')
return 1+my_strlen(str + 1);
else
return 0;
}//未使用臨時變量進行計數,實現了求字符串長度
int main()
{
char arr[] = "bit";
int len = my_strlen(arr);//傳送過去的是數組第一個元素的地址,並不是整個數組
printf("len = %d\n", len);
return 0;
}
遞歸還是一樣,用畫圖的方法解釋起來更容易理解,將程序執行的過程可視化,增強理解,有一定的限制條件,而且每一層調用函數都在使得限制條件更加接近不滿足if語句,最終能夠停止遞歸過程。俗稱停止套娃。
遞歸和迭代 求階乘
當理解了前面兩個遞歸的例子之後,我也獨立寫出了這個求階乘的遞歸代碼,大概思路爲,想要求出n!,先要求出(n-1)!,想要求出(n-1)!,先要求出(n-2)!,快進到想要求出2!,就先求出1!,所以還是對自身的不斷調用,代碼如下:
#include <stdio.h>
int Fac1(int n)
{
int num = 1;
int i = 0;
for (i = 1; i <= n; i++)
{
num = i * num;
}
return num;
}
int Fac2(int n)
{
int ret = 1;
if (n != 0)
ret = n * Fac2(n - 1);
else
return ret;
}
int main()
{
int n = 0;
scanf("%d", &n);
printf("%d!=%d\n",n, Fac1(n));
printf("%d!=%d\n",n, Fac2(n));
return 0;
}
其中也用了個for循環寫了另一個函數實現目的。
求斐波那契數列的第n個
斐波那契數列,1 1 2 3 5 8...簡單的說,就是想知道第n個,就得先知道第n-1個和第n-2個,要知道第n-1個和第n-2個,就得知道第n-2和n-3個,第n-3和n-4個...以此類推,可是遞歸求第n個,需要運算的次數,就是2^n+2^(n-1)+...+2^2+2次運算,當求到第40個以上的時候,程序已經有了很明顯的等待,需要等待才能出結果,計算的次數其實已經很大,n每+1,運算次數就會多出兩倍,運算時間也會多出兩倍,代碼如下:
#include <stdio.h>
int Fib1(int n)
{
if (n <= 2)
return 1;
else
return Fib1(n - 1) + Fib1(n - 2);
}
int Fib2(int n)
{
int a = 1, b = 1, c=0;
int i = 0;
if (n > 2)
{
for (i = 3; i <= n; i++)
{
c = a + b;
a = b;
b = c;
}
return c;
}
else
return 1;
}
int main()
{
int n = 0;
scanf("%d", &n);
int f2 = Fib2(n);
printf("第%d個斐波那契數列:%d\n", n, f2);
int f1 = Fib1(n);
printf("第%d個斐波那契數列:%d\n", n, f1);
return 0;
}
所以以上代碼中還有一個,不用遞歸,直接用迭代實現的函數,一運行就能看到,兩個函數實現目標的時間差,所以當,遞歸之中函數調用的次數過大時,運算量巨大,就很有必要使用其他手段實現目標。