第五章 运算符、表达式和语句

GitHub地址,欢迎 star

5.1 循环简介

我们在这里简单介绍一种——while循环,它能让你对运算符做更有趣地探索。

#include <stdio.h>
#define ADJUST 7.31 // 字符常量
int main(void) {
    const double SCALE = 0.333; // const 变量
    double shoe, foot;
    
    printf("Shoe size (men's)  foot length\n");
    shoe = 3.0;
    while (shoe < 18.5) /* while 循环开始 */
    { /* 块开始 */
        foot =  SCALE * shoe + ADJUST;
        printf("%10.1f %15.2f inches\n", shoe, foot);
        shoe = shoe + 1.0;
    } /* 块结束 */
    printf("If the shoe fits, wear it.\n");
    return 0;
}

5.2 基本运算符

C用运算符(operator)表示算术运算。基本运算符:=、+、-、*、/(C没有指数运算符。不过,C的标准数学库有。)。

5.2.1 赋值运算符:=

在C语言中,=并不意味着 “相等”,而是一个赋值运算符。下面的赋值表达式语句:bmw = 2002; 。把值 2002 赋给变量 bmw 。
在C语言中,类似这样的语句没有意义(实际上是无效的):2002 = bmw; 因为在这种情况下,2002 被称为右值(rvale),智能是字面常量。不能给常量赋值,常量本身就是它的值。因此,在编写代码时要记住,= 号左侧的项必须是一个变量名。实际上,赋值运算符左侧必须引用一个存储位置。最简单的方法就是使用变量名。概括地说,C 使用可修改的左值(modifiable lvalue) 标记那些可赋值的实体。

赋值表达式语句的目的是把值储存到内存位置上。用于储存值得数据存储区域统称为 数据对象(data object)左值(lvalue) 是C语言的术语,用于标识特定数据对象的名称或表达式。因此,对象指的是实际的数据存储而左值是用于标识或定位存储位置的标签。
出现 const 限定符后,C标准新增了一个术语:可修改的左值(modifiable lvalue) ,用于标识可修改的对象。所以,赋值运算符的左侧应该是可修改的左值。当前标准建议,使用术语对象定位值(object locator value) 更好。
右值(rvalue) 指的是能赋值给可修改左值得量,且本身不是左值。
bmw = 2002; 在当前标准在描述这一概念时,使用的是 表达式的值(value of an expression),而不是右值。
在学习名称时,被称为 “项” 的就是 运算对象(operand)

5.2.2 加法运算符:+

加法运算符(addition operator) 用于加法运算,使其两侧的值相加。

5.2.3 减法运算符:-

减法运算符(subtraction operator) 用于减法运算,使其左侧的数减去右侧的数。

5.2.4 符号运算符:-和+

减号还可用于标明或改变一个值得代数符号。如:rocky = -12;。以这种方式使用的负号被称为一元运算符(unary operator) 。一元运算符只需要一个运算对象。

5.2.5 乘法运算符:*

符号 * 表示乘法。

5.2.6 除法运算符:/

符号 / 表示除法。/ 左侧的值是被除数,右侧的值是除数。整数除法和浮点数除法不同。浮点数除法的结果是浮点数,而整数除法的结果是整数。整数是没有小数部分的数。在C语言中,整数除法结果的小数部分被丢弃,这一过程被称为 截断(truncation)

5.2.7 运算符优先级

C语言中运算符的优先级与数学的算术优先级相同。

5.2.8 优先级和求值顺序

运算符优先级为表达式中的求值顺序提供重要的依据,但是并没有规定所有的顺序。

5.3 其他运算符

C语言中大约有40个运算符,有些运算符比其他运算符常用得多。

5.3.1 sizeof 运算符和 size_t 类型

sizeof 运算符以字节为单位返回运算对象的大小。运算对象可以是具体的数据对象或类型。C语言中规定,sizeof 返回 size_t 类型的值。这是一个无符号整数类型,但它不是新类型。前面介绍过,size_t 是语言定义的标准类型。C有一个 typedef 机制,允许程序员为现有类型创建别名。例如:typedef double real; 这样,real 就是 double 的别名。现在,可以声明一个 real 类型的变量。

编译器查看 real 时会发现,在typedef 声明中 real 已经成为 double 的别名,于是创建 real 类型的变量时,会替换为 double 类型。类似地,C头文件系统可以使用 typedef 把 size_t 作为 unsigned int 或 unsigned long 的别名。这样,在使用 size_t 类型时,编译器会根据不同的系统替换标准类型。

C99 做了进一步调整,新增了 %zd 转换说明用于 printf() 显示 size_t 类型的值。如果系统不支持 %zd,可使用 %u 或 %lu 代替 %zd。

5.3.2 求模运算符:%

求模运算符(modulus operator) 用于整数运算。求模运算符给出其左侧整数除以右侧整数的 余数(remainder)

负数求模如何进行?C99规定 “趋零截断” 之前,该问题的处理方法很多。但自从有了这条规则之后,如果第1个运算对象是负数,那么求模的结果为负数;如果第1个运算对象是正数,那么求模的结果也是正数:
11 / 5 得 2,11 % 5 得 1
11 / -5 得 -2,11 % -5 得 1
-11 / -5 得 2,-11 % -5 得 -1
-11 / 5 得 -2,-11 % 5 得 -1
如果当前系统不支持 C99 标准,会显示不同的结果。实际上,标准规定:无论何种情况,只要 a 和 b 都是整数值,便可通过 a - ( a / b ) * b 来计算 a % b。

5.3.3 递增运算符:++

递增运算符(increment operator) 执行简单的任务,将其运算对象递增1。该运算符以两种方式出现:
第1种,++ 出现在其作用的变量前面,这是 前缀模式
第2种,** 出现在其作用的变量后面,这是 后缀模式
两种模式的区别在于递增行为发生的时间不同。前缀模式在执行运算前对变量进行加 1,后缀模式是在执行完运算后对变量进行加 1。

5.3.4 递减运算符:–

每种形式的递增运算符都有一个 递减运算符(decrement operator) 与之对应,用 – 代替 ++ 即可。

5.3.5 优先级

递增运算符和递减运算符都有很高的结合优先级,只有圆括号的优先级比它们高。因此,x * y++ 表示的是 (x) * (y++),而不是(x + y)++。不过后者无效,因为递增和递减运算符只能影响一个变量。

5.3.6 不要自作聪明

1、如果一个变量出现在一个函数的多个参数中,不要对该变量使用递增或递减运算符;

2、如果一个变量多次出现在一个表达式中,不要对该变量使用递增或递减运算符。

另一方面,对于何时执行递增,C还是做了一些保证。

5.4 表达式和语句

在前几章中,我们已经多次使用了术语 表达式(expression)语句(statement)

5.4.1 表达式

表达式(expression) 由运算符和运算对象组成。
子表达式(subexpression) 即较小的表达式。

C表达式的一个最重要的特性是,每个表达式都有一个值。要获得这个值,必须根据运算符优先级规定的顺序来执行操作。

5.4.2 语句

语句(statement) 是C程序的基本构建块。一条语句相当于一条完整的计算机指令。在C中,大部分语句都以分好结尾。

副作用(side effect) 是对数据对象或文件的修改。

序列点(sequence point) 是程序执行的点,在该点上,所有的副作用都在进入下一步之前发生。在C语言中,语句中的分号标记了一个序列点。意思是,在一个语句中,复制运算符、递增运算符和递减运算符对运算对象做的改变必须在程序执行下一条语句之前完成。

完整表达式(full expression) 指这个表达式不是另一个更大表达式的子表达式。

5.4.3 复合语句(块)

复合语句(compound statement) 是用花括号括起来的一条或多条语句,复合语句也称为 块(block).

提示 风格提示

注意循环体中的缩进,缩进对编译器不起作用,编译器通过花括号和 while 循环的结构来识别和解释指令。

总结 表达式和语句
表达式:

表达式由运算符和运算对象组成。最简单的表达式是不带运算符的一个常量或变量(如,22 或 beebop),更负载的例子是 55 + 22vap = 2 * (vip + (vup = 4))

语句:

到目前为止,接触到的语句可分为简单语句和复合语句。简单语句以一个分号结尾。如:
赋值表达式语句:toes = 12;
函数表达式语句:printf("%d\n",toes);
空语句:; /* 什么也不做 */
复合语句(或 块)由花括号括起来的一条或多条语句组成。如下:

while (years < 100)
{
    wisdom = wisdom * 1.05;
    printf("%d %d\n", years, wisdom);
    years = years + 1;
}

5.5 类型转换

1、当类型转换出现在表达式时,无论是 unsigned 还是 signed 的 char 和 short 都会被自动转换成 int,如有必要会被转换成 unsigned int(如果 short 与 int 的大小相同,unsigned short 就比 int 大。这种情况下,unsigned short 会被转换成 unsigned int)。由于都是从较小类型转换为较大类型,所以这些转换被称为 升级(promotion)

2、涉及两种类型的运算,两个值会被分别转换成两种类型的更高级别。

3、类型的级别从高至低依次是 long double、double、float、unsignedlong long、long long、unsigned long、long、unsigned int、int。例外情况是,当 long 和 int 的大小相同时,unsigned int 比 long 的级别高。之所以 short 和 char 类型没有列出,是因为它们已经被升级到 int 和 unsigned int。

4、在赋值表达式语句中,计算的最终结果会被转换成赋值变量的类型。这个过程可能导致类型升级或降级(demotion)。所谓降级,是指把一种类型转换成更低级别的类型。

5、当作为函数参数传递时,char 和 short 被转换成 int,float 被转换成 double。类型升级通常都不会有什么问题,但是类型降级会导致真正的麻烦。原因很简单:较低类型可能放不下整个数字。

5.5.1 强制类型转换运算符

通常,应该避免自动类型转换,尤其是类型降级。但是如果能小心使用,类型转换也很方便。强制类型转换(cast) 在某个量的前面放置用圆括号括起来的类型名,该类型名即是希望转换成的目标类型。圆括号和它括起来的类型名构成了 **强制类型转换运算符(cast operator)。

总结 C的一些运算符

下面是我们学过的一些运算符。

赋值运算符:
= 将其右侧的值赋给左侧的变量
算术运算符:
+ 将其左侧的值与右侧的值相加
- 将其左侧的值减去右侧的值
- 作为一元运算符,改变其右侧值的符号
* 将其左侧的值乘以右侧的值
/ 将其左侧的值除以右侧的值,如果两束都是整数,计算结果将被截断
% 当其左侧的值除以右侧的值时,取其余数(智能应用于整数)
++ 对其右侧的值加 1 (前缀模式),或对其左侧的值加 1(后缀模式)
-- 对其右侧的值减 1(前缀模式),或对其左侧的值减 1 (后缀模式)
其他运算符:
sizeof 获得其右侧运算对象的大小(以字节为单位),运算对象可以是一个呗圆括号括起来的类型说明符,如 sizeof(float),或者是一个具体的变量名、数组名等,如 sizeof foo
(类型名) 强制类型转换运算符将其右侧的值转换成圆括号中指定的类型,如 (float)9 把整数 9 转换成浮点数 9.0

5.6 带参数的函数

示例程序:

/* 定义一个带一个参数的函数 */
#include <stdio.h>
void pound(int n); // ANSI 函数原型声明
int main(void)
{
    int times = 5;
    char ch = '!'; // ASCII 码是33
    float f = 6.0f;

    pound(times); // int类型的参数
    pound(ch); // 和pound((int)ch);相同
    pound(f); // 和pound((int)f);相同
    return 0;
}

void pound(int n) // ANSI风格函数头
{                 // 表明函数接受一个int类型的参数
    while(n-- > 0)
    {
        printf("#");
    }
    printf("\n");
}

首先,看程序的函数头:
void pound(int n);
如果函数不接受任何参数,函数头的圆括号中应该写上关键字void。声明参数就创建了被称为 形式参数(formal argument 或 formal parameter,简称形参) 的变量。我们称函数调用传递的值为 实际参数(actual argument 或 actual parameter,简称实参)

注意 实参和形参

在英文中,argument 和 parameter 经常可以互换使用,但是C99标准规定了:对于 actual argument 或 actual argument 使用术语 argument(译为实参);对于 formal argument 或 formal parameter 使用术语 parameter(译为形参)。为遵循这一规定,我们可以说形参是变量,实参是函数调用提供的值,实参被赋给相应的形参。因此,在程序中,times 是 pound() 的实参,n 是 pound() 的形参。类似地,在函数调用 pound(times + 4) 中,表达式 times + 4 的值是该函数的实参。

变量名时函数私有的,即在函数中定义的函数名不会和别处的相同名称发生冲突。原型(prototype) 即是函数的声明,描述了函数的返回值和参数。
pound() 函数的原型说明了两点:

  • 该函数没有返回值(函数名前面有 void 关键字);
  • 该函数有一个 int 类型的参数。

5.7 示例程序

// A useful program for runners
#include <stdio.h>
const int S_PER_M = 60; // 1分钟的秒数
const int S_PER_H = 3600; // 1小时的秒数
const double M_PER_K = 0.62137; // 1公里的英里数
int main(void)
{
    double distk, distm; // 跑过的距离(分别以公里和英里为单位)
    double rate; // 平均速度(以英里/小时为单位)
    int min, sec; // 跑步用时(以分钟和秒为单位)
    int time; // 跑步用时(以秒为单位)
    double mtime; // 跑1英里需要的时间,以秒为单位
    int mmin, msec; // 跑1英里需要的时间,以分钟和秒为单位

    printf("This program converts your time for a metric race\n");
    printf("to a time for running a mile and to your average\n");
    printf("speed in miles per hour.\n");
    printf("Please enter, in kilometers, the distance run.\n");
    scanf("%lf", &distk); // %lf 表示读取一个 double 类型的值
    printf("Next enter the time in minutes and seconds.\n");
    printf("Begin by entering the minutes.\n");
    scanf("%d", &min);
    printf("Now enter the seconds.\n");
    scanf("%d", &sec);

    time = S_PER_M * min + sec; // 把时间转换成秒
    distm = M_PER_K * distk; // 把公里转换成英里
    rate = distm / time * S_PER_H; // 英里/秒 X 秒/小时 = 英里/小时
    mtime = (double)time / distm; // 时间/距离 = 跑1英里所用的时间
    mmin = (int)mtime / S_PER_M; // 求出分钟数
    msec = (int)mtime % S_PER_M; // 求生剩余的秒数

    printf("You ran %1.2f km (%1.2f miles) in %d min, %d sec.\n", distk, distm, min, sec);
    printf("That pace corresponds to running a mile in %d min, ", mmin);
    printf("%d sec.\nYour average speed was %1.2f mph.\n", msec, rate);

    return 0;
}

5.8 关键概念

C通过运算符提供多种操作。每个运算符的特性包括运算对象的数量、优先级和结合律。当两个运算符共享一个运算对象时,优先级和结合律决定了先进行哪项运算。每个C表达式都有一个值。如果不了解运算符的优先级和结合律,写出的表达式可能不合法或者表达式的值与预期不符。这会影响你成为一名优秀的程序员。

虽然C允许编写混合数值类型的表达式,但是算术运算要求对象都是相同的类型。因此,C会进行自动类型转换。尽管如此,不要养成依赖自动类型转换的习惯,应该显式选择合适的类型或使用强制类型转换。这样,就不用担心出现不必要的自动类型转换。

5.9 本章小结

C语言有许多运算符,如本章讨论的赋值运算符和算术运算符。一般而言,运算符需要一个或多个运算对象才能完成运算生产一个值。只需要一个运算对象的运算符(如符号和 sizeof )称为一元运算符,需要两个运算对象的运算符(如加法运算和乘法运算)称为二元运算符。

表达式 由运算符和运算对象组成。在C语言中,每个表达式都有一个值,包括赋值表达式和比较表达式。运算符优先级规则决定了表达式中各项的求职顺序。当两个运算符共享一个运算对象时,先进行优先级高的运算。如果运算符的优先级相等,有结合律(从左往右或从右往左)界定求值顺序。

大部分 语句 都以分好结尾。最常用的语句是 表达式语句 。用花括号括起来的一条或多条语句构成了 复合语句(或称为块) 。while 语句是一种迭代语句,只要测试条件为真,就重复执行循环体中的语句。

在C语言中,许多类型转换都是自动进行的。当 char 和 short 类型出现在表达式里或作为函数的参数(函数原型除外)时,都会被升级为 int 类型;float 类型在函数参数中时,会被升级为 double 类型。在 K&R C(不是 ANSI C)下,表达式中的 float 也会被升级为 double 类型。当把一种类型的值赋给另一种类型的变量时,值将被转换成与变量的类型相同。当把较大类型转换成较小类型时(如,long 转换成 short,或 double 转换成 float ),可能会丢失数据。根据本章介绍的规则,在混合类型的运算中,较小类型会被转换成较大类型。

定义带一个参数的函数时,便在函数定义中声明了一个变量,或称为 形式参数。然后,在函数调用中传入的值会被赋给这个变量。这样,在函数中就可以使用该值了。

5.10 复习题

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、x = 30;b、x = 27;c、y = x = 1;d、y = 9,x = 3;

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、x = 6;b、x = 52;c、x = 0;d、x = 13;

3、对下列各表达式求值:
a、30.0 / 40.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;b、1.5;c、37;d、37;e、37.5;f、35.0;

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

/** 应增加 #include <stdio.h>*/
int main(void)
{
    int i = 1, /** 末尾应该是分号 */
    float n;
    printf("Watch out! Here come a bunch of fractions!\n");
    while(i < 30) /** 当前为死循环,猜测应该是 while(i++ < 30) */ /** 不写花括号只能包含一行代码 */
        n = 1 / i; /** 因为 1 和 i 都是整数,所以当 i 为 1 时,除法的结果是 1;当 i 为更大的数时,除法结果为 0。用 n = 1.0 / i,i 在除法运算之前会被转换为浮点数,这样就能得到非零值。*/
        printf(" %f", n);  /** 没有换行符,所有数将被打印到一行中 */
    printf("That's all, folks!\n");
    return; /** 应该是return 0;
}

5、这是一个程序,从表面上看,该程序只使用了一条 scanf() 语句,请找出程序的不足之处。

#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 。这样就可通过第一次测试。不过,还有另一个问题。当最后输入 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 TEM 10
int main(void)
{
    int n = 0;

    while(n++ < TEM)
    {
        printf("%5d", n);
    }
    printf("\n");
    return 0;
}

答案:把 1 ~ 10 打印在一行,每个数字占5列宽度,然后开始新一行。

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

#include <stdio.h>
#define TEM 'g'
int main(void)
{
    char n = 96;

    while(n++ < TEM)
    {
        printf("%5c", n);
    }
    printf("\n");
    return 0;
}

10、假设嫌慢是完整程序中的一部分,它们分别打印什么?
a、

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

b、

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

c、

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

答案:
a、1 2(每个数字占4个字符宽度)
b、101
102
103
104
此题注意第二个语句不在循环内。
c、stuvw

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

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

答案:由于 n++ 没有包裹在循环里,所以会无限循环打印 COMPUTER BYTES DOG。

12、分别编写一条语句,完成下列各任务:
a、将变量 x 的值增加 10
b、将变量 x 的值增加 1
c、将 a 与 b 之和的两倍赋给 c
d、将 a 与 b 的两倍之和赋给 c
答案:a、x = x + 10;b、x++ 或 ++x 或 x = x + 1;c、c = (a + b) * 2;d、c = a * 2 + b * 2;

13、分别编写一条语句,完成下列各任务:
a、将变量 x 的值减少 1
b、将 n 除以 k 的余数赋给 m
c、q 除以 b 减去 a,并将结果赋给 p
d、a 与 b 之和除以 c 与 d 的乘积,并将结果赋给 x
答案:a、x-- 或 --x 或 x = x - 1;b、m = n % k;c、p = q / b - a;d、x = (a + b) / (c * d);

5.11 编程练习

1、编写一个程序,把用分钟表示的时间转换成用小时和分钟表示的时间。使用 #define 或 const 创建一个表示 60 的符号常量或 const 变量。通过 while 循环让用户重复输入值,直到用户输入小于或等于 0 的值才停止循环。
答案:

#include <stdio.h>
#define H_TO_M 60
int main(void)
{
    int hour;
    printf("Please input hour!\n");
    scanf("%d", &hour);
    while(hour > 0)
    {
        printf("Hour is %d, Minutes are %d.\n", hour, hour * H_TO_M);
        printf("Please input hour(<= 0 to quit)!\n");
        scanf("%d", &hour);
    }
    printf("bye!\n");
    return 0;
}

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

#include <stdio.h>
#define M 11
int main(void)
{
    int num,max;
    printf("Please input a number!\n");
    scanf("%d", &num);
    max = num + M;
    while(num < max)
    {
        printf("%4d", num++);
    }
    return 0;
}

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

#include <stdio.h>
#define WEEK_TO_DAYS 7
int main(void)
{
    int days;
    int c,j;
    printf("Please input days!\n");
    scanf("%d", &days);
    while(days > 0)
    {
        c = days / WEEK_TO_DAYS;
        j = days % WEEK_TO_DAYS;
        printf("%d days are %d weeks, %d days.\n", days, c, j);
        printf("Please input days(<= 0 to quit)!\n");
        scanf("%d", &days);
    }
    printf("bye!\n");
    return 0;
}

4、编写一个程序,提示用户输入一个身高(单位:厘米),并分别以厘米和英寸为单位显示该值,运行有小数部分。程序应该能让用户重复输入身高,直到用户输入一个非正值。其输出示例如下:
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 FEET_TO_CM 30.48
#define INCH_TO_CM 2.45
int main(void)
{
    int height, s;
    float i, yu, inche;
    printf("Enter a height in centimeters: ");
    scanf("%f", &i);
    while(i > 0)
    {
        height = i;
        s = height / FEET_TO_CM;
        yu = height - s * FEET_TO_CM;
        inche = yu / INCH_TO_CM;
        printf("%0.1f cm = %d feet, %0.1f inches.\n", (float)height, s, inche);
        printf("Enter a height in centimeters( <= 0 to quit):");
        scanf("%f", &i);
    }
    printf("bye!\n");
    return 0;
}

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

#include <stdio.h>
int main(void)
{
    int count, sum;

    count = 0;
    sum = 0;
    while(count++ < 20)
        sum = sum + count;
    printf("sum = %d\n", sum);
    return 0;
}

答案:

#include <stdio.h>
int main(void)
{
    int count = 0, sum = 0, sumCount;

    printf("Please input days!\n");
    scanf("%d", &sumCount);
    while(count++ < sumCount)
        sum = sum + count;
    printf("sum money = $ %d\n", sum);
    return 0;
}

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

#include <stdio.h>
int main(void)
{
    int count = 0, sum = 0, sumCount;

    printf("Please input days!\n");
    scanf("%d", &sumCount);
    while(count++ < sumCount)
        sum = sum + count * count;
    printf("sum money = $ %d\n", sum);
    return 0;
}

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

#include <stdio.h>
double msquare(double i);
int main(void)
{
    double count;

    printf("Please input number!\n");
    scanf("%lf", &count);
    printf("The number square is %f\n", msquare(count));
    return 0;
}

double msquare(double i)
{
    return i * i * i;
}

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>
int main(void)
{
    int fc, sc;

    printf("This program computes moduli.\n");
    printf("Enter an integer to serve as the second operand: ");
    scanf("%d", &fc);
    printf("Now enter the first operand:");
    scanf("%d", &sc);
    while(sc > 0)
    {
        printf("%d %% %d is %d\n", sc, fc, sc % fc);
        printf("Enter next number for first operand(<= 0 to quit): ");
        scanf("%d", &sc);
    }
    printf("Done\n");
    return 0;
}

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 ct);
int main(void)
{
    double ct;
    printf("Please input Centigrade Temperature: ");
    while(scanf("%lf", &ct) == 1)
    {
        Temperatures(ct);
        printf("Please input Centigrade Temperature(q or not number to quit): ");
    }
    return 0;
}

void Temperatures(double ct)
{
    const double CT_TO_KT = 273.16;
    const double CT_TO_FT_1 = 5.0;
    const double CT_TO_FT_2 = 9.0;
    const double CT_TO_FT_3 = 32.0;
    double ft, kt;
    ft = ct * CT_TO_FT_2 / CT_TO_FT_1 + CT_TO_FT_3;
    kt = ct + CT_TO_KT;
    printf("Centigrade Temperature:%f,Fahrenheit Thermometer:%f,Kelvin Temperature:%f\n", ct, ft, kt);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值