1. 程式人生 > 其它 >《C語言程式設計》(譚浩強第五版) 第8章 善於利用指標 習題解析與答案

《C語言程式設計》(譚浩強第五版) 第8章 善於利用指標 習題解析與答案

你也可以上程式咖(https://meta.chengxuka.com),開啟大學幕題板塊,不但有答案,講解,還可以線上答題。

本章習題均要求用指標方法處理。

題目1:輸入3個整數,按由小到大的順序輸出。

解:

答案程式碼:

#include <stdio.h>
int main()
{

	void swap(int *p1, int *p2);
	int n1, n2, n3;
	int *p1, *p2, *p3;
	printf("input three integer n1,n2,n3:");
	scanf("%d,%d,%d", &n1, &n2, &n3);
	p1 = &n1;
	p2 = &n2;
	p3 = &n3;
	if (n1 > n2)
		swap(p1, p2);
	if (n1 > n3)
		swap(p1, p3);
	if (n2 > n3)
		swap(p2, p3);
	printf("Now,the order is:%d,%d,%d\n", n1, n2, n3);
	return 0;
}
void swap(int *p1, int *p2)
{
	int p;
	p = *p1;
	*p1 = *p2;
	*p2 = p;
}

執行結果:

題目2:輸入3個字串,按由小到大的順序輸出。

解:

#include <stdio.h>
#include <string.h>
int main()
{
	void swap(char *, char *);
	char str1[20], str2[31], str3[20];
	printf("input three line:\n");
	gets(str1);
	gets(str2);
	gets(str3);
	if (strcmp(str1, str2) > 0)
		swap(str1, str2);
	if (strcmp(str1, str3) > 0)
		swap(str1, str3);
	if (strcmp(str2, str3) > 0)
		swap(str2, str3);
	printf("Now,the order is:\n");
	printf("%s\n%s\n%s\n", str1, str2, str3);
	return 0;
}

void swap(char *p1, char *p2)
{
	char p[20];
	strcpy(p, p1);
	strcpy(p1, p2);
	strcpy(p2, p);
}

執行結果:

輸入3行文字,程式把它們按字母由小到大的順序輸出。

題目3:輸入10個整數,將其中最小的數與第一個數對換,把最大的數與最後一個數對換。寫3 個函式:

①輸入 10個數;

②進行處理;

③輸出 10 個數。

解:

#include <stdio.h>
int main()
{
	void input(int *);
	void max_min_value(int *);
	void output(int *);
	int number[10];
	input(number);		   //呼叫輸入10 個數的函式
	max_min_value(number); //呼叫交換函式
	output(number);		   //呼叫輸出函式
	return 0;
}

//輸入10 個數的函式
void input(int *number)
{
	int i;
	printf("input 10 numbers:");
	for (i = 0; i < 10; i++)
		scanf("%d", &number[i]);
}

// 交換函式
void max_min_value(int *number)
{
	int *max, *min, *p, temp;
	max = min = number; //開始時使 max和 min都指向第1個數
	for (p = number + 1; p < number + 10; p++)
		if (*p > *max)
			max = p; //若 p指向的數大於max指向的數,就使 max指向p指向的大數
		else if (*p < *min)
			min = p;  //若 p指向的數小於min指向的數,就使 min指向p指向的小數
	temp = number[0]; //將最小數與第1個數 number[0]交換
	number[0] = *min;
	*min = temp;
	if (max == number)
		max = min;	  //如果 max和 number相等,表示第1個數是最大數,則使 max指向當前的最大數
	temp = number[9]; //將最大數與最後一個數交換
	number[9] = *max;
	*max = temp;
}

//輸出函式
void output(int *number)
{
	int *p;
	printf("Now, they are:   ");
	for (p = number; p < number + 10; p++)
		printf("%d ", *p);
	printf("\n");
}

分析:關鍵在 max_min_value 函式,請認真分析此函式。形參 number 是指標,區域性變數 max,min和 p都定義為指標變數,max 用來指向當前最大的數,min 用來指向當前最小的數。

number 是第 1 個數 number[0] 的地址,開始時執行 max=min=number 的作用就是使 max 和 min 都指向第 1 個數 number[0] 。以後使 p 先後指向10個數中的第 2~10 個數。如果發現第 2 個數比第 1 個數 number[0] 大,就使 max 指向這個大的數,而 min 仍指向第 1 個數。如果第 2 個數比第. 1個數 number[0] 小,就使 min 指向這個小的數,而 max 仍指向第 1個數。然後使 p 移動到指向第 3 個數,處理方法同前。直到 p 指向第 10 個數,並比較完畢為止。此時 max. 指向 10個數中的最大者,min 指向 10 個數中的最小者。假如原來 10 個數是:

32		24		56		78		1		98		36		44		29		6

在經過比較和對換後,max和 min的指向為

32		24		56		78		1		98		36		44		29		6
												↑		↑
												min	max

此時,將最小數 1 與第 1 個數(即 number[0] )32 交換,將最大數 98 與最後一個數 6 交換。因此應執行以下兩行:

temp = number[0]; number[0] = *min;*min = temp;//將最小數與第1個數 number[0]交換
	
temp = number[9]; number[9] = *max;*max = temp;//將最大數與最後一個數交換

最後將已改變的陣列輸出。

執行結果:

但是,有一個特殊的情況應當考慮:如果原來 10 個數中第 1 個數 number[0] 最大,如:

98		24		56		78		1		32		36		44		29		6

在經過比較和對換後,max 和 min 的指向為

98		24		56		78		1		32		36		44		29		6
↑												↑		
max											min	

在執行完上面第1行"temp=number[0] ;number[0]=*min;*min=temp;"後,最小數 1 與第 1個數 number[0] 對換,這個最大數就被調到後面去了(與最小的數對調)。

1		24		56		78		98		32		36		44		29		6
↑											↑		
max										min	

請注意:陣列元素的值改變了,但是 max 和 min 的指向未變,max 仍指向 number[0]。此時如果接著執行下一行:
temp= number[9];number[9]=* max;* max= temp;
 就會出問題,因為此時 max 並不指向最大數,而指向的是第1個數,結果是將第 1個數(最小的數已調到此處)與最後一個數 number[9] 對調。結果變成:

6		24		56		78		98		32		36		44		29		1

顯然就不對了。

為此,在以上兩行中間加上一行:

if(max== number)max = min;

由於經過執行"temp= number[0]; number[0]=* min;* min=temp;"後,10 個數的排列為

1		24		56		78		98		32		36		44		29		6
↑											↑		
max										min	

max指向第 1 個數,if 語句判別出 max 和 number 相等(即 max 和 number 都指向 number[0] ),而實際上 max 此時指向的已非最大數了,就執行"max=min",使 max 也指向 min 當前的指向。而 min 原來是指向最小數的,剛才與 number[0] 交換,而 number[0] 原來是最大數,所以現在 min 指向的是最大數。執行 max=min 後 max也指向這個最大數。

1		24		56		78		98		32		36		44		29		6
											↑		
										max,min	

然後執行下面的語句:

temp = number[9]; number[9]=*max;* max= temp;

這就沒問題了,實現了把最大數與最後一個數交換。

執行結果:

讀者可以將上面的"if(max==number)max=min;" 刪去,再執行程式,輸入以上資料,分析一下結果。

也可以採用另一種方法:先找出 10 個數中的最小數,把它和第 1 個數交換,然後再重新找 10 個數中的最大數,把它和最後一個數交換。這樣就可以避免出現以上的問題。重寫void max_min_value 函式如下:

//交換函式
void max_min_value(int *number)
{
	int *max, *min, *p, temp;
	max = min = number; //開始時使max和min都指向第1個數
	for (p = number + 1; p < number + 10; p++)
		if (*p < *min) //若p指向的數小於min指向的數,就使min指向p指向的小數
			min = p;
	temp = number[0]; //將最小數與第1個數 number[0] 交換
	number[0] = *min;
	*min = temp;
	for (p = number + 1; p < number + 10; p++)
		if (*p > *max) //若p指向的數大於max指向的數,就使max指向p指向的大數
			max = p;
	temp = number[9]; //將最大數與最後一個數交換
	number[9] = *max;
	*max = temp;
}

這種思路容易理解。

這道題有些技巧,請讀者仔細分析,學會分析程式執行時出現的各種情況,並善於根據情況予以妥善處理。

題目4:有n個整數,使前面各數順序向後移 m 個位置,最後 m 個數變成最前面 m 個數,見圖 8.43。寫一函式實現以上功能,
謎
在主函式中輸入 n個整數和輸出調整後的n個數。

解:

答案程式碼:

#include <stdio.h>
int main()
{
	void move(int[20], int, int);
	int number[20], n, m, i;
	printf("how many numbers?"); //問共有多少個數
	scanf("%d", &n);
	printf("input %d numbers:\n", n);
	for (i = 0; i < n; i++)
		scanf("%d", &number[i]);			 //輸入n 個數
	printf("how many place you want move?"); //問後移多少個位置
	scanf("%d", &m);
	move(number, n, m); //呼叫move 函式
	printf("Now,they are:\n");
	for (i = 0; i < n; i++)
		printf("%d ", number[i]);
	printf("\n");
	return 0;
}

//迴圈後移一次的函式
void move(int array[20], int n, int m)
{
	int *p, array_end;
	array_end = *(array + n - 1);
	for (p = array + n - 1; p > array; p--)
		*p = *(p - 1);
	*array = array_end;
	m--;
	if (m > 0) //遞迴呼叫,當迴圈次數m減至為0時,停止呼叫
		move(array, n, m);
}

執行結果:

題目5:有 n 個人圍成一圈,順序排號。從第 1 個人開始報數。(從1到3報數),凡報到3 的 人退出圈子,問最後留下的是原來第幾號的那位。

解:N-S圖如圖8.2所示。

答案程式碼:

#include <stdio.h>
int main()
{
	int i, k, m, n, num[50], *p;
	printf("\ninput number of person: n=");
	scanf("%d", &n);
	p = num;
	for (i = 0; i < n; i++)
		*(p + i) = i + 1; //以1至 n 為序給每個人編號
	i = 0;				  // i為每次迴圈時計數變數
	k = 0;				  // k 為按1,2,3報數時的計數變數
	m = 0;				  // m為退出人數
	while (m < n - 1)	  //當退出人數比 n-1少時(即未退出人數大於1時)執行迴圈體
	{
		if (*(p + i) != 0)
			k++;
		if (k == 3)
		{
			*(p + i) = 0; //對退出的人的編號置為0
			k = 0;
			m++;
		}
		i++;
		if (i == n) //報數到尾後,i 恢復為0了
			i = 0;
	}
	while (*p == 0)
		p++;
	printf("The last one is NO.%d\n", *p);
	return 0;
}

執行結果:

題目6:寫一函式,求一個字串的長度。在 main 函式中輸入字串,並輸出其長度。

解:

答案程式碼:

#include <stdio.h>
int main()
{
	int length(char *p);
	int len;
	char str[20];
	printf("input string: ");
	scanf("%s", str);
	len = length(str);
	printf("The length of string is %d.\n", len);
	return 0;
}

//求字串長度函式
int length(char *p)
{
	int n;
	n = 0;
	while (*p != '\0')
	{
		n++;
		p++;
	}
	return (n);
}

執行結果:

題目7:有一字串,包含 n 個字元。寫一函式,將此字串中從第 m 個字元開始的全部字元複製成為另一個字串。

解:

答案程式碼:

#include <stdio.h>
#include <string.h>
int main()
{
	void copystr(char *, char *, int);
	int m;
	char str1[20], str2[20];
	printf("input string:");
	gets(str1);
	printf("which character that begin to copy?");
	scanf("%d", &m);
	if (strlen(str1) < m)
		printf("input error!");
	else
	{
		copystr(str1, str2, m);
		printf("result:%s\n", str2);
	}
	return 0;
}

void copystr(char *p1, char *p2, int m) //字串部分複製函式
{
	int n;
	n = 0;
	while (n < m - 1)
	{
		n++;
		p1++;
	}
	while (*p1 != '\0')
	{
		*p2 = *p1;
		p1++;
		p2++;
	}
	*p2 = '\0';
}

執行結果:

題目8:輸入一行文字,找出其中大寫字母、小寫字母、空格、數字以及其他字元各有多少。

解:

答案程式碼:

#include <stdio.h>
int main()
{

	int upper = 0, lower = 0, digit = 0, space = 0, other = 0, i = 0;
	char *p, s[20];
	printf("input string:	");
	while ((s[i] = getchar()) != '\n')
		i++;
	p = &s[0];
	while (*p != '\n')
	{
		if (('A' <= *p) && (*p <= 'Z'))
			++upper;
		else if (('a' <= *p) && (*p <= 'z'))
			++lower;
		else if (*p == ' ')
			++space;
		else if ((*p <= '9') && (*p >= '0'))
			++digit;
		else
			++other;
		p++;
	}
	printf("upper case:%d	lower case:%d", upper, lower);
	printf("	space:%d	digit:%d	other:%d\n", space, digit, other);
	return 0;
}

執行結果:

題目9:寫一函式,將一個 3×3 的整型矩陣轉置。

解:

答案程式碼:

#include <stdio.h>
int main()
{
	void move(int *pointer);
	int a[3][3], *p, i;
	printf("input matrix:\n");
	for (i = 0; i < 3; i++)
		scanf("%d %d %d", &a[i][0], &a[i][1], &a[i][2]);
	p = &a[0][0];
	move(p);
	printf("Now,matrix:\n");
	for (i = 0; i < 3; i++)
		printf("%d %d %d\n", a[i][0], a[i][1], a[i][2]);
	return 0;
}
void move(int *pointer)
{
	int i, j, t;
	for (i = 0; i < 3; i++)
		for (j = i; j < 3; j++)
		{
			t = *(pointer + 3 * i + j);
			*(pointer + 3 * i + j) = *(pointer + 3 * j + i);
			*(pointer + 3 * j + i) = t;
		}
}

執行結果:

說明: a 是二維陣列,p 和形參 pointer 是指向整型資料的指標變數,p 指向陣列 0 行 0 列元素 a[0][0] 。在呼叫 move 函式時,將實參p的值&a[0][0] 傳遞給形參 pointer,在 move 函式中將 a[i][j]a[j][i] 的值互換。由於 a 陣列的大小是 3×3,而陣列元素是按行排列的,因此 a[i][j] 在 a 陣列中是第(3×i+j)個元素,例如,a[2][1]是陣列中第(3×2+1)個元素,即第 7 個元素(序號從 0 算起)。a[i][j]的地址是(pointer十3*i+j,同理,a[i][j] 的地址是(pointer十3*j+i)。將*(pointer+3*i+j)*(pointer+3*j+i)互換,就是將a[i][j]a[j][i]互換。

題目10:將一個 5×5 的矩陣中最大的元素放在中心,4 個角分別放 4 個最小的元素(順序為從左到右,從上到下依次從小到大存放),寫一函式實現之。用 main 函式呼叫。

解:

答案程式碼(1):

#include <stdio.h>
int main()
{
	void change(int *p);
	int a[5][5], *p, i, j;
	printf("input matrix:\n"); //提示輸入二維陣列各元素
	for (i = 0; i < 5; i++)
		for (j = 0; j < 5; j++)
			scanf("%d", &a[i][j]);
	p = &a[0][0]; //使p指向0行0列元素
	change(p);	  //呼叫change 函式,實現交換
	printf("Now,matrix:\n");
	for (i = 0; i < 5; i++) //輸出已交換的二維陣列
	{
		for (j = 0; j < 5; j++)
			printf("%d ", a[i][j]);
		printf("\n");
	}
	return 0;
}

//交換函式
void change(int *p)
{
	int i, j, temp;
	int *pmax, *pmin;
	pmax = p;
	pmin = p;
	for (i = 0; i < 5; i++) //找最大值和最小值的地址,並賦給 pmax,pmin
		for (j = i; j < 5; j++)
		{
			if (*pmax < *(p + 5 * i + j))
				pmax = p + 5 * i + j;
			if (*pmin > *(p + 5 * i + j))
				pmin = p + 5 * i + j;
		}
	temp = *(p + 12); //將最大值換給中心元素
	*(p + 12) = *pmax;
	*pmax = temp;
	temp = *p; //將最小值換給左上角元素
	*p = *pmin;
	*pmin = temp;
	pmin = p + 1;
	for (i = 0; i < 5; i++) //找第二最小值的地址並賦給 pmin
		for (j = 0; j < 5; j++)
			if (((p + 5 * i + j) != p) && (*pmin > *(p + 5 * i + j)))
				pmin = p + 5 * i + j;
	temp = *pmin; //將第二最小值換給右上角元素
	*pmin = *(p + 4);
	*(p + 4) = temp;
	pmin = p + 1;
	for (i = 0; i < 5; i++) //找第三最小值的地址並賦給 pmin
		for (j = 0; j < 5; j++)
			if (((p + 5 * i + j) != (p + 4)) && ((p + 5 * i + j) != p) && (*pmin > *(p + 5 * i + j)))
				pmin = p + 5 * i + j;
	temp = *pmin; //將第三最小值換給左下角元素
	*pmin = *(p + 20);
	*(p + 20) = temp;
	pmin = p + 1; //找第四最小值的地址並賦給 pmin
	for (i = 0; i < 5; i++)
		for (j = 0; j < 5; j++)
			if (((p + 5 * i + j) != p) && ((p + 5 * i + j) != (p + 4)) && ((p + 5 * i + j) != (p + 20)) && (*pmin > *(p + 5 * i + j)))
				pmin = p + 5 * i + j;
	temp = *pmin; //將第四最小值換給右下角元素
	*pmin = *(p + 24);
	*(p + 24) = temp;
}

執行結果:

說明:程式中用 change 函式來實現題目所要求的元素值的交換,分為以下幾個步驟:

①找出全部元素中的最大值和最小值,將最大值與中心元素互換,將最小值與左上角元素互換。中心元素的地址為p+12(該元素是陣列中的第 12 個元素——序號從 0 算起)。

②找出全部元素中的次小值。由於最小值已找到並放在 a[0][0]中,因此,在這一輪的比較中應不包括a[0][0],在其餘 24 個元素中值最小的就是全部元素中的次小值。在雙重 for 迴圈中應排除 a[0][0]參加比較。在 if 語句中,只有滿足條件 ((p+5*i+j)!=p) 才進行比較。不難理解, (p+5*i+j) 就是 &a[i][j] ,p 的值是 &a[0][0]((p+5*i+j)!=p) 意味著在 i 和 j 的當前值條件下 &a[i][j] 不等於 &a[0][0] 才滿足條件,這樣就排除了 a[0][0] 。因此執行雙重 for 迴圈後得到次小值,並將它與右上角元素互換,右上角元素的地址為 p+4。

③找出全部元素中第 3 個最小值。此時 a[0][0]a[0][4](即左上角和右上角元素)不應參加比較。可以看到∶在 if 語句中規定,只有滿足條件 ((p+5*i+j)!=p)&&((p+5*i+j)!=(p+4)) 才進行比較。((p+5*i+j)!=p) 的作用是排除 a[0][0]((p+5*i+j)!=(p+4)) 的作用是排除 a[0][4](p+5*i+j)&a[i][j] ,(p+4) 是 &a[0][4] ,即右上角元素的地址。滿足 ((p+5*i+j)!=(p+4)) 條件意味著排除了 a[0][4] 。執行雙重 for 迴圈後得到除了 a[0][0]a[0][4] 外的最小值,也就是全部元素中第 3 個最小值,將它

與左下角元素互換,左下角元素的地址為 p+20。

④找出全部元素中第 4 個最小值。此時 a[0][0]a[0][4]a[4][0](即左上角、右上角和左下角元素)不應參加比較,在 if 語句中規定,只有滿足條件 ((p+5*i+j)!=p)&&((p+5*i+j)!=(p+4))&&((p+5*i+j)!=(p+20)) 才進行比較。((p+5*i+j)!=p)((p+5*i+j)!=(p+4)) 的作用前已說明,((p+5*i+j)!=(p+20)) 的作用是排除 a[4][0] ,理由與前面介紹的類似。執行雙重 for 迴圈後得到除了 a[0][0]a[0][4]a[4][0] 以外的最小值,也就是全部元素中第 4 個最小值,將它與右下角元素互換,左下角元素的地址為 p+24。

上面所說的元素地址是指以元素為單位的地址,p+24 表示從指標 p 當前位置向前移動 24 個元素的位置。如果用位元組地址表示,上面右下角元素的位元組地址應為 p+4*24,其中4 是整型資料所佔的位元組數。

(2)可以改寫上面的 if 語句,change 函式可以改寫如下∶


//交換函式
void change(int *p)
{
	int i, j, temp;
	int *pmax, *pmin;
	pmax = p;
	pmin = p;
	for (i = 0; i < 5; i++) //找最大值和最小值的地址,並賦給 pmax,pmin
		for (j = i; j < 5; j++)
		{
			if (*pmax < *(p + 5 * i + j))
				pmax = p + 5 * i + j;
			if (*pmin > *(p + 5 * i + j))
				pmin = p + 5 * i + j;
		}
	temp = *(p + 12); //將最大值與中心元素互換
	*(p + 12) = *pmax;
	*pmax = temp;

	temp = *p; //將最小值與左上角元素互換
	*p = *pmin;
	*pmin = temp;

	pmin = p + 1;
	//將 a[0][1]的地址賦給 pmin,從該位置開始找最小的元素
	for (i = 0; i < 5; i++) //找第二最小值的地址並賦給 pmin
		for (j = 0; j < 5; j++)
		{
			if (i == 0 && j == 0)
				continue;
			if (*pmin > *(p + 5 * i + j))
				pmin = p + 5 * i + j;
		}
	temp = *pmin; //將第二最小值與右上角元素互換
	*pmin = *(p + 4);
	*(p + 4) = temp;

	pmin = p + 1;
	for (i = 0; i < 5; i++) //找第三最小值的地址並賦給 pmin
		for (j = 0; j < 5; j++)
		{
			if ((i == 0 && j == 0) || (i == 0 && j == 4))
				continue;
			if (*pmin > *(p + 5 * i + j))
				pmin = p + 5 * i + j;
		}
	temp = *pmin; //將第三最小值與左下角元素互換
	*pmin = *(p + 20);
	*(p + 20) = temp;

	pmin = p + 1; //找第四最小值的地址並賦給 pmin
	for (i = 0; i < 5; i++)
		for (j = 0; j < 5; j++)
		{
			if ((i == 0 && j == 0) || (i == 0 && j == 4) || (i == 4 && j == 0))
				continue;
			if (*pmin > *(p + 5 * i + j))
				pmin = p + 5 * i + j;
		}
	temp = *pmin; //將第四最小值與右下角元素互換
	*pmin = *(p + 24);
	*(p + 24) = temp;
}

這種寫法可能更容易為一般讀者所理解。

題目11:在主函式中輸入 10 個等長的字串。用另一函式對它們排序。然後在主函式輸出這 10 個已排好序的字串。

解∶

(1)用字元型二維陣列

答案程式碼:

#include <stdio.h>
#include <string.h>
int main()
{
	void sort(char s[][6]);
	int i;
	char str[10][6]; // p 是指向由 6個元素組成的一維陣列的指標
	printf("input 10 strings:\n");
	for (i = 0; i < 10; i++)
		scanf("%s", str[i]);
	sort(str);
	printf("Now,the sequence is:\n");
	for (i = 0; i < 10; i++)
		printf("%s\n", str[i]);
	return 0;
}

void sort(char s[10][6]) //形參s是指向由6個元素組成的一維陣列的指標
{
	int i, j;
	char *p, temp[10];
	p = temp;
	for (i = 0; i < 9; i++)
		for (j = 0; j < 9 - i; j++)
			if (strcmp(s[j], s[j + 1]) > 0)
			{
				//以下3行是將s[i]指向的一維陣列的內容與s[j+1]指向的一維陣列的內容互換
				strcpy(p, s[j]);
				strcpy(s[j], s[+j + 1]);
				strcpy(s[j + 1], p);
			}
}

執行結果:

(2) 用指向一維陣列的指標作函式引數

#include <stdio.h>
#include <string.h>
int main()
{
	void
	sort(char(*p)[6]);
	int i;
	char str[10][6];
	char(*p)[6];
	printf("input 10 strings:\n");
	for (i = 0; i < 10; i++)
		scanf("%s", str[i]);
	p = str;
	sort(p);
	printf("Now, the sequence is:\n");
	for (i = 0; i < 10; i++)
		printf("%s\n", str[i]);
	return 0;
}

void sort(char (*s)[6])
{
	int i, j;
	char temp[6], *t = temp;
	for (i = 0; i < 9; i++)
		for (j = 0; j < 9 - i; j++)
			if (strcmp(s[j], s[j + 1]) > 0)
			{
				strcpy(t, s[j]);
				strcpy(s[j], s[+j + 1]);
				strcpy(s[j + 1], t);
			}
}

執行結果同(1)。

題目12:用指標陣列處理上一題目,字串不等長。

解:

答案程式碼:

#include <stdio.h>
#include <string.h>
int main()
{
	void sort(char *[]);
	int i;
	char *p[10], str[10][20];
	for (i = 0; i < 10; i++)
		p[i] = str[i]; //將第i個字串的首地址賦予指標陣列 p的第i個元素
	printf("input 10 strings:\n");
	for (i = 0; i < 10; i++)
		scanf("%s", p[i]);
	sort(p);
	printf("Now,the sequence is:\n");
	for (i = 0; i < 10; i++)
		printf("%s\n", p[i]);
	return 0;
}
void sort(char *s[])
{
	int i, j;
	char *temp;
	for (i = 0; i < 9; i++)
		for (j = 0; j < 9 - i; j++)
			if (strcmp(*(s + j), *(s + j + 1)) > 0)
			{
				temp = *(s + j);
				*(s + j) = *(s + j + 1);
				*(s + j + 1) = temp;
			}
}

執行結果:

題目13:寫一個用矩形法求定積分的通用函式,分別求

$$
\int _0^1 sinxdx, \int _0^1 cosxdx,\int_01exdx
$$

說明:sin,cos,exp 函式已在系 統的數學函式庫中,程式 開頭要用 # include <math.h>。

解:

可以看出,每次需要求定積分的函式是不一樣的。可以編寫一個求定積分的通用函式 integral,它有3個形參,即下限 a、上限 b及指向函式的指標變數 fun。函式原型可寫為


float integral(float a, float b,float(* fun)());

先後呼叫integral函式3次,每次呼叫時把 a,b,sin,cos,exp 之一作為實參,把上限、下限及有關函式的入口地址傳送給形參 fun。在執行 integral 函式過程中求出定積分的值。根據以上思路編寫出程式:

#include <stdio.h>
#include <math.h>
int main()
{
	float integral(float (*)(float), float, float, int); //對 integarl函式的宣告
	float fsin(float);									 //對 fsin 函式的宣告
	float fcos(float);									 //對 fcos 函式的宣告
	float fexp(float);									 //對fexp 函式的宣告
	float a1, b1, a2, b2, a3, b3, c, (*p)(float);
	int n = 20;
	printf("input a1,b1:");
	scanf("%f,%f", &a1, &b1); //輸入求 sin(x)定積分的下限和上限
	printf("input a2,b2:");
	scanf("%f,%f", &a2, &b2); //輸入求 cos(x)定積分的下限和上限
	printf("input a3,b3:");
	scanf("%f,%f", &a3, &b3);	//輸入求e的x次方的定積分的下限和上限
	p = fsin;					//使p指向 fsin 函式
	c = integral(p, a1, b1, n); //求出 sin(x)的定積分
	printf("The integral of sin(x)is:%f\n", c);
	p = fcos;					//使 p指向fcos 函式
	c = integral(p, a2, b2, n); //求出 cos(x)的定積分
	printf("The integral of cos(x)is:%f\n", c);
	p = fexp;					//使p指向 fexp 函式
	c = integral(p, a3, b3, n); //求出e的x次方的定積分
	printf("The integral of exp(x)is:%f\n", c);
	return 0;
}

//下面是用矩形法求定積分的通用函式
float integral(float (*p)(float), float a, float b, int n)
{
	int i;
	float x, h, s;
	h = (b - a) / n;
	x = a;
	s = 0;
	for (i = 1; i <= n; i++)
	{
		x = x + h;
		s = s + (*p)(x)*h;
	}
	return (s);
}

float fsin(float x) //計算 sin(x)的函式
{
	return sin(x);
}

float fcos(float x) //計算 cos(x)的函式
{
	return cos(x);
}
float fexp(float x) //計算e的 x次方的函式
{
	return exp(x);
}

執行結果:

說明:sin,cos 和 exp 是系統提供的數學函式,在程式中定義3 個函式,即 fsin,fcos 和 fexp。分別用來計算 sin(x) ,cos(x) 和exp(x) 的值。在 main 函式中要對這 3 個函式作宣告。在 main 函式定義中 p為指向函式的指標變數,定義形式是"float(*p)(float)",表示 p 指向的函式有一個實型形參,p 指向返回值為實型的函式。在 main 函式中有"p=fsin;",表示將 fsin 函式的入口地址傳賦給 p,在呼叫 integral 函式時,用 p 作為實參,把 fsin 函式的入口地址傳遞給形參 p(相當於 fsin(x) 。fsin(x) 的值就是 sin(x)的值。因此通過呼叫 integral 函式求出 sin(x) 的定積分。求其餘兩個函式的定積分的情況與此類似。

題目14:將 n 個數按輸入時順序的逆序排列,用函式實現。

解:

答案程式碼:

#include <stdio.h>
int main()
{
	void sort(char *p, int m);
	int i, n;
	char *p, num[20];
	printf("input n:");
	scanf("%d", &n);
	printf("please input these numbers: \n");
	for (i = 0; i < n; i++)
		scanf("%d", &num[i]);
	p = &num[0];
	sort(p, n);
	printf("Now,the sequence is:\n");
	for (i = 0; i < n; i++)
		printf("%d ", num[i]);
	printf("\n");
	return 0;
}

//將n 個數逆序排列函式
void sort(char *p, int m)
{
	int i;
	char temp, *p1, *p2;
	for (i = 0; i < m / 2; i++)
	{
		p1 = p + i;
		p2 = p + (m - 1 - i);
		temp = *p1;
		*p1 = *p2;
		*p2 = temp;
	}
}

執行結果:

題目15:有一個班 4 個學生,5 門課程。

①求第 1 門課程的平均分;

②找出有兩門以上課程不及格的學生,輸出他們的學號和全部課程成績及平均成績;

③找出平均成績在 90 分以上或全部課程成績在 85分以上的學生。

分別編3個函式實現以上 3個要求。

解:

答案程式碼:

#include <stdio.h>
int main()

{
	void avsco(float *, float *);											 //函式宣告
	void avcour1(char(*)[10], float *);										 //函式宣告
	void fali2(char course[5][10], int num[], float *pscore, float aver[4]); //函式宣告
	void good(char course[5][10], int num[4], float *pscore, float aver[4]); //函式宣告
	int i, j, *pnum, num[4];
	float score[4][5], aver[4], *pscore, *paver;
	char course[5][10], (*pcourse)[10];
	printf("input course:\n");
	pcourse = course;
	for (i = 0; i < 5; i++)
		scanf("%s", course[i]);
	printf("input NO. and scores:\n");
	printf("NO.");
	for (i = 0; i < 5; i++)
		printf(",%s", course[i]);
	printf("\n");
	pscore = &score[0][0];
	pnum = &num[0];
	for (i = 0; i < 4; i++)
	{
		scanf("%d", pnum + i);
		for (j = 0; j < 5; j++)
			scanf("%f", pscore + 5 * i + j);
	}
	paver = &aver[0];
	printf("\n\n");
	avsco(pscore, paver);	  //求出每個學生的平均成績
	avcour1(pcourse, pscore); //求出第 1 門課的平均成績
	printf("\n\n");
	fali2(pcourse, pnum, pscore, paver); //找出兩門課不及格的學生
	printf("\n\n");
	good(pcourse, pnum, pscore, paver); //找出成績好的學生
	return 0;
}

//求每個學生的平均成績的函式
void avsco(float *pscore, float *paver)
{
	int i, j;
	float sum, average;
	for (i = 0; i < 4; i++)
	{
		sum = 0.0;
		for (j = 0; j < 5; j++)
			sum = sum + (*(pscore + 5 * i + j)); //累計每個學生的各科成績
		average = sum / 5;						 //計算平均成績
		*(paver + i) = average;
	}
}

//求第1課程的平均成績的函式
void avcour1(char (*pcourse)[10], float *pscore)
{
	int i;
	float sum, average1;
	sum = 0.0;
	for (i = 0; i < 4; i++)
		sum = sum + (*(pscore + 5 * i)); //累計每個學生的得分
	average1 = sum / 4;					 //計算平均成績
	printf("course 1:%s average score:%7.2f\n", *pcourse, average1);
}

//找兩門以上課程不及格的學生的函式
void fali2(char course[5][10], int num[], float *pscore, float aver[4])
{
	int i, j, k, label;
	printf("	======Student who is fail in two courses ======\n");
	printf("NO. ");
	for (i = 0; i < 5; i++)
		printf("%11s", course[i]);
	printf("	average\n");
	for (i = 0; i < 4; i++)
	{
		label = 0;
		for (j = 0; j < 5; j++)
			if (*(pscore + 5 * i + j) < 60.0)
				label++;
		if (label >= 2)
		{
			printf("%d", num[i]);
			for (k = 0; k < 5; k++)
				printf("%11.2f", *(pscore + 5 * i + k));
			printf("%11.2f\n ", aver[i]);
		}
	}
}

//找成績優秀學生(各門85分以上或平均 90分以上)的函式
void good(char course[5][10], int num[4], float *pscore, float aver[4])
{
	int i, j, k, n;
	printf("	======Students whose score is good ======\n");
	printf("NO. ");
	for (i = 0; i < 5; i++)
		printf("%11s", course[i]);
	printf("	average\n");
	for (i = 0; i < 4; i++)
	{
		n = 0;
		for (j = 0; j < 5; j++)
			if (*(pscore + 5 * i + j) > 85.0)
				n++;
		if ((n == 5) || (aver[i] >= 90))
		{
			printf("%d", num[i]);
			for (k = 0; k < 5; k++)
				printf("%11.2f", *(pscore + 5 * i + k));
			printf("%11.2f\n", aver[i]);
		}
	}
}

執行結果:

程式中 num 是存放 4 個學生學號的一維陣列,course 是存放 5 門課名稱的二維字元陣列,score 是存放 4 個學生 5 門課成績的二維陣列,aver 是存放每個學生平均成績的陣列。pnum 是指向 num陣列的指標變數,pcou 是指向 course 陣列的指標變數,psco 是指向 score 陣列的指標變數,pave是指向 aver 陣列的指標變數,見圖8.3。

函式的形參用陣列,呼叫函式時的實參用指標變數。形參也可以不用陣列而用指標變
量,請讀者自己分析。

題目16:輸入一個字串,內有數字和非數字字元,例如∶

A123x456 17960?302tab5876

將其中連續的數字作為一個整數,依次存放到一陣列 a 中。例如,123放在 a[0],456放在a[1]……統計共有多少個整數,並輸出這些數。

解∶

答案程式碼:

#include <stdio.h>
int main()
{
	char str[50], *pstr;
	int i, j, k, m, e10, digit, ndigit, a[10], *pa;
	printf("input a string:\n");
	gets(str);
	pstr = &str[0]; //字元指標 pstr置於陣列 str 首地址
	pa = &a[0];		//指標 pa 置於a陣列首地址
	ndigit = 0;		// ndigit 代表有多少個整數
	i = 0;			//代表字串中的第幾個字元
	j = 0;
	while (*(pstr + i) != '\0')
	{
		if ((*(pstr + i) >= '0') && (*(pstr + i) <= '9'))
			j++;
		else
		{
			if (j > 0)
			{
				digit = *(pstr + i - 1) - 48; //將個數位賦予digit
				k = 1;
				while (k < j) //將含有兩位以上數的其他位的數值累加於digit
				{
					e10 = 1;
					for (m = 1; m <= k; m++)
						e10 = e10 * 10;								  // e10 代表該位數所應乘的因子
					digit = digit + (*(pstr + i - 1 - k) - 48) * e10; //將該位數的數值\累加於 digit
					k++;											  //位數k自增
				}
				*pa = digit; //將數值賦予陣列 a
				ndigit++;
				pa++; //指標 pa 指向 a陣列下一元素
				j = 0;
			}
		}
		i++;
	}
	if (j > 0) //以數字結尾字串的最後一個數據
	{
		digit = *(pstr + i - 1) - 48; //將個數位賦予 digit
		k = 1;
		while (k < j)
		{
			e10 = 1; //將含有兩位以上數的其他位的數值累加於 digit
			for (m = 1; m <= k; m++)
				e10 = e10 * 10;								  // e10代表位數所應乘的因子
			digit = digit + (*(pstr + i - 1 - k) - 48) * e10; //將該位數的數值累加於 digit
			k++;											  //位數k自增
		}
		*pa = digit; //將數值賦予陣列 a
		ndigit++;
		j = 0;
	}
	printf("There are %d numbers in this line, they are:\n", ndigit);
	j = 0;
	pa = &a[0];
	for (j = 0; j < ndigit; j++) //輸出列印資料
		printf("%d ", *(pa + j));
	printf("\n");
	return 0;
}

執行結果:

題目17:寫一函式,實現兩個字串的比較。即自己寫一個 strcmp 函式,函式原型為

int strcmp(char * p1,char * p2);

設 p1指向字串sl,p2指向字串s2。要求當s1=s2時,返回值為0;若 s1≠s2,返回它們二者第 1 個不同字元的 ASCII 碼差值(如"BOY"與"BAD",第 2個字母不同,O與 A 之差為79-65=14)。如果 s1>s2,則輸出正值;如果 s1<s2,則輸出負值。

解:

答案程式碼:

#include <stdio.h>
int main()
{
	int m;
	char str1[20], str2[20], *p1, *p2;
	printf("input two strings:\n");
	scanf("%s", str1);
	scanf("%s", str2);
	p1 = &str1[0];
	p2 = &str2[0];
	m = strcmp(p1, p2);
	printf("result:%d,\n", m);
	return 0;
} //兩個字串比較函式
strcmp(char *p1, char *p2)
{
	int i;
	i = 0;
	while (*(p1 + i) == *(p2 + i))
		if (*(p1 + i++) == '\0') //相等時返回結果0
			return (0);
	return (*(p1 + i) - *(p2 + i)); /*不等時返回結果為第一個不等字元ASCII碼的差值 */
}

執行結果∶

①:

②:

題目18:編一程式,輸入月份號,輸出該月的英文月名。例如,輸入 3,則輸出"March",要求用指標陣列處理。

解:

答案程式碼:

#include <stdio.h>
int main()
{
	char *month_name[13] = {"illegal month ", " January", " February", " March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
	int n;
	printf("input month:\n");
	scanf("%d", &n);
	if ((n <= 12) && (n >= 1))
		printf("It is %s.\n", *(month_name + n));
	else
		printf("It is wrong.\n");
	return 0;
}

執行結果∶

①:

②:

③:

題目19:(1)編寫一個函式 new,對 n 個字元開闢連續的儲存空間,此函式應返回一個指標(地址),指向字串開始的空間。new(n)表示分配 n 個位元組的記憶體空間。(2)寫一函式 free;將 前面用 new 函式佔用的空間釋放。free(p)表示將 p(地址))指向的單元以後的記憶體段釋放。

解∶

(1)編寫函式new 程式如下∶

#include <stdio.h>
#define NEWSIZE 1000  //指定開闢儲存區的最大容量
char newbuf[NEWSIZE]; //定義字元陣列 newbuf
char *newp = newbuf;  //定義指標變數 newp,指向可儲存區的始端

//定義開闢儲存區的函式 new,開闢儲存區後返回指標
char *new (int n)
{
	if (newp + n <= newbuf + NEWSIZE) //開闢區未超過 newbuf 陣列的大小
	{
		newp += n;		   // newp指向儲存區的末尾
		return (newp - n); //返回一個指標,它指向儲存區的開始位置
	}
	else
		return (NULL); //當儲存區不夠分配時,返回一個空指標
}

new 函式的作用是:分配 n 個連續字元的儲存空間。為此,應先開闢一個足夠大的連續儲存區,今設定字元陣列 newbuf[1000],new 函式將在此範圍內進行操作。指標變數 newp 開始指向儲存區首位元組。在每當請求用 new 函式開闢 n 個字元的儲存區時,要先檢查一下 newbuf 陣列是否還有足夠的可用空間。若有,則使指標變數 newp 指向開闢區的末尾(newp=newp十n),見圖8.4中的 newp 。此時 newp 指向下面的空白(未分配)的區域的開頭,即 newp 的值是下一次可用空間的開始地址。如果再一次呼叫 new 函式,就從 newp 最後所指向的位元組開始分配下一個開闢區。如果若儲存區不夠分配,則返回 NULL,表示開闢失敗。

new返回一個指向字元型資料的指標,指向新開闢的區域的首位元組。

在主函式中可以用以下語句:

pt = new(n);

把新開闢的區域首位元組的地址賦給 pt,使指標變數 pt 也指向新開闢的區域的開頭。

(2)編寫函式 free

free 的作用是使newp 的值恢復為p。

free 函式如下∶

#include <stdio.h>
#define NEWSIZE 1000
char newbuf[NEWSIZE];
char *newp = newbuf;

//釋放存區函式
void free(char *p)
{
	if (p >= newbuf && p < newbuf + NEWSIZE)
		newp = p;
}

在主函式中用以下語句指令釋放 pt 指向的儲存區。

free(pt);

呼叫 free時,實參 pt 的值傳給形參 p,因此 p的值也是新開闢的區域首位元組的地址。用 if 語句檢查 p 是否在已開闢區的範圍內(否則不合法,不能釋放未分配的區域)。如果確認 p 在上述範圍內,就把 p(即 pt)的值賦給 newp,使 newp 重新指向原來開闢區的開頭,這樣,下次再開闢新區域時就又從 newp 指向的位元組開始分配,這就相當於釋放了此段空間,使這段空間可再分配作其他用途。

有人可能對 if語句所檢查的條件 "p>=newbuf && p<newbuf + NEWSIZE" 不理解,為什麼不直接檢查 "p==newbuf" 呢?他們認為 p 應當指向 newbuf 的開頭。這裡有個細節要考慮∶當第 1 次呼叫 new 函式開闢儲存區時,new 函式的返回值(也是 pt 的值)的確是 newbuf。但是如果接著再開闢第 2 個儲存區,new 函式的返回值(也是 pt 的值)就不是newbuf了,而是指標變數 newp的當前值,即 newbuf+n了。因此,呼叫free 函式時,形參p得到的值也是第 2個儲存區的起始地址。要釋放的是第 2個儲存區,而不是第1個儲存區。但p的值必然在"newbuf 到 newbuf+NEWSIZE"的範圍內。

上面只是編寫了兩個函式,並不是完整的程式,它沒有main 函式。本題是示意性的,可以大體瞭解開闢儲存區的思路。

題目20:用指向指標的指標的方法對 5個字串排序並輸出。

解:

程式如下

#include <stdio.h>
#include <string.h>
#define LINEMAX 20 //定義字串的最大長度
int main()
{
	void sort(char **p);
	int i;
	char **p, *pstr[5], str[5][LINEMAX];
	for (i = 0; i < 5; i++)
		pstr[i] = str[i]; //將第i個字串的首地址賦予指標陣列 pstr 的第i個元素
	printf("input 5 strings:\n");
	for (i = 0; i < 5; i++)
		scanf("%s", pstr[i]);
	p = pstr;
	sort(p);
	printf("\nstrings sorted:\n");
	for (i = 0; i < 5; i++)
		printf("%s\n", pstr[i]);
	return 0;
}
//用冒泡法對5個字串排序函式
void sort(char **p)
{
	int i, j;
	char *temp;
	for (i = 0; i < 5; i++)
	{
		for (j = i + 1; j < 5; j++) //比較後交換字串地址
		{
			if (strcmp(*(p + i), *(p + j)) > 0)
			{
				temp = *(p + i);
				*(p + i) = *(p + j);
				*(p + j) = temp;
			}
		}
	}
}

執行結果:

題目21:用指向指標的指標的方法對 n個整數排序並輸出。要求將排序單獨寫成一個函式。n 個整數在主函式中輸入,最後在主函式中輸出。

解:

答案程式碼:

#include <stdio.h>
int main()
{
	void sort(int **p, int n);
	int i, n, data[20], **p, *pstr[20];
	printf("input n:\n");
	scanf("%d", &n);
	for (i = 0; i < n; i++)
		pstr[i] = &data[i]; //將第i個整數的地址賦予指標陣列 pstr 的第i個元素
	printf("input %d integer numbers:", n);
	for (i = 0; i < n; i++)
		scanf("%d", pstr[i]);
	p = pstr;
	sort(p, n);
	printf("Now,the sequence is:\n");
	for (i = 0; i < n; i++)
		printf("%d ", *pstr[i]);
	printf("\n");
	return 0;
}
void sort(int **p, int n)
{
	int i, j, *temp;
	for (i = 0; i < n - 1; i++)
	{
		for (j = i + 1; j < n; j++)
		{
			if (**(p + i) > **(p + j)) //比較後交換整數地址
			{
				temp = *(p + i);
				*(p + i) = *(p + j);
				*(p + j) = temp;
			}
		}
	}
}

執行結果:

data陣列用來存放n個整數,pstr 是指標陣列,每一個元素指向 data 陣列中的一個元素,p 是指向指標的指標,請參考圖8.5。圖8.5(a)表示的是排序前的情況,圖8.5(b)表示的是排序後的情況。可以看到,data 陣列中的數的順序沒有變化,而 pstr 指標陣列中的各元素的值(也就是它們的指向)改變了。