Linux C一站式編程習題答案


<<Linux C編程一站式學習>>前15章習題及部分習題答案  
2011-05-12 23:11
 
http://hi.baidu.com/wenlei168/blog/item/c24a0621003928aa4623e885.html
 
1、總結前面介紹的轉義序列的規律,想想在printf的格式化字符串中怎么表示一個%字符?寫個小程序
 
試驗一下。
 
printf("\%");
===================================================================
1、假設變量x和n是兩個正整數,我們知道x/n這個表達式的結果要取Floor,例如x是17,n是4,則結果
 
是4。如果希望結果取Ceiling應該怎么寫表達式呢?例如x是17,n是4,則結果是5;x是16,n是4,則結
 
果是4。
 
(x+n-1)/n
===================================================================
1、定義一個函數increment,它的作用是把傳進來的參數加1。例如:
void increment(int x)
{
x = x + 1;
}
 
int main(void)
{
int i = 1, j = 2;
increment(i); /* i now becomes 2 */
increment(j); /* j now becomes 3 */
return 0;
}
我們在main函數中調用increment增加變量i和j的值,這樣能奏效嗎?為什么?
 
能 定義的函數相當於一個自增的語句,調用函數后執行自增命令后就能增加變量i和j的值
 
2、如果在一個程序中調用了printf函數卻不包含頭文件,例如int main(void) { printf("\n"); },編
 
譯時會報警告:warning: incompatible implicit declaration of built-in function ‘printf’。
 
請分析錯誤原因。
 
頭文件作為一種包含功能函數、數據接口聲明的載體文件,用於保存程序的聲明(declaration),而定義
 
文件用於保存程序的實現 (implementation)。 沒有了頭文件,程序便不能執行實現printf函數
===================================================================
1、以下程序段編譯能通過,執行也不出錯,但是執行結果不正確(根據第 3 節 “程序的調試”的定義
 
,這是一個語義錯誤),請分析一下哪里錯了。還有,既然錯了為什么編譯能通過呢?
int x = -1;
if (x > 0);
printf("x is positive.\n");
 
正確的寫法是:
int x = -1;
if (x > 0)                              /*多了個分號,變成了語句,無論如何都會打印下個語句
 
*/
printf("%d is positive.\n", x);    /*只能打印x is positive,沒有%d與冒號外的變量x*/
else printf("%d isn't positive.\n", x);
===================================================================
1、寫兩個表達式,分別取整型變量x的個位和十位。
 
x = x % 10 ;
x = x % 100 / 10 ;
 
2、寫一個函數,參數是整型變量x,功能是打印x的個位和十位。
 
void p(int x)  
{
printf("%d %d", x%10, x%100/10);
}
===================================================================  
1、把代碼段
if (x > 0 && x < 10);
else
printf("x is out of range.\n");
改寫成下面這種形式:
if (____ || ____)
printf("x is out of range.\n");
____應該怎么填?
 
if(x <= 0 || x >= 10)
 
2、把代碼段:
if (x > 0)
printf("Test OK!\n");
else if (x <= 0 && y > 0)
printf("Test OK!\n");
else
printf("Test failed!\n");
改寫成下面這種形式:
if (____ && ____)
printf("Test failed!\n");
else
printf("Test OK!\n");
____應該怎么填?
 
if (x < 0 && y < 0)
 
3、有這樣一段代碼:
if (x > 1 && y != 1) {
...
} else if (x < 1 && y != 1) {
...
} else {
...
}
要進入最后一個else,x和y需要滿足條件____ || ____。這里應該怎么填?
 
x = 1 || y = 1
 
4、以下哪一個if判斷條件是多余的可以去掉?這里所謂的“多余”是指,某種情況下如果本來應該打印
 
Test OK!,去掉這個多余條件后仍然打印Test OK!,如果本來應該打印Test failed!,去掉這個多余條
 
件后仍然打印Test failed!。
if (x<3 && y>3)
printf("Test OK!\n");
else if (x>=3 && y>=3)
printf("Test OK!\n");
else if (z>3 && x>=3)
printf("Test OK!\n");
else if (z<=3 && y>=3)
printf("Test OK!\n");
else
printf("Test failed!\n");
 
可以去掉的只有x<3 && y>3
最終結果為:
if (x>=3 && y>=3)
printf("Test OK!\n");
else if (z>3 && x>=3)
printf("Test OK!\n");
else if (z<=3 && y>=3)
printf("Test OK!\n");
else
printf("Test failed!\n");
===================================================================
1、編寫一個布爾函數int is_leap_year(int year),判斷參數year是不是閏年。如果某年份能被4整除
 
,但不能被100整除,那么這一年就是閏年,此外,能被400整除的年份也是閏年。
 
       int is_leap_year(int year)
       {
              if (year % 4) {
                     return 0;
              } else if (year % 100) {
                     return 1;
              } else if (year % 400) {
                     return 0;
              } else {
                     return 1;
              }
       }
 
 
2、編寫一個函數double myround(double x),輸入一個小數,將它四舍五入。例如myround(-3.51)的值
 
是-4.0,myround(4.49)的值是4.0。可以調用math.h中的庫函數ceil和floor實現這個函數。
 
double myround(double x)
{
return floor(x - 0.5) + 1.0;
}
===================================================================
1、編寫遞歸函數求兩個正整數a和b的最大公約數(GCD,Greatest Common Divisor),使用Euclid算法
 

1. 如果a除以b能整除,則最大公約數是b。
2. 否則,最大公約數等於b和a%b的最大公約數。
Euclid算法是很容易證明的,請讀者自己證明一下為什么這么算就能算出最大公約數。最后,修改你的
 
程序使之適用於所有整數,而不僅僅是正整數。
 
/*負數不是自然數,一般不討論最大公約數*/
#include <stdio.h>    
 
int Euclid(int a,int b)    
{    
      if(b==0)    
            return a;    
      else  
            return Euclid(b,a%b);    
}    
 
int main(void)    
{    
    int m, n;    
    printf("Enter two integers: ");    
    scanf("%d %d", &m, &n);    
    printf("Greatest common divisor: %d\n", Euclid(m, n));    
 
    system("pause");    
    return 0;    
}  
 
2、編寫遞歸函數求Fibonacci數列的第n項,這個數列是這樣定義的:
fib(0)=1
fib(1)=1
fib(n)=fib(n-1)+fib(n-2)
上面兩個看似毫不相干的問題之間卻有一個有意思的聯系:
 
int fibonacci(int n)
{
if(n > 2)
return fibonacci(n-1) + fibonacci(n-2);
if(n < 0)
return -1;
return 1;
}
 
===================================================================
1、用循環解決第 3 節 “遞歸”的所有習題,體會遞歸和循環這兩種不同的思路。
 
循環解決求兩個正整數a和b的最大公約數:
int Euclid(int a,int b)    
{    
      if(b==0)    
            return a;    
      else  
            return Euclid(b,a%b);    
}
 
循環解決求Fibonacci數列的第n項:
int fibonacci(int n)
{
int t, a[n];
a[0] = a[1] = 1;
for(t = 2; t < n +1 ; t++)
a[t] = a[t-1] + a[t-2];
return a[n];
}
 
 
2、編寫程序數一下1到100的所有整數中出現多少次數字9。在寫程序之前先把這些問題考慮清楚:
1. 這個問題中的循環變量是什么?
2. 這個問題中的累加器是什么?用加法還是用乘法累積?
3. 在第 2 節 “if/else語句”的習題1寫過取一個整數的個位和十位的表達式,這兩個表達式怎
 
樣用到程序中?
 
#include<stdio.h>
int main(void)
{
int t, count;
count =0;
for(t = 9; t<100; t++)
{
   if(t % 10 == 9)
   count++;
   if(t % 100 / 10== 9)
   count++;
}
printf("1到100的所有整數中出現 %d 次數字9\n",count);
return 0;
}
 
===================================================================
1、求素數這個程序只是為了說明break和continue的用法才這么寫的,其實完全可以不用break和
 
continue,請讀者修改一下控制流程,去掉break和continue而保持功能不變。
 
#include <stdio.h>
int is_prime(int n)
{  
int i;  
for (i = 2; i < n; i++)  
   if (n % i == 0)  
   {  
    if (i == n)  
     return 1;  
    else  
     return 0;
   }
}
 
int main(void)
{  
int i;  
for (i = 1; i <= 100; i++)
   {  
   if (is_prime(i))  
   printf("%d\n", i);  
}  
return 0;
}
2、上一節講過怎樣把for循環改寫成等價的while循環,但也提到如果循環體中有continue語句這兩種形
 
式就不等價了,想一想為什么不等價了?
while(exc):  
                   while(exc)-->...-->continue-(直接)->while(exc)-->...
for(a;b;c):
                   a-->b-->...-->continue-(直接)->c-->b-->...
===================================================================
1、上面打印的小九九有一半數據是重復的,因為8*9和9*8的結果一樣。請修改程序打印這樣的小九九:
1  
2 4  
3 6 9  
4 8 12 16  
5 10 15 20 25  
6 12 18 24 30 36  
7 14 21 28 35 42 49  
8 16 24 32 40 48 56 64  
9 18 27 36 45 54 63 72 81
 
#include <stdio.h>
int main(void)
{  
int i, j;
   for (i = 1; i <= 9; i++)  
{  
   for (j = 1; j < i+1; j++)
   {
    if(i*j < 10)
     printf("%d       ", i*j);
    else
     printf("%d      ", i*j);  
   }
   printf("\n");
}
   return 0;
}
 
2、編寫函數diamond打印一個菱形。如果調用diamond(3, '*')則打印:
*
* * *
*
如果調用diamond(5, '+')則打印:
   +
+ + +
+ + + + +
+ + +
   +
如果用偶數做參數則打印錯誤提示。
 
#include<stdio.h>
void diamond(int line, char mark)
{
int i, j;
for(i = 1; i <= line/2 + 1; i++)
{
   for(j = 1; j <= line + 1 - 2*i; j++)
   printf(" ");
   for(j = 1; j <= 2*i - 1; j++)
   printf("%c ", mark);
   printf("\n");
}
for(i -= 2; i > 0; i--)    /*i -= 1是錯誤的,因為上面通過i++后i又增加了一次*/
{
   for(j = 1; j <= line + 1 - 2*i; j++)
   printf(" ");
   for(j = 1; j <= 2*i - 1; j++)
   printf("%c ", mark);
   printf("\n");
}
}
   
int main(void)
{
int line;
char mark;
scanf("%d %c",&line, &mark);
if(line % 2)
diamond(line, mark);
else printf("Error!\n");
return 0;
}
 
===================================================================
1、在本節的基礎上實現一個打印復數的函數,打印的格式是x+yi,如果實部或虛部為0則省略,例如:
 
1.0、-2.0i、-1.0+2.0i、1.0-2.0i。最后編寫一個main函數測試本節的所有代碼。想一想這個打印函數
 
應該屬於上圖中的哪一層?
 
void printf(int a, int b)
{
scanf("%f %f",&a, &b);
if (a && b)
{
   if(b < 0.0)
   printf("z = %f %fi\n", a, b);
   else printf("z = %f + %fi\n", a, b);
}
else if (a && b == 0)
   printf("z = %f\n", a);
else if (a == 0.0 && b)
   printf("z = %fi\n", b);
else printf("z = 0\n");
}
 
數據存儲表示層
 
 
2、實現一個用分子分母的格式來表示有理數的結構體rational以及相關的函數,rational結構體之間可
 
以做加減乘除運算,運算的結果仍然是rational。測試代碼如下:
int main(void)
{
struct rational a = make_rational(1, 8); /* a=1/8 */
struct rational b = make_rational(-1, 8); /* b=-1/8 */
print_rational(add_rational(a, b));
print_rational(sub_rational(a, b));
print_rational(mul_rational(a, b));
print_rational(div_rational(a, b));
 
return 0;
}
注意要約分為最簡分數,例如1/8和-1/8相減的打印結果應該是1/4而不是2/8,可以利用第 3 節 “遞歸
 
”練習題中的Euclid算法來約分。在動手編程之前先思考一下這個問題實現了什么樣的數據抽象,抽象
 
層應該由哪些函數組成。
 
 
===================================================================
1、本節只給出了make_from_real_img和make_from_mag_ang函數的實現,請讀者自己實現real_part、
 
img_part、magnitude、angle這些函數。
 
 
2、編譯運行下面這段程序:
#include <stdio.h>
 
enum coordinate_type { RECTANGULAR = 1, POLAR };
 
int main(void)
{
int RECTANGULAR;
printf("%d %d\n", RECTANGULAR, POLAR);
return 0;
}
結果是什么?並解釋一下為什么是這樣的結果。
 
37814176 2
因為枚舉中與整型變量的定義中都有RECTANGULAR
===================================================================
1、編寫一個程序,定義兩個類型和長度都相同的數組,將其中一個數組的所有元素拷貝給另一個。既然
 
數組不能直接賦值,想想應該怎么實現。
 
===================================================================
1、用rand函數生成[10, 20]之間的隨機整數,表達式應該怎么寫?
 
srand((unsigned)time(NULL));  
===================================================================
1、補完本節直方圖程序的main函數,以可視化的形式打印直方圖。例如上一節統計20個隨機數的結果是
 

0 1 2 3 4 5 6 7 8 9
 
* * * *     * * *     *
*     * *     * * *     *
      * *        *
                  *
                  *
 
#include<stdio.h>
int main(void)
{
char num[64];
int i, j, a[10]={0}, max=0;
gets(num);
for(i=0; i<64; i++)
for(j=0; j<10; j++)
{
   if(num[i]==j+'0')
   ++a[j];
}
for(i=0; i<10; i++)
max= max>a[i] ? max: a[i];
printf("0 1 2 3 4 5 6 7 8 9\n");
for(i=0; i<max; i++)                     /*控制列*/  
for(j=0; j<10; j++)                      /*控制行*/
{
   if(a[j]>0&&j!=9){
   printf("* ");
   a[j]--;
   }
   else if(a[j]>0&&j==9){
   printf("* \n");
   a[j]--;
   }
   else if(a[j]==0&&j!=9)printf("   ");
   else if(a[j]==0&&j==9)printf("\n");
}
printf("\n");
}
 
2、定義一個數組,編程打印它的全排列。比如定義:
#define N 3
int a[N] = { 1, 2, 3 };
則運行結果是:
$ ./a.out
1 2 3  
1 3 2  
2 1 3  
2 3 1  
3 2 1  
3 1 2  
1 2 3
程序的主要思路是:
1. 把第1個數換到最前面來(本來就在最前面),准備打印1xx,再對后兩個數2和3做全排列。
2. 把第2個數換到最前面來,准備打印2xx,再對后兩個數1和3做全排列。
3. 把第3個數換到最前面來,准備打印3xx,再對后兩個數1和2做全排列。
可見這是一個遞歸的過程,把對整個序列做全排列的問題歸結為對它的子序列做全排列的問題,注意我
 
沒有描述Base Case怎么處理,你需要自己想。你的程序要具有通用性,如果改變了N和數組a的定義(比
 
如改成4個數的數組),其它代碼不需要修改就可以做4個數的全排列(共24種排列)。
完成了上述要求之后再考慮第二個問題:如果再定義一個常量M表示從N個數中取幾個數做排列(N == M
 
時表示全排列),原來的程序應該怎么改?
最后再考慮第三個問題:如果要求從N個數中取M個數做組合而不是做排列,就不能用原來的遞歸過程了
 
,想想組合的遞歸過程應該怎么描述,編程實現它。
 
 
===================================================================
1、看下面的程序:
#include <stdio.h>
 
int main(void)
{
int i;
char str[6] = "hello";
char reverse_str[6] = "";
 
printf("%s\n", str);
for (i = 0; i < 5; i++)
   reverse_str[5-i] = str[i];
printf("%s\n", reverse_str);
return 0;
}
首先用字符串"hello"初始化一個字符數組str(算上'\0'共6個字符)。然后用空字符串""初始化一個同
 
樣長的字符數組reverse_str,相當於所有元素用'\0'初始化。然后打印str,把str倒序存入
 
reverse_str,再打印reverse_str。然而結果並不正確:
$ ./main  
hello
 
我們本來希望reverse_str打印出來是olleh,結果什么都沒有。重點懷疑對象肯定是循環,那么簡單驗
 
算一下,i=0時,reverse_str[5]=str[0],也就是'h',i=1時,reverse_str[4]=str[1],也就是'e',
 
依此類推,i=0,1,2,3,4,共5次循環,正好把h,e,l,l,o五個字母給倒過來了,哪里不對了?用gdb跟蹤
 
循環,找出錯誤原因並改正。
 
===================================================================
1、快速排序是另外一種采用分而治之策略的排序算法,在平均情況下的時間復雜度也是Θ(nlgn),但比
 
歸並排序有更小的時間常數。它的基本思想是這樣的:
int partition(int start, int end)
{
從a[start..end]中選取一個pivot元素(比如選a[start]為pivot);
在一個循環中移動a[start..end]的數據,將a[start..end]分成兩半,
使a[start..mid-1]比pivot元素小,a[mid+1..end]比pivot元素大,而a[mid]就是pivot元素;
return mid;
}
 
void quicksort(int start, int end)
{
int mid;
if (end > start) {
   mid = partition(start, end);
   quicksort(start, mid-1);
   quicksort(mid+1, end);
}
}
請補完partition函數,這個函數有多種寫法,請選擇時間常數盡可能小的實現方法。想想快速排序在最
 
好和最壞情況下的時間復雜度是多少?快速排序在平均情況下的時間復雜度分析起來比較復雜,有興趣
 
的讀者可以參考[算法導論]。
 
===================================================================
1、實現一個算法,在一組隨機排列的數中找出最小的一個。你能想到的最直觀的算法一定是Θ(n)的,
 
想想有沒有比Θ(n)更快的算法?
 
2、在一組隨機排列的數中找出第二小的,這個問題比上一個稍復雜,你能不能想出Θ(n)的算法?
 
3、進一步泛化,在一組隨機排列的數中找出第k小的,這個元素稱為k-th Order Statistic。能想到的
 
最直觀的算法肯定是先把這些數排序然后取第k個,時間復雜度和排序算法相同,可以是Θ(nlgn)。這個
 
問題雖然比前兩個問題復雜,但它也有平均情況下時間復雜度是Θ(n)的算法,將上一節習題1的快速排
 
序算法稍加修改就可以解決這個問題:
/* 從start到end之間找出第k小的元素 */
int order_statistic(int start, int end, int k)
{
用partition函數把序列分成兩半,中間的pivot元素是序列中的第i個;
if (k == i)
   返回找到的元素;
else if (k > i)
   從后半部分找出第k-i小的元素並返回;
else
   從前半部分找出第k小的元素並返回;
}
請編程實現這個算法。
 
===================================================================
1、本節的折半查找算法有一個特點:如果待查找的元素在數組中有多個則返回其中任意一個,以本節定
 
義的數組int a[8] = { 1, 2, 2, 2, 5, 6, 8, 9 };為例,如果調用binarysearch(2)則返回3,即a[3]
 
,而有些場合下要求這樣的查找返回a[1],也就是說,如果待查找的元素在數組中有多個則返回第一個
 
。請修改折半查找算法實現這一特性。
 
2、編寫一個函數double mysqrt(double y);求y的正平方根,參數y是正實數。我們用折半查找來找這個
 
平方根,在從0到y之間必定有一個取值是y的平方根,如果我們查找的數x比y的平方根小,則x2<y,如果
 
我們查找的數x比y的平方根大,則x2>y,我們可以據此縮小查找范圍,當我們查找的數足夠准確時(比
 
如滿足|x2-y|<0.001),就可以認為找到了y的平方根。思考一下這個算法需要迭代多少次?迭代次數的
 
多少由什么因素決定?
 
3、編寫一個函數double mypow(double x, int n);求x的n次方,參數n是正整數。最簡單的算法是:
double product = 1;
for (i = 0; i < n; i++)
product *= x;
這個算法的時間復雜度是Θ(n)。其實有更好的辦法,比如mypow(x, 8),第一次循環算出x?x=x2,第二
 
次循環算出x2?x2=x4,第三次循環算出4?x4=x8。這樣只需要三次循環,時間復雜度是Θ(lgn)。思考一
 
下如果n不是2的整數次冪應該怎么處理。請分別用遞歸和循環實現這個算法。
從以上幾題可以看出,折半查找的思想有非常廣泛的應用,不僅限於從一組排好序的元素中找出某個元
 
素的位置,還可以解決很多類似的問題。[編程珠璣]對於折半查找的各種應用和優化技巧有非常詳細的
 
介紹。
 
===================================================================
1、修改本節的程序,要求從起點到終點正向打印路線。你能想到幾種辦法?
 
2、本節程序中predecessor這個數據結構占用的存儲空間太多了,改變它的存儲方式可以節省空間,想
 
想該怎么改。
 
3、上一節我們實現了一個基於堆棧的程序,然后改寫成遞歸程序,用函數調用的棧幀替代自己實現的堆
 
棧。本節的DFS算法也是基於堆棧的,請把它改寫成遞歸程序,這樣改寫可以避免使用predecessor數據
 
結構,想想該怎么做
 
===================================================================
1、本節的例子直接在隊列元素中加一個指針成員表示前趨,想一想為什么上一節的例 12.3 “用深度優
 
先搜索解迷宮問題”不能采用這種方法表示前趨?
2、本節例子中給隊列分配的存儲空間是512個元素,其實沒必要這么多,那么解決這個問題至少要分配
 
多少個元素的隊列空間呢?跟什么因素有關?
 
===================================================================
1、現在把迷宮問題的要求改一下,只要求程序給出最后結論就可以了,回答“有路能到達終點”或者“
 
沒有路能到達終點”,而不需要把路徑打印出來。請把例 12.4 “用廣度優先搜索解迷宮問題”改用環
 
形隊列實現,然后試驗一下解決這個問題至少需要分配多少個元素的隊列空間。
 
===================================================================
1、二進制小數可以這樣定義:
(0.A1A2A3...)2=A1×2-1+A2×2-2+A3×2-3+...
這個定義同時也是從二進制小數到十進制小數的換算公式。從本節講的十進制轉二進制的推導過程出發
 
類比一下,十進制小數換算成二進制小數應該怎么算?
2、再類比一下,八進制(或十六進制)與十進制之間如何相互換算?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM