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、再类比一下,八进制(或十六进制)与十进制之间如何相互换算?

posted @ 2016-07-03 18:57  立体风  阅读(3845)  评论(0编辑  收藏  举报