C primer plus复习题答案(下)

C primer plus复习题答案下

C primer plus复习题答案下

  • C primer plus复习题答案下
    • 第十二章 存储类别,链接和存储
    • 第十三章 文件输入输出
    • 第十四章 结构和其他数据形式
    • 第十五章 位操作
    • 第十六章 C预处理器和C库
    • 第十七章 高级数据表示

第十二章 存储类别,链接和存储

  1. 哪些类别的变量可以成为它所在函数的局部变量?

    自动存储类别;寄存器存储类别;静态、无链接存储类别
    
  2. 哪些类别的变量在它所在程序的运行期一直存在?

    静态、无链接存储类别;静态、内部链接存储类别;静态、外部链接存储类别。
    
  3. 哪些类别的变量可以被多个文件使用?哪些类别的变量仅限于在一个 文件中使用?

    静态、外部链接存储类别可以被多个文件使用;静态、内部链接存储类别。
    
  4. 块作用域变量具有什么链接属性?

    无链接接
    
  5. extern 关键字有什么用途?

    extern用来引用式声明(referencing declaration),告诉编译器该变量的定义在程序的别处,该声明不会引起分配存储空间。
    对于函数来说,extern 关键字声明定义在其他文件中,使用extern让编译器在别处查找函数的定义式声明, 且除非使用static关键字,否则一般函数声明都默认为extern。
    
  6. 考虑下面两行代码,就输出的结果而言有何异同:

int * p1 = (int *)malloc(100 * sizeof(int)); 

int * p1 = (int *)calloc(100, sizeof(int)); 
两者都分配了一个内容100个int类型的数组,第二行代码使用了calloc()把数组中的每个元素都设置为0
  1. 下面的变量对哪些函数可见?程序是否有误?

     /* 文件 1 */ 
     int daisy; 
    int main(void) 
    { 
        int lily;
     	...; 
    } 
    int petal() 
    { 
        extern int daisy, lily; 
        ...; 
    } 
    
    /* 文 件 2 */  
    extern int daisy;  
    static int lily; 
     int rose; 
    int stem() 
    { 
        int rose; 
        ...; 
    } 
    void root() 
    { 
        ...; 
    }
    
    默认情况下,daisy只对main()可见,以extern声明的daisy才对petal()、stem()和root()可见。文件2中的extern  int  daisy;声明使得daisy对文件2中的所
    有函数都可见。第1个lily是main()的局部变量。petal()函数中引用的lily是错误的,因为两个文件中都没有外部链接的lily。虽然文件2中有一个静态的lily,但是它只对文件2可见。第1个外部rose对root()函数可见,但是stem()中的局部rose覆盖了外部的rose。
    
    1. 下面程序会打印什么?

      #include 
      char color = 'B';
      void first(void); 
      void second(void); 
      int main(void) 
      { 
          extern char color; 
          printf("color in main() is %c\n", color); 
          first(); 
          printf("color in main() is %c\n", color); 
          second(); 
          printf("color in main() is %c\n", color); 
          return 0; 
      } 
      void first(void) 
      { 
          char color; 
       	color = 'R';  
          printf("color in first() is %c\n", color); 
      } 
      void second(void) 
      { 
          color = 'G'; 
          printf("color in second() is %c\n", color); 
      } 
      
      color in main() is B
      color in first() is R
      color in main() is B
      color in second() is G
      color in main() is G
      first()函数没有使用color变量,但是second()函数使用了。
      
      1. 假设文件的开始处有如下声明:
       static int plink; 
       int value_ct(const int arr[], int value, int n);
      

      a. 以上声明表明了程序员的什么意图?

      b. 用 const int value 和 const int n 分别替换 int value 和 int n,是否对主调 程序的值加强保护。

      a.声明告诉我们,程序将使用一个变量plink,该文件包含的函数都可以使用这个变量。calu_ct()函数的第1个参数是指向一个整数的指针,并假定它指向内含n个元素的数组。这里关键是要理解该程序不允许使用指针arr修改原始数组中的值。
      b.不会。value和n已经是原始数据的备份,所以该函数无法更改主调函数中相应的值。这些声明的作用是防止函数修改value和n的值。例如,如果用const限定n,就不能使用n++表达式。
      

第十三章 文件输入输出

  1. 下面的程序有什么问题?
int main(void) 

{ 
    int * fp; 
    int k; 
    fp =  fopen("gelatin"); 
    for (k = 0; k <  30; k++) 
        fputs(fp, "Nanette eats  cgelatin."); 
    fclose("gelatin");  
    return 0; 
} 

​ 根据文件定义,应包含#include 。应该把fp声明为文件指针:FILE *fp;。要给fopen()函数提供一种模式:fopen(“gelatin”,“w”),或者"a"模式。fputs()函数的参数顺序应该反过来。输出字符串应该有一个换行符,提高可读性。fclose()函数需要一个文件指针,而不是一个文件名:fclose(fp);。下面是修改后的版本:

#include 
int main(void)
{
	FILE * fp;
	int k;
	fp = fopen("gelatin", "w");
	for (k = 0; k < 30; k++)
		fputs("Nanette eats gelatin.\n", fp);
    fclose(fp);
	return 0;
}
  1. 下面的程序完成什么任务?(假设在命令行环境中运行)
#include 
#include  
#include  
int main(int argc, char *argv [])
{
	int ch;
	FILE *fp; 
    if (argc < 2)
		exit(EXIT_FAILURE); 
    if ((fp = fopen(argv[1], "r")) == NULL) 
		exit(EXIT_FAILURE);
    while ((ch = getc(fp)) != EOF)
        if (isdigit(ch)) 
            putchar(ch); 
    fclose(fp); 
    return 0;
}

​ 如果可以打开的话,会打开与命令行第1个参数名相同名称的文件,并在屏幕上显示文件中的每个数字字符。

  1. 假设程序中有下列语句:
#include
FILE  * fp1,* fp2; 
char ch; 
fp1 =  fopen("terky", "r"); 
fp2 =  fopen("jerky", "w"); 
另外,假设成功打开了两个文件。补全下面函数调用中缺少的参数:  
 a.ch = getc(); 
b.fprintf( ,"%c\n", ); 
c.putc( , ); 
d.fclose(); 
/* 关闭 terky 文件 */
a.ch = getc(fp1);
b.fprintf(fp2,"%c"\n",ch);
c.putc(ch,fp2);
d.fclose(fp1); /* 关闭terky文件 */

注意:
fp1用于输入操作,因为它识别以读模式打开的文件。与此类似,fp2以写模式打开文件,所以常用于输出操作。

  1. 编写一个程序,不接受任何命令行参数或接受一个命令行参数。如果 有一个参数,将其解释为文件名;如果没有参数,使用标准输入(stdin)作 为输入。假设输入完全是浮点数。该程序要计算和报告输入数字的算术平均 值。

    #include 
    #include 
    int main(int argc, char * argv [])
    {
    	FILE * fp;
    	double n;
    	double sum = 0.0;
        int ct = 0;
    	if (argc == 1)
    		fp = stdin;
    	else if (argc == 2)
    	{
    		if ((fp = fopen(argv[1], "r")) == NULL)
    		{
    			fprintf(stderr, "Can't open %s\n", argv[1]);
    			exit(EXIT_FAILURE);
    		}
    	}
    	else
    	{
    		fprintf(stderr, "Usage: %s [filename]\n", argv[0]);
    		exit(EXIT_FAILURE);
    	}
    	while (fscanf(fp, "%lf", &n) == 1)
    	{
    		sum += n;
       		 ++ct;
    	}
    	if (ct > 0)
    		printf("Average of %d values = %f\n", ct, sum / ct);
    	else
    		printf("No valid data.\n");
    	return 0;
    }
    
  2. 编写一个程序,接受两个命令行参数。第 1 个参数是字符,第 2 个参 数是文件名。要求该程序只打印文件中包含给定字符的那些行。 注意 C 程序根据’\n’识别文件中的行。假设所有行都不超过 256 个字符,你可 能会想到用 fgets()。

    #include 
    #include 
    #define BUF 256
    int has_ch(char ch, const char * line);
    int main(int argc, char * argv [])
    {
    	FILE * fp;
    	char ch;
    	char line[BUF];
    	if (argc != 3)
    	{
    		printf("Usage: %s character filename\n", argv[0]);
    		exit(EXIT_FAILURE);
    	}
    	ch = argv[1][0];
    	if ((fp = fopen(argv[2], "r")) == NULL)
    	{
    		printf("Can't open %s\n", argv[2]);
    		exit(EXIT_FAILURE);
    	}
    	while (fgets(line, BUF, fp) != NULL)
    	{
    		if (has_ch(ch, line))
    			fputs(line, stdout);
    	}
    	fclose(fp);
    	return 0;
    }
    int has_ch(char ch, const char * line)
    {
    	while (*line)
    	if (ch == *line++)
    	return(1);
    	return 0;
    }
    

    fgets()和 fputs()函数要一起使用,因为 fgets()会把按下 Enter 键的\n 留在
    字符串中, fputs()与puts()不一样,不会添加一个换行符。

  3. 二进制文件和文本文件有何区别?二进制流和文本流有何区别?

    二进制文件与文本文件的区别是,这两种文件格式对系统的依赖性不同。二进制流和文本流的区别包括是在读写流时程序执行的转换(二进制流不转换,而文本流可能要转换换行符和其他字符)。
    
  4. a.分别用 fprintf()和 fwrite()储存 8238201 有何区别? b.分别用 putc()和 fwrite()储存字符 S 有何区别?

    a.用fprintf()储存8238201时,将其视为7个字符,保存在7字节中。用fwrite()储存时,使用该数的二进制表示,将其储存为一个4字节的整数。
    b.没有区别。两个函数都将其储存为一个单字节的二进制码。
    
  5. 下面语句的区别是什么?

    printf("Hello, %s\n", name); 
    fprintf(stdout, "Hello, %s\n", name);
    fprintf(stderr, "Hello, %s\n", name); 
    
    第1条语句是第2条语句的速记表示。第3条语句把消息写到标准错误上。通常,标准错误被定向到与标准输出相同的位置。但是标准错误不受标准输出重定向的影响。
    
  6. “a+”、"r+"和"w+"模式打开的文件都是可读写的。哪种模式更适合用 来更改文件中已有的内容?

可以在以"r+"模式打开的文件中读写,所以该模式最合适。"a+"只允许在文件的末尾添加内容。"w+"模式提供一个空文件,丢弃文件原来的内容。

第十四章 结构和其他数据形式

  1. 下面的结构模板有什么问 题:

    structure  
    { 
    	char itable;
        int num[20];
        char * togs 
    }
    
    正确的关键是 struct,不是 structure。该结构模板要在左花括号前面有一个标记,或者在右花括号后面有一个结构变量名。另外,*togs后面和模板结尾处都少一个分号。
    

    2.下面是程序的一部分,输出是什么?

     #include   
    struct house 
    { 
        float  sqft;
        int rooms;
        int  stories;
        char  address[40]; 
    }; 
    int  main(void)
    { 
        struct house fruzt = {1560.0, 6, 1, "22 Spiffo Road"}; 
        struct house *sign; sign  = &fruzt; 
        printf("%d %d\n", fruzt.rooms, sign->stories); 
        printf("%sc \n",  fruzt.address); 
        printf("%c %c\n", sign->address[3], fruzt.address[4]); 
        return 0;
    }
    
    
    
    //6 1
    //22 Spiffo Road
    //S p
    
    1. 设计一个结构模板储存一个月份名、该月份名的 3 个字母缩写、该月 的天数以及月份号。

      struct month {
      	char name[10];
      	char abbrev[4];
      	int days;
      	int monumb;
      };
      
    2. 定义一个数组,内含 12 个结构(第 3 题的结构类型)并初始化为一 个年份(非闰年)。

      struct month months[12] =
      {
      	{ "January", "jan", 31, 1 },
      	{ "February", "feb", 28, 2 },
      	{ "March", "mar", 31, 3 },
      	{ "April", "apr", 30, 4 },
      	{ "May", "may", 31, 5 },
      	{ "June", "jun", 30, 6 },
      	{ "July", "jul", 31, 7 },
      	{ "August", "aug", 31, 8 },
      	{ "September", "sep", 30, 9 },
      	{ "October", "oct", 31, 10 },
      	{ "November", "nov", 30, 11 },
      	{ "December", "dec", 31, 12 }
      };
      
    3. 编写一个函数,用户提供月份号,该函数就返回一年中到该月为止 (包括该月)的总天数。假设在所有函数的外部声明了第 3 题的结构模版和 一个该类型结构的数组。

      extern struct month months [];
      int days(int month)
      {
      	int index, total;
      	if (month < 1 || month > 12)
      		return(-1); /* error signal */
      	else
      	{
      		for (index = 0, total = 0; index < month; index++)
      			total += months[index].days;
      		return(total);
      	}
      }
      //注意,index比月数小1,因为数组下标从0开始。然后,用index  <  month代替index <= month。
      
    4. a.假设有下面的 typedef,声明一个内含 10 个指定结构的数组。然 后,单独给成员赋值(或等价字符串),使第 3 个元素表示一个焦距长度有 500mm,孔径为 f/2.0 的 Remarkata 镜头。

      typedef struct lens { /* 描述镜头  */ 
      	float foclen; /* 焦距长度,单位为 mm */ 
      	float fstop; /* 孔径 */  
      	char brand[30]; /* 品牌名称 */ 
      } LENS;
      

      b.重写 a,在声明中使用一个待指定初始化器的初始化列表,而不是对 每个成员单独赋值。

      a.要包含string.h头文件,提供strcpy()的原型:

      typedef struct lens { /* lens 描述 */
      	float foclen;   /* 焦距长度,单位:mm */
      	float fstop;    /* 孔径 */
      	char brand[30];/* 品牌 */
      } LENS;
      

      LENS bigEye[10];
      bigEye[2].foclen = 500;
      bigEye[2].fstop = 2.0;
      strcpy(bigEye[2].brand, “Remarkatar”);

      b.

      LENS bigEye[10] = { [2] = {500, 2, "Remarkatar"} };
      
      
      
    5. 考虑下面程序片 段:

      struct name  
      { 
          char  1072 first[20]; 
       	char  last[20]; 
      }; 
      struct bem  
      { 
          int limbs;  
          struct name title;
          char type[30]; 
      }; 
      struct bem * pb;  
      struct bem deb = { 
          6, 
          { "Berbnazel", "Gwolkapwolk" }, 
          "Arcturan" 
      }; 
      pb = &deb; 
      
      a.
      6
      Arcturan
      cturan
      
          
       b.使用结构名和指针:
      deb.title.last
      pb->title.last
      
          
      c.下面是一个版本:
      #include 
      #include "starfolk.h"   /* 让结构定义可用 */
      void prbem (const struct bem * pbem )
      {
      	printf("%s %s is a %d-limbed %s.\n", pbem->title.first,
      			pbem->title.last, pbem->limbs, pbem->type);
      }
      

      a.下面的语句分别打印什么?

      printf("%d\n", deb.limbs); 
      
      printf("%s\n", pb->type);  
      
      printf("%s\n", pb->type + 2); 
      

      b.如何用结构表示法(两种方法)表示"Gwolkapwolk"?

      c.编写一个函数,以 bem 结构的地址作为参数,并以下面的形式输出结 构的内容(假定结构模板在一个名为 starfolk.h 的头文件中):Berbnazel Gwolkapwolk is a 6-limbed Arcturan.

    6. 考虑下面的声明:

    struct fullname  
    
    { 
    	char  fname[20]; 
        char  lname[20]; 
    }; 
    struct bard 
    { 
        struct  fullname name; 
        int  born; 
        int died; 
    };
    struct bard willie; 
    struct bard *pt = &willie; 
    

    a.用 willie 标识符标识 willie 结构的 born 成员。

    b.用 pt 标识符标识 willie 结构的 born 成员。

    c.调用 scanf()读入一个用 willie 标识符标识的 born 成员的值。

    d.调用 scanf()读入一个用 pt 标识符标识的 born 成员的值。

    e.调用 scanf()读入一个用 willie 标识符标识的 name 成员中 lname 成员的 值。

    f.调用 scanf()读入一个用 pt 标识符标识的 name 成员中 lname 成员的 值。

    g.构造一个标识符,标识 willie 结构变量所表示的姓名中名的第3个字母 (英文的名在前)。

    h.构造一个表达式,表示 willie 结构变量所表示的名和姓中的字母总 数。


    a.willie.born
    b.pt->born
    c.scanf(“%d”, &willie.born);
    d.scanf(“%d”, &pt->born);
    e.scanf(“%s”, willie.name.lname);
    f.scanf(“%s”, pt->name.lname);
    g.willie.name.fname[2]
    h.strlen(willie.name.fname) + strlen(willie.name.lname)


    1. 定义一个结构模板以储存这些项:汽车名、马力、EPA(美国环保 局)城市交通 MPG(每加仑燃料行驶的英里数)评级、轴距和出厂年份。 使用 car 作为该模版的标记。

      struct car {
      	char name[20];
      	float hp;
      	float epampg;
      	float wbase;
      	int year;
      };
      
    2. 假设有如下结构:

struct gas 
{ 
    float distance; 
    float gals; 
    float mpg; 
}; 

a.设计一个函数,接受 struct gas 类型的参数。假设传入的结构包含 distance 和 gals 信息。该函数为 mpg 成员计算正确的值,并把值返回该结 构。

b.设计一个函数,接受 struct gas 类型的参数。假设传入的结构包含 distance 和 gals 信息。该函数为 mpg 成员计算正确的值,并把该值赋给合适 的成员

struct gas 
{
	float distance;
	float gals;
	float mpg;
};
struct gas mpgs(struct gas trip)
{
	if (trip.gals > 0)
		trip.mpg = trip.distance / trip.gals;
	else
		trip.mpg = -1.0;
	return trip;
}
void set_mpgs(struct gas * ptrip)
{
	if (ptrip->gals > 0)
		ptrip->mpg = ptrip->distance / ptrip->gals;
	else
		ptrip->mpg = -1.0;
}

注意,第1个函数不能直接改变其主调程序中的值,所以必须用返回值
才能传递信息。

struct gas idaho = {430.0, 14.8};  // 设置前两个成员
idaho = mpgs(idaho);        // 重置数据结构

但是,第2个函数可以直接访问最初的结构:

struct gas ohio = {583, 17.6};   //设置前两个成员
set_mpgs(&ohio);          // 设置第3个成员
  1. 声明一个标记为 choices 的枚举,把枚举常量 no、yes 和 maybe 分别 设置为 0、1、2。

    enum choices {no, yes, maybe};
    
  2. 声明一个指向函数的指针,该函数返回指向 char 的指针,接受一个 指向 char 的指针和一个 char 类型的值。

    char * (*pfun)(char *, char);
    
  3. 声明 4 个函数,并初始化一个指向这些函数的指针数组。每个函数 都接受两个 double 类型的参数,返回 double 类型的值。另外,用两种方法 使用该数组调用带 10.0 和 2.5 实参的第 2 个函数。

    double sum(double, double);
    double diff(double, double);
    double times(double, double);
    double divide(double, double);
    **double (pf1[4])(double, double) = {sum, diff, times, divide};
    或者用更简单的形式,把代码中最后一行替换成:
    typedef double (ptype) (double, double);

    ptype pfl[4] = {sum,diff, times, divide};
    调用diff()函数:
    pf1[1](10.0, 2.5);   // 第1种表示法
    (*pf1[1])(10.0, 2.5); // 等价表示法

第十五章 位操作

  1. 把下面的十进制转换为二进制:
a.3
b.13
c.59
d.119

a.00000011
b.00001101
c.00111011
d.01110111

  1. 将下面的二进制值转换为十进制、八进制和十六进制的形式:
a.00010101
b.01010101
c.01001100
d.10011101

a.21, 025, 0x15
b.85, 0125, 0x55
c.76, 0114, 0x4C
d.157, 0235, 0x9D

  1. 对下面的表达式求值,假设每个值都为8位:
b.3 & 6
c.3 | 6
d.1 | 6
e.3 ^ 6
f.7 >> 1
1188
g.7 << 2

a.252
b.2
c.7
d.7
e.5
f.3
g.28

  1. 对下面的表达式求值,假设每个值都为8位:
a.~0
b.!0
c.2 & 4
d.2 && 4
e.2 | 4
f.2 || 4
g.5 << 3

a.255
b.1 (not false is true)
c.0
d.1 (true and true is true)
e.6
f.1 (true or true is true)
g.40

  1. 因为ASCII码只使用最后7位,所以有时需要用掩码关闭其他位,其相应的二进制掩码是什么?分别用十进制、八进制和十六进制来表示这个掩码。
    掩码的二进制是1111111;十进制是127;八进制是0177;十六进制是0x7F。
  2. 程序清单15.2中,可以把下面的代码:
while (bits-- > 0)
{
	mask |= bitval;
	bitval <<= 1;
}
替换成:
while (bits-- > 0)
{
	mask += bitval;
	bitval *= 2;
}

程序照常工作。这是否意味着*=2等同于<<=1?+=是否等同于|=?


bitval * 2和bitval << 1都把bitval的当前值增加一倍,它们是等效的。但是mask +=bitval和mask |= bitval只有在bitval和mask没有同时打开的位时效果才相同。例如, 2 | 4得6,但是3 | 6也得6。


. 7. a.Tinkerbell计算机有一个硬件字节可读入程序。该字节包含以下信息:
C primer plus复习题答案(下)_第1张图片

Tinkerbell和IBM PC一样,从右往左填充结构位字段。创建一个适合存
放这些信息的位字段模板。
b.Klinkerbell与Tinkerbell类似,但是它从左往右填充结构位字段。请为
Klinkerbell创建一个相应的位字段模板。

.a.
struct tb_drives 
{
	unsigned int diskdrives : 2;
	unsigned int : 1;
	unsigned int cdromdrives : 2;
	unsigned int : 1;
	unsigned int harddrives : 2;
};
b.
struct kb_drives
 {
	unsigned int harddrives : 2;
	unsigned int : 1;
	unsigned int cdromdrives : 2;
	unsigned int : 1;
	unsigned int diskdrives : 2;
};

第十六章 C预处理器和C库

  1. 下面的几组代码由一个或多个宏组成,其后是使用宏的源代码。在每种情况下代码的结果是什么?这些代码是否是有效代码?(假设其中的变量已声明)
a.
#define FPM 5280 /*每英里的英尺数*/
dist = FPM * miles;
b.
#define FEET 4
#define POD FEET + FEET
plort = FEET * POD;
c.
#define SIX = 6;
nex = SIX;
d.
#define NEW(X) X + 5
y = NEW(y);
berg = NEW(berg) * lob;
est = NEW(berg) / NEW(y);
nilp = lob * NEW(-berg);
1.a.dist = 5280 * miles;有效。
b.plort = 4 * 4 + 4;有效。但是如果用户需要的是4 * (4 + 4),则应该使用
#define POD (FEET + FEET)
c.nex = = 6;;无效(如果两个等号之间没有空格,则有效,但是没有意
义)。显然,用户忘记了在编写预处理器代码时不用加=。
d.y = y + 5;有效。berg = berg + 5 * lob;有效,但是可能得不到想要的结
果。est = berg +5/y + 5;有效,但是可能得不到想要的结果。
  1. 修改复习题1中d部分的定义,使其更可靠。
    .#define NEW(X) ((X) + 5)
  2. 定义一个宏函数,返回两值中的较小值。
#define MIN(X,Y) ( (X) < (Y) ? (X) : (Y) )
  1. 定义EVEN_GT(X, Y)宏,如果X为偶数且大于Y,该宏返回1。
#define EVEN_GT(X,Y) ( (X) > (Y) && (X) % 2 == 0 ? 1 : 0 )
  1. 定义一个宏函数,打印两个表达式及其值。例如,若参数为3+4和4*12,则打印:
#define PR(X,Y) printf(#X " is %d and " #Y " is %d\n", X,Y)

(因为该宏中没有运算符(如,乘法)作用于X和Y,所以不需要使用圆括号。)

3+4 is 7 and 4*12 is 48
  1. 创建#define指令完成下面的任务。
a.创建一个值为25的命名常量。
b.SPACE表示空格字符。
c.PS()代表打印空格字符。
d.BIG(X)代表X的值加3。
e.SUMSQ(X, Y)代表X和Y的平方和。
a.#define QUARTERCENTURY 25
b.#define SPACE ' '
c.#define PS() putchar(' ')或#define PS() putchar(SPACE)
d.#define BIG(X) ((X) + 3)
e.#define SUMSQ(X,Y) ((X)*(X) + (Y)*(Y))
  1. 定义一个宏,以下面的格式打印名称、值和int类型变量的地址:
    name: fop; value: 23; address: ff464016

试试这样:#define P(X) printf(“name: “#X”; value: %d; address: %p\n”,X, &X) (如果你的实现无法识别地址专用的%p转换说明,可以用%u或%lu代替。)

  1. 假设在测试程序时要暂时跳过一块代码,如何在不移除这块代码的前提下完成这项任务?
    使用条件编译指令。一种方法是使用#ifndef:
#define _SKIP_ /* 如果不需要跳过代码,则删除这条指令 */
#ifndef _SKIP_
/* 需要跳过的代码 */
#endif
  1. 编写一段代码,如果定义了PR_DATE宏,则打印预处理的日期。
 #ifdef PR_DATE
	printf("Date = %s\n", _ _DATE_ _);
#endif
  1. 内联函数部分讨论了3种不同版本的square()函数。从行为方面看,这3种版本的函数有何不同?

第1个版本返回xx,这只是返回了square()的double类型值。例如,square(1.3)会返回1.69。第2个版本返回 (int)(xx),计算结果被截断后返回。但是,由于该函数的返回类型是double,int类型的值将被升级为double类型的值,所以1.69将先被转换成1,然后被转换成1.00。第3个版本返回(int)(x*x+0.5)。加上 0.5可以让函数把结果四舍五入至与原值最接近的值,而不是简单地截断。所以,1.69+0.5得2.19,然后被截断为2,然后被转换成2.00;而1.44+0.5得1.94,被截断为1,然后被转换成1.00。

  1. 创建一个使用泛型选择表达式的宏,如果宏参数是_Bool类型,对"boolean"求值,否则对"not boolean"求值。
    这是一种方案: #define BOOL(X) _Generic((X), _Bool : "boolean",default : "not boolean")

  2. 下面的程序有什么错误?
    . 应该把argv参数声明为char *argv[]类型。命令行参数被储存为字符串,所以该程序应该先把argv[1]中的字符串转换成double类型的值。例如,用stdlib.h库中的atof()函数。程序中使用了sqrt()函数,所以应包含math.h头文件。程序在求平方根之前应排除参数为负的情况(检查参数是否大于或等于0)。

#include 
int main(int argc, char argv[])
{
printf("The square root of %f is %f\n", argv[1],sqrt(argv[1]) );
}

13.假设 scores 是内含 1000 个 int 类型元素的数组,要按降序排序该数
组中的值。假设你使用qsort()和comp()比较函数。

a.如何正确调用qsort()?
b.如何正确定义comp()?
a.qsort( (void *)scores, (size_t) 1000, sizeof (double), comp);
b.下面是一个比较使用的比较函数:
int comp(const void * p1, const void * p2)
{
/* 要用指向int的指针来访问值 */
/* 在C中是否进行强制类型转换都可以,在C++中必须进行强制类型转换 */
	const int * a1 = (const int *) p1; const int * a2 = (const int *)p2;
	if (*a1 > *a2)
		return -1;
	else if (*a1 == *a2)
		return 0;
	else
		return 1;
}

14.假设data1是内含100个double类型元素的数组,data2是内含300个
double类型元素的数组。

a.编写memcpy()的函数调用,把data2中的前100个元素拷贝到data1中。
b.编写memcpy()的函数调用,把data2中的后100个元素拷贝到data1中。

a.函数调用应该类似:memcpy(data1, data2, 100 * sizeof(double));
b.函数调用应该类似:memcpy(data1, data2 + 200 , 100 * sizeof(double));

第十七章 高级数据表示

  1. 定义一种数据类型涉及哪些内容?

     定义一种数据类型包括确定如何储存数据,以及设计管理该数据的一系列函数。
    
  2. 为什么程序清单17.2 只能沿一个方向遍历链表?如何修改struct film定义才能沿两个方向遍历链表?

     因为每个结构包含下一个结构的地址,但是不包含上一个结构的地址,所以这个链表只能沿着一个方向遍历。可以修改结构,在结构中包含两个指针,一个指向上一个结构,一个指向下一个结构。当然,程序也要添加代码,在每次新增结构时为这些指针赋正确的地址。
    
  3. 什么是ADT?

     ADT是抽象数据类型,是对一种类型属性集和可以对该类型进行的操作的正式定义。ADT应该用一般语言表示,而不是用某种特殊的计算机语言,而且不应该包含实现细节
    
  4. QueueIsEmpty()函数接受一个指向queue结构的指针作为参数,但是也可以将其编写成接受一个queue结构作为参数。这两种方式各有什么优缺点?

     直接传递变量的优点:该函数查看一个队列,但是不改变其中的内容。直接传递队列变量,意味着该函数使用的是原始队列的副本,这保证了该函数不会更改原始的数据。直接传递变量时,不需要使用地址运算符或指针。
     直接传递变量的缺点:程序必须分配足够的空间储存整个变量,然后拷贝原始数据的信息。如果变量是一个大型结构,用这种方法将花费大量的时间和内存空间。
     传递变量地址的优点:如果待传递的变量是大型结构,那么传递变量的地址和访问原始数据会更快,所需的内存空间更少。
     传递变量地址的缺点:必须记得使用地址运算符或指针。在K&R C中,函数可能会不小心改变原始数据,但是用ANSI C中的const限定符可以解决这个问题。
    
  5. 栈(stack)是链表系列的另一种数据形式。在栈中,只能在链表的一端添加和删除项,项被“压入”栈和“弹出”栈。因此,栈是一种LIFO(即后进先出last in,first out)结构。
    a.设计一个栈ADT
    b.为栈设计一个C编程接口,例如stack.h头文件

a.
类型名: 栈
类型属性: 可以储存有序项
类型操作: 初始化栈为空
确定栈是否为空
确定栈是否已满
从栈顶添加项(压入项)
从栈顶删除项(弹出项)
b.下面以数组形式实现栈,但是这些信息只影响结构定义和函数定义的
细节,不会影响函数原型的接口。
/* stack.h –– 栈的接口 */
#include 
/* 在这里插入 Item 类型 */
/* 例如: typedef int Item; */
#define MAXSTACK 100
typedef struct stack
{
Item items[MAXSTACK]; /* 储存信息 */
int top; /* 第1个空位的索引 */
} Stack;
/* 操作: 初始化栈 */
/* 前提条件: ps 指向一个栈 */
/* 后置条件: 该栈被初始化为空 */
void InitializeStack(Stack * ps);
/* 操作: 检查栈是否已满 */
/* 前提条件: ps 指向之前已被初始化的栈 */
/* 后置条件: 如果栈已满,该函数返回true;否则,返回false*/
bool FullStack(const Stack * ps);
/* 操作: 检查栈是否为空 */
/* 前提条件: ps 指向之前已被初始化的栈 */
/* 后置条件: 如果栈为空,该函数返回true;否则,返回false*/
bool EmptyStack(const Stack *ps);
/* 操作: 把项压入栈顶 */
/* 前提条件: ps 指向之前已被初始化的栈 */
/* item 是待压入栈顶的项 */
/* 后置条件: 如果栈不满,把 item 放在栈顶,该函数返回ture;*/
/* 否则,栈不变,该函数返回 false */
bool Push(Item item, Stack * ps);
/* 操作: 从栈顶删除项 */
/* 前提条件: ps 指向之前已被初始化的栈 */
/* 后置条件: 如果栈不为空,把栈顶的item拷贝到*pitem,*/
/* 删除栈顶的item,该函数返回ture; */
/* 如果该操作后栈中没有项,则重置该栈为空。*/
/* 如果删除操作之前栈为空,栈不变,该函数返回false*/
bool Pop(Item *pitem, Stack * ps);
  1. 在一个含有3个项的分类列表中,判断一个特定项是否在该列表中,用顺序查找和二叉查找方法分别需要最多多少次?当列表中有1023个项时分别是多少次?65535个项是分别是多少次?
    C primer plus复习题答案(下)_第2张图片

  2. 假设一个程序用本章介绍的算法构造了一个储存单词的二叉查找树。
    假设根据下面所列的顺序输入单词,请画出每种情况的树:

a.nice food roam dodge gate office wave
b.wave roam office nice gate food dodge
c.food dodge roam wave office gate nice
d.nice roam office food wave gate dodge

C primer plus复习题答案(下)_第3张图片

  1. 考虑复习题7构造的二叉树,根据本章的算法,删除单词food之后,
    各树是什么样子?

    C primer plus复习题答案(下)_第4张图片

你可能感兴趣的:(c,primer,plus,c语言,c++,开发语言)