《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 指標陣列中的各元素的值(也就是它們的指向)改變了。