复习题

1.假设所有变量的类型都是int,下列各项变量的值是多少:

a.x = (2 + 3) 6;
b.x = (12 + 6)/2
3;
c.y = x = (2 + 3)/4;
d.y = 3 + 2(x = 7/2);
答:
a.30
b.27(不是3,(12+6)/(2
3))得3
c.x = y = 1(整数除法)
d.x = 3(整数除法),y =9

2.假设所有变量的类型都是int,下列各项变量的值是多少:

a.x = (int)3.8 + 3.3;
b.x = (2 + 3) 10.5;
c.x = 3 / 5
22.0;
d.x = 22.0 3 / 5;
答:
a.6(由3+3.3截断而来)
b.52
c.0(0
22.0的结果)
d.13(66.0/5或13.2,然后把结果赋值给int类型变量)

3.对下列各表达式求值:

a.30.0 / 4.0 5.0;
b.30.0 / (4.0
5.0);
c.30 / 4 5;
d.30
5 / 4;
e.30 / 4.0 5;
f.30 / 4
5.0;
答:
主要考查,在对表达式求值的过程中,浮点数和整数相互转换以及算数运算符计算顺序。
a.37.5(7.55.0的结果)
b.1.5(30.0/20.0的结果)
c.35(7
5的结果)
d.37(150/4的结果)
e.37.5(7.55的结果)
f.35.0(7
5.0的结果)

4.请找出下面的程序中的错误。

  1. int main(void)
  2. {
  3. int i = 1,
  4. float n;
  5. printf("Watch out! Here come a bunch of fractions!\n");
  6. while (i < 30)
  7. n = 1/i;
  8. printf(" %f", n);
  9. printf("That's all, folks!\n");
  10. return;
  11. }

答:
第0行:应增加一行#include
第3行:末尾用分号,而不是逗号。
第6行: While语句创建了一个无限循环。因为i的值始终为1,所以它总是小于30。推测应该是想写 While(i++<30)。
第6~8行:这样的缩进布局不能使第7行和第8行组成一个代码块。由于没有用花括号括起来,While循环只包括第7行,所以要添加花括号。
第7行:因为1和i都是整数,所以当i为1时,除法的结果是1;当i为更大的数时,除法结果为0。用n=1.0/i,i在除法运算之前会被转换为浮点数,这样就能得到非零值。
第8行:在格式化字符串中没有换行符(\n),这导致数字被打印成一行。
第10行:应该是 return0。
下面是正确的版本:

#include <stdio.h>

int main(void)
{
    int i = 1;
    float n;
    printf("Watch out! Here come a bunch of fractions!\n");
    while (i++ < 30)
    {
        n = 1.0 / i;
        printf(" %f\n", n);
    }
    printf("That's all, folks!\n");
    return 0;
}

5.在一个程序中找毛病

#include <stdio.h>
#define S_TO_M 60
int main(void)
{
    int sec, min, left;
    printf("This program converts seconds to minutes and ");
    printf("seconds.\n");
    printf("Just enter the number of seconds.\n");
    printf("Enter 0 to end the program.\n");
    while (sec > 0) {
    scanf("%d", &sec);
    min = sec/S_TO_M;
    left = sec % S_TO_M;
    printf("%d sec is %d min, %d sec. \n", sec, min, left);
    printf("Next input?\n");
}
printf("Bye!\n");
return 0;
}

答:
这个版本最大的问题是测试条件(sec是否大于0?)和 scanf()语句获取sec变量的值之间的关系。具体地说,第一次测试时,程序尚未获得sec的值,用来与0作比较的是正好在sec变量内存位置上的一个垃圾值。一个比较笨拙的方法是初始化sec(如,初始化为1)。这样就可通过第一次测试。不过,还有另一个问题。当最后输入0结束程序时,在循环结束之前不会检查sec,所以0也被打印了出来。因此,更好的方法是在 While测试之前使用 scanf()语句。
可以这样修改:

scanf("%d", &sec);
while(sec > 0)
{
    min = sec/S_TO_M;
    left = sec % S_TO_M;
    printf("%d sec is %d min, %d sec. \n", sec, min, left);
    printf("Next input?\n");
    scanf("%d", &sec);
}

6.下面的程序将打印出什么内容?

#include <stdio.h>
#define FORMAT "%s! C is cool!\n"
int main(void)
{
    int num = 10;
    printf(FORMAT,FORMAT);
    printf("%d\n", ++num);
    printf("%d\n", num++);
    printf("%d\n", num--);
    printf("%d\n", num);
    return 0;
}

答:
主要考查明示常量及自增自减表达式的值。常量可以理解为替换,替换之后再进行解析或运算。
运行结果如下

%s! C is cool!
! C is cool!
11
11
12
11

7.下面的程序将打印出什么内容?

#include <stdio.h>
int main(void)
{
    char c1, c2;
    int diff;
    float num;
    c1 = 'S';
    c2 = 'O';
    diff = c1 - c2;
    num = diff;
    printf("%c%c%c:%d %3.2f\n", c1, c2, c1, diff, num);
    return 0;
}

答:运算结果

SOS:4 4.00

8.下面的程序将打印出什么内容?

#include <stdio.h>
#define TEN 10
int main(void)
{
    int n = 0;
    while (n++ < TEN)
    printf("%5d", n);
    printf("\n");
    return 0;
}

答:运行结果如下

    1    2    3    4    5    6    7    8    9    10

9.修改上一个程序,使其可以打印字母a~g。

#include <stdio.h>
#define TEN 7
int main(void)
{
    char ch = 'a';
    int n = 0;
    while (n < TEN)
    {
        printf("%5c",ch + n);
        n++;
    }
    printf("\n");
    return 0;
}

10.假设下面是完整程序中的一部分,它们分别打印什么?

主要考查循环,把握住循环临界点即可
a.

int x = 0;
while (++x < 3)
printf("%4d", x);

结果: 1 2
b.

int x = 100;
while (x++ < 103)
printf("%4d\n",x);
printf("%4d\n",x);

结果:

 101
 102
 103
 104

c.

char ch = 's';
while (ch < 'w')
{
    printf("%c", ch);
    ch++;
}
printf("%c\n",ch);

结果:

stuvw

11.下面的程序会打印出什么?

#define MESG "COMPUTER BYTES DOG"
#include <stdio.h>
int main(void)
{
    int n = 0;
    while ( n < 5 )
    printf("%s\n", MESG);
    n++;
    printf("That's all.\n");
    return 0;
}

答:
这个程序有点问题。 While循环没有用花括号把两个缩进的语句括起来,只有 printf()是循环的一部分,所以该程序一直重复打印消息 COMPUTER BYTES DOG,直到强行关闭程序为止。

12.分别编写一条语句,完成下列各任务(或者说,使其具有以下副作用):

a.将变量x的值增加10
x= x + 10;
b.将变量x的值增加1
x++;或++x;``或``x = x +1
c.将a与b之和的两倍赋给c
c = 2 * (a + b);
d.将a与b的两倍之和赋给c
c = a + 2*b;

13.分别编写一条语句,完成下列各任务:

a.将变量x的值减少1
x--;--x;x = x -1
b.将n除以k的余数赋给m
m = n % k;
c.q除以b减去a,并将结果赋给p
p = q / (b - a);
d.a与b之和除以c与d的乘积,并将结果赋给x
x = (a + b) / (c * d);


编程练习

1.把用分钟表示的时间转换成用小时和分钟表示的时间。

使用#define或const创建一个表示60的符号常量或const变量。通过while循环让用户重复输入值,直到用户输入小于或等于0的值才停止循环。
答:

#include <stdio.h>
#define MIN 60

int main(void)
{
    int minute = 0;
    int hour = 0;
    int remain_minute = 0;    
    printf("请输入分钟数,输入小于或等于0的数程序将会停止:");
    while(scanf("%d", &minute) == 1)
    {
        if(minute <= 0) break;
        hour = minute / MIN;
        remain_minute = minute % MIN;
        printf("您输入的分钟数转换为小时为:%d:%d\n", hour, remain_minute);
    }
    printf("程序结束!\n");
    return 0;
}

2.打印一定规则的数字

编写一个程序,提示用户输入一个整数,然后打印从该数到比该数大10的所有整数(例如,用户输入5,则打印5~15的所有整数,包括5和15)。要求打印的各值之间用一个空格、制表符或换行符分开。

#include <stdio.h>
#define STEP 10

int main(void)
{
    int num = 0;
    int step = 0;
    printf("请输入一个整数:");
    scanf("%d", &num);
    step = STEP + num;
    while(num <= step)
    {
        printf("%d\n", num);
        num++;
    }
    printf("程序结束\n");
    return 0;    
}

3.天数转换为周数和天数

编写一个程序,提示用户输入天数,然后将其转换成周数和天数。例如,用户输入18,则转换成2周4天。以下面的格式显示结果:18 days are 2 weeks, 4 days.
通过while循环让用户重复输入天数,当用户输入一个非正值时(如0或-20),循环结束。

#include <stdio.h>
#define WEEK 7

int main(void)
{
    int days, week, remain_days;

    printf("请输入天数:");
    scanf("%d", &days);    
    while(days > 0)
    {
        week = days / WEEK;
        remain_days = days % WEEK;
        printf("您输入的天数转换之后:%d周%d天\n", week, remain_days);
        scanf("%d", &days);
    }
    printf("程序结束\n");

    return 0;
}

4.身高(cm)转换为身高(英寸英尺)

编写一个程序,提示用户输入一个身高(单位:厘米),并分别以厘米和英寸为单位显示该值,允许有小数部分。程序应该能让用户重复输入身高,直到用户输入一个非正值。其输出示例如下:

Enter a height in centimeters: 182
182.0 cm = 5 feet, 11.7 inches
Enter a height in centimeters (<=0 to quit): 168.7
168.0 cm = 5 feet, 6.4 inches
Enter a height in centimeters (<=0 to quit): 0
bye
#include <stdio.h>
#define INC_PER_FEET 12
#define CM_PER_INCHE 2.54

int main(void)
{
    float cm,inche;
    int feet;
    printf("Enter a height in centimeters: ");
    scanf("%f", &cm);
    while(cm > 0)
    {
        inche = cm / CM_PER_INCHE;
        feet = (int)(inche / INC_PER_FEET);
        inche = inche - feet * INC_PER_FEET;            
        printf("%.1f cm = %d feet, %.1f inches\n", cm, feet, inche);
        printf("Enter a height in centimeters(<= to quit!): ");
        scanf("%f", &cm);
    }
    printf("bye!\n");
    return 0;
}

5.按照一定的规律,计算能赚到多少钱

你可以认为addemup.c是计算20天里赚多少钱的程序(假设第1天赚$1、第2天赚$2、第3天赚$3,以此类推)。修改程序,使其可以与用户交互,根据用户输入的数进行计算(即,
用读入的一个变量来代替20)。

#include <stdio.h>
int main(void) /* finds sum of first n integers */
{
    int count, sum;
    int n;
    printf("Enter the upper limit: ");
    scanf("%d", &n);
    count = 0;
    sum = 0;
    while (count++ < n)
    sum = sum + count;
    printf("sum = %d\n", sum);
    return 0;
}

6.第五题的一个延展

修改编程练习5的程序,使其能计算整数的平方和(可以认为第1天赚$1、第2天赚$4、第3天赚$9,以此类推,这看起来很不错)。C没有平方函数,但是可以用n * n来表示n的平方。

#include <stdio.h>
int main(void) /* finds sum of first n integers */
{
    int count, sum;
    int n;
    printf("Enter the upper limit: ");
    scanf("%d", &n);
    count = 0;
    sum = 0;
    while (count++ < n)
    sum = sum + count * count;
    printf("sum = %d\n", sum);
    return 0;
}

7.求一个double类型数的立方值

编写一个程序,提示用户输入一个double类型的数,并打印该数的立方值。自己设计一个函数计算并打印立方值。main()函数要把用户输入的值传递给该函数。

#include <stdio.h>
void showCube(double x);
int main(void) /* finds cube of entered number */
{
    double val;
    printf("Enter a floating-point value: ");
    scanf("%lf", &val);
    showCube(val);
    return 0;
}
void showCube(double x)
{
    printf("The cube of %e is %e.\n", x, x*x*x );
}

8.求一个数的模

编写一个程序,显示求模运算的结果。把用户输入的第1个整数作为求模运算符的第2个运算对象,该数在运算过程中保持不变。用户后面输入的数是第1个运算对象。当用户输入一个非正值时,程序结束。其输出示例如下:

This program computes moduli.
Enter an integer to serve as the second operand: 256
Now enter the first operand: 438
438 % 256 is 182
Enter next number for first operand (<= 0 to quit): 1234567
1234567 % 256 is 135
Enter next number for first operand (<= 0 to quit): 0
Done
#include <stdio.h>

void cal_mode(int op1, int op2);

int main(void)
{
    printf("This program computes moduli.\n");
    int op1, op2;
    printf("Enter an integer to serve as the second operand: ");
    scanf("%d", &op2);
    //printf("op2 = %d\n", op2);
    printf("Now enter the first operand: ");
    scanf("%d", &op1);
    //printf("op1 = %d\n", op1);
    while(op1 > 0 && op2 > 0)
    {
        cal_mode(op1, op2);
        printf("Now enter the first operand(<=0 to quit): ");
        scanf("%d", &op1);
        //printf("op1 = %d\n", op1);
    }
    printf("bye.\n");
}


void cal_mode(int op1, int op2)
{
    printf("%d %% %d is %d\n", op1, op2, op1 % op2);    
}

9.华氏温度,摄氏温度和开氏温度直接的转换

编写一个程序,要求用户输入一个华氏温度。程序应读取double类型的值作为温度值,并把该值作为参数传递给一个用户自定义的函数Temperatures()。该函数计算摄氏温度和开氏温度,并以小数点后面两位数字的精度显示3种温度。要使用不同的温标来表示这3个温度值。下面是华氏温度转摄氏温度的公式:
摄氏温度 = 5.0 / 9.0 * (华氏温度 - 32.0)
开氏温标常用于科学研究,0表示绝对零,代表最低的温度。下面是摄氏温度转开氏温度的公式:
开氏温度 = 摄氏温度 + 273.16
Temperatures()函数中用const创建温度转换中使用的变量。在main()函数中使用一个循环让用户重复输入温度,当用户输入 q 或其他非数字时,循环结束。scanf()函数返回读取数据的数量,所以如果读取数字则返回1,如果读取q则不返回1。可以使用==运算符将scanf()的返回值和1作比较,测试两值是否相等。

#include <stdio.h>
void temperatures(double temp);

int main(void)
{
    double temp_hua;
    printf("请输入一个华氏温度:");
    while(scanf("%lf", &temp_hua) == 1)
    {
        printf("%lf\n", temp_hua);
        temperatures(temp_hua);
        printf("请输入一个华氏温度(输入q退出):");
    }

    return 0;
}

void temperatures(double temp_hua)
{
    double temp_she, temp_kai;
    const float  she1 = 5.0, she2 = 9.0, she3 = 32.0, kai = 273.16;

    temp_she = she1 / she2 * (temp_hua - she3);
    temp_kai = temp_hua  + kai;

    printf("华氏温度为:%.2lf\n", temp_hua);
    printf("摄氏温度为:%.2lf\n", temp_she);
    printf("开式温度为:%.2lf\n", temp_kai);

}

总结

1.C语言中,所有非0的都视为真,只有0被视为假。

2.每一个表达式都是一个值。

一些表达式及其值
表达式
-4 + 6 2
c = 3 + 8 11
5 > 3 1
6 + (c = 3 + 8) 17

3.语句(statement)是C程序的基本构建块,一条语句相当于一条完整的计算机指令。

C的基本程序步骤由语句组成,而大多数语句都由表达式构成。在C中,大部分语句都以分号结尾. C Primer Plus 第五章复习题 - 图1

4.副作用和序列点

我们再讨论一个C语言的术语副作用(side effect)。副作用是对数据对象或文件的修改。例如,语句:
states = 50;
它的副作用是将变量的值设置为50。副作用?这似乎更像是主要目的!但是从C语言的角度看,主要目的是对表达式求值。给出表达式4 + 6,C会对其求值得10;给出表达式states = 50,C会对其求值得50。对该表达式求值的副作用是把变量states的值改为50。跟赋值运算符一样,递增和递减运算符也有副作用,使用它们的主要目的就是使用其副作用。
类似地,调用 printf()函数时,它显示的信息其实是副作用(printf()的返回值是待显示字符的个数)。
序列点(sequence point)是程序执行的点,在该点上,所有的副作用都在进入下一步之前发生。在 C语言中,语句中的分号标记了一个序列点。意思是,在一个语句中,赋值运算符、递增运算符和递减运算符对运算对象做的改变必须在程序执行下一条语句之前完成。另外,任何一个完整表达式的结束也是一个序列点。
什么是完整表达式?所谓完整表达式(full expression),就是指这个表达式不是另一个更大表达式的子表达式。例如,表达式语句中的表达式和while循环中的作为测试条件的表达式,都是完整表达式。
序列点有助于分析后缀递增何时发生。例如,考虑下面的代码:
while (guests++ < 10)
printf("%d \n", guests);
对于该例,C语言的初学者认为“先使用值,再递增它”的意思是,在printf()语句中先使用guests,再递增它。但是,表达式guests++ < 10是一个完整的表达式,因为它是while循环的测试条件,所以该表达式的结束就是一个序列点。因此,C 保证了在程序转至执行 printf()之前发生副作用(即,递增guests)。同时,使用后缀形式保证了guests在完成与10的比较后才进行递增。
现在,考虑下面这条语句:
y = (4 + x++) + (6 + x++);
表达式4 + x++不是一个完整的表达式,所以C无法保证x在子表达式4 +x++求值后立即递增x。这里,完整表达式是整个赋值表达式语句,分号标记了序列点。所以,C 保证程序在执行下一条语句之前递增x两次。C并未指明是在对子表达式求值以后递增x,还是对所有表达式求值后再递增x。因此,要尽量避免编写类似的语句。

5.类型转换原则

①当类型转换出现在表达式时,无论是unsigned还是signed的char和short都会被自动转换成int,如有必要会被转换成unsigned int(如果short与int的大小相同,unsigned short就比int大。这种情况下,unsigned short会被转换成unsigned int)。由于都是从较小类型转换为较大类型,所以这些转换被称为升级(promotion)。
②涉及两种类型的运算,两个值会被分别转换成两种类型的更高级别。
③类型的级别从高至低依次是long double、double、float、unsignedlonglong、long long、unsigned long、long、unsigned int、int。例外的情况是,当long 和 int 的大小相同时,unsigned int比long的级别高。之所以short和char类型没有列出,是因为它们已经被升级到int或unsigned int。
④在赋值表达式语句中,计算的最终结果会被转换成被赋值变量的类型。这个过程可能导致类型升级或降级(demotion)。所谓降级,是指把一种类型转换成更低级别的类型。
⑤当作为函数参数传递时,char和short被转换成int,float被转换成double。第9章将介绍,函数原型会覆盖自动升级。
类型升级通常都不会有什么问题,但是类型降级会导致真正的麻烦。原因很简单:较低类型可能放不下整个数字。