1. 程式人生 > >[日常練習] 2. 基於函式輸出9*9乘法表、交換兩數、判斷閏年、清空/初始化陣列、判斷素數的C語言實現

[日常練習] 2. 基於函式輸出9*9乘法表、交換兩數、判斷閏年、清空/初始化陣列、判斷素數的C語言實現


在C語言學習中,我們知道它是面向過程進行程式設計的,強調的是功能行為,其主要框架為:資料結構+演算法。在此也可以理解成:資料+函式。其實,函式在C語言學習中無時無刻不在使用,最為簡單的#include<stdio.h>,這便是我們程式的開頭,也是我們所呼叫的第一個函式,稱為:庫函式。裡面包含著許多功能供我們直接進行使用,不需要在重複的進行編輯程式來實現一些重複率高的功能,如printf,scanf,況且這些函式的編輯也並不一定都是簡單的。當我們的需求是特定的,沒有現成的庫函式供我們再來揮霍,這就需要我們進行自定義函式來完成我們的目標。接下來有5個小題來加深我們對函式的理解和認識!


練習題目:

1.實現一個函式,列印乘法口訣表,口訣表的行數和列數自己指定,輸入9,輸出9*9口訣表,輸出12,輸出12*12的乘法口訣表。
2.使用函式實現兩個數的交換。
3.實現一個函式判斷year是不是潤年。
4.建立一個數組,實現函式init()初始化陣列、實現empty()清空陣列、實現reverse()函式完成陣列元素的逆置。要求:自己設計函式的引數,返回值。
5.實現一個函式,判斷一個數是不是素數。

題目分析及程式碼、結果展示:

1.輸出9*9乘法表在上次習題處理已經練習過了,重點要採用迴圈的思想去尋找他們行數、列數之間的關係,從而得到迴圈的初值及限值條件,那麼這個迴圈就能夠跑起來,也就達到了我們的目標。但是,要是我們想隨意的輸出3*3、5*5、19*19的乘法表呢?難道我們需要每次都重複一遍上述的編碼過程嗎?我匿名反對這樣做!!!這3個要求相似度極高,定然有他們的共同之處。我們可以從函式的角度考慮下,它們都是輸出乘法表,這個可以作為它們的對應法則,要求輸入的行數不同,也就是他們的自變數X不一樣,得到的因變數Y也不一樣。這樣我們只需要得到這個對應法則,只要我們給定了X,自然可以得到我們想要的Y,這樣9*9乘法表也就推廣到i*i乘法表了!

#include<stdio.h>

void print_table(int line)                //注意函式型別
{
	int i = 0;
	int j = 0; 
	for(i=0; i<=line; i++)          
	{
		for(j=1; j<=i;j++)       //跳出條件
		{
			printf("%d*%d=%2d ", i, j, i*j);
		}
		printf("\n");            //換行位置
	}
	
}
int main()
{
	int line = 0;
	scanf("%d",&line);
	print_table(line);
	return 0;
}

上面就是我們的原始碼和所得結果!來列印一波“如意乘法表”吧!

在此我們需要注意到的是:

1. 函式的使用,對這個問題進行的便利在那裡,我們可以任意打出想要行列數的乘法表,要正確認識函式的優點!

2. 要注意函式內部的換行操作的位置,由於大括號較多,這個邏輯一定要清楚!

回味:

函式的使用會很方便,每次再列印乘法表的時候,只需要拷貝一份函式再呼叫下就行了。但是,這樣將函式的宣告、定義都放在一個原始檔裡面是合理常見的做法嗎?這樣你的函式思想不全都洩露了嗎?那怎麼賣錢呢!!!函式的宣告、定義、使用都是有其特殊的規則的,不是簡簡單單的全部放在檔案的開頭處。


2. 交換兩數的值、交換兩字串的各個字元、交換兩陣列中的元素,看到交換我們大多都想到建立第三個變數,作為載體。當然也可使用異或操作符直接進行交換,無需第三個變數。但是,如何用函式進行兩數的交換呢?函式體要是簡簡單單的寫為 swap(int a,int b),怕是達不到你的預期目標。在此,要一定分清楚什麼是形參、什麼是實參。函式的呼叫有傳值呼叫和傳址呼叫兩種形式,形參作為實參的一份臨時拷貝,形參與實參佔用不同的記憶體塊,形參值的交換在此是無法對實參產生實質性的作用的,然而傳址呼叫就可以讓函式和函式外邊的變數建立起真正的聯絡,也就是函式內部可以直接操作函式外部的變數。

#include<stdio.h>

void f_swap(int *p, int *a)
{
	int tmp = 0;
	tmp = *p;
	*p = *a;
	*a = tmp;
}
int main()
{
	int i = 2;
	int j = 6;
	printf("%d %d\n", i, j);
	f_swap(&i, &j);
	printf("%d %d\n", i, j);
	return 0;
}

上面就是使用函式交換兩整數值,結果顯而易見!

在此我們需要注意到的是:

1. 在函式體中,使用了指標變數,指向了i,j的地址,這樣的操作就是傳址呼叫。

2. 函式名的寫法需要見名知意(在此的函式名好像不規範)。

回味:

在交換兩址採用函式的寫法時,這才是最基本的函式的傳址呼叫。也可以自己嘗試嘗試用最初的想法(建立第三變數,直接交換兩值,不考慮地址),看看會不會交換?也可以進入監視看看真正的形參和實參在記憶體中的儲存到底是什麼個樣子,加深對傳址、傳值呼叫的理解。


3. 使用函式判斷是否為閏年,在這我們依舊考慮判斷1000--2000之內的閏年,其實與判斷是否為素數相同,僅需要改變函式體中的判斷條件即可,在此就直接給出原始碼以及結果了(偷個懶,詳情可以看看昨天的部落格)。

​
#include<stdio.h>

int is_year(int year)
{
	if(((year%4==0)&&(year%100!=0)) || (year%400==0))
	{
		return 1;
	}
	else
	{
		return 0;
	}
}
int main()
{
	int year = 0;
	int count = 0;
	for(year=1000; year<=2000; year++)
	{
		if(is_year(year) == 1)
		{
			count++;
			printf("%d ",year);
		}
	}
	printf("\n");
	printf("%d ",count);
	return 0;
}


​

4. 3個函式分別有三個相應的功能進行處理,Init函式即初始化陣列,就是將一個數組的所有元素按照我們的要求進行重新賦值。那對於函式要求就需要知道陣列名、元素個數、初始化要求,這三個要求就足以完成這項操作了,對陣列進行遍歷賦值即可。Empty函式,清空陣列,其實就是將陣列中的各個元素全部賦值為0,也就是Init函式的一個特殊情況。Reverse函式要進行陣列的逆置,逆置可不是倒序輸出。將第一個元素和最後一個元素進行交換,將第二個元素和倒數第二個元素進行交換...就可以完成逆置。最左側與最右側交換一次,在各向中間移動一次,再進行上面的操作,就可以寫成迴圈的結構了,當左側>右側那麼就可以停下 這部分的操作了,逆置也就結束了。

#include<stdio.h>
void Init_arr(int arr[], int sz, int j)
{
	int i = 0;
	for(i=0; i<=sz; i++)
	{
		arr[i] = j;
	}
}

void Empty_arr(int arr[], int sz)
{
	int i = 0;
	for(i=0; i<=sz; i++)
	{
		arr[i] = 0;
	}
}
void Reverse_arr(int arr[], int sz)
{
	int left = 0;
	int right = sz-1;

	while(left<right)
	{
		int tmp = arr[left];
		arr[left] = arr[right];
		arr[right] = tmp;
		left++;
		right--;
	}

}
int main()
{
	int i = 0;
	int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
	int sz = sizeof(arr)/sizeof(arr[0]);
	for(i=0; i<=sz; i++)
	{
		printf("%d",arr[i]);
	}
	Init_arr(arr, sz, 2);
	printf("\n");
	for(i=0; i<=sz; i++)
	{
		printf("%d",arr[i]);
	}
	Empty_arr(arr, sz);
	printf("\n");
	for(i=0; i<=sz; i++)
	{
		printf("%d",arr[i]);
	}
	Reverse_arr(arr, sz);
	printf("\n");
	for(i=0; i<=sz; i++)
	{
		printf("%d",arr[i]);
	}
	return 0;
}

 

上圖就是我們3個函式的原始碼以及結果,但是這執行時出現的這個問題,我也不知道怎麼搞,在陣列初始化的時候,後面那一圈是什麼東西???還有這三個函式為什麼都是11位了...

在此我們需要注意到的是:

1. 我們將3個函式寫在了一起,在執行的時候是可以得到3個結果,但是我們就會看到第一張圖的情況。第一個函式的使用已經對第二次函式使用產生了影響,要想得到逆置的函式時,可以先將函式Init、Empty先遮蔽掉,就會出現第二張圖的結果,是正確的
2. 在進行Reverse函式逆置操作時,使用left、right進行操作是及其普遍和重要的,也是我們必須要掌握的!!!

回味:

這個bug出現我也是始料未及,學的太過淺薄了。這三個函式倒是都比較常規,期間對於陣列長度的計算要熟練使用sizeof來進行計算,但是在函式體外就不能在進行計算了,sizeof(arr)=4,它計算的是位元組數,sizeof是一個操作符,不論是什麼型別的陣列,在函式中sizeof(arr)都等於4,因為arr其實是首元素的地址,也就是可以看作指標型別,所有的指標的所佔位元組數都是4/8(32/64),與陣列其型別無關。


5.素數的函式實現,邏輯也是很簡單,與普通一樣,注意優化即可,在此我們判斷100--200間的素數,也不再對其進行贅述了。

#include<stdio.h>

int is_prime(int n)
{
	int i = 0;
	for(i=1; i<n; i++)
	{
		if(n%i == 0)
		{
			return 0;
		}
	}
	return 1;
}

int main()
{
	int i = 0;
	for(i=100; i<=200; i++)
	{
		
		if(is_prime(i) == 1);
		{
			printf("%d ",i);
		}
	}
	return 0;
}

這個原始碼以及結果也是昨天剛剛寫過的,正確性可以保證!

在這還是強調下函式命名規則,真的好挫...