《C语言程序设计现代方法》note-2 格式化输入/输出 运算符和表达式

助记提要

  1. 转换说明的格式;
  2. scanf处理输入的过程;
  3. 除法操作的注意事项;
  4. 运算符的结合性;
  5. 不在子表达式中输入操作数的原因;
  6. 表达式允许用作语句的问题;

3章 格式化输入/输出

3.1 printf函数

printf函数用来显示格式串的内容,并在该串中指定位置插入要显示的值。

printf(格式串, 表达式1, 表达式2, ...);

表达式表示用来插到指定位置的值,也可以是常量、变量。
格式串包含普通字符和转换说明。普通字符会按照它们在字符串中的样子显示出来。
转换说明以字符%开头,表示待填充的值的占位符。跟随在%后的信息指定了把数值从二进制形式转换为打印格式的方法。

转换说明的数量应该和表达式的数量匹配。表达式不够时,多余的转换说明会显示无意义的数值;表达式过多时,多出的项的值不会显示。

转换说明

转换说明的格式为:%m.pX
m表示显示数值的区域的最小宽度。可以省去。表示小数时,m指定的宽度包括小数点在内。

X是转换指定符,表示显示时需要对数值做什么转换。显示数值,常用的转换指定符有:

  1. d,转换为十进制整数;
  2. e,转换为指数形式的浮点数;
  3. f,转换为定点十进制形式的浮点数;
  4. g,根据浮点数的大小,转换为指数形式或定点十进制形式的浮点数;

p表示精度,转换指定符不同,含义也不同。对于d是表示待显示数值的最少个数,默认为1;对于ef,表示小数点后应该出现的数字个数,默认为6;对于g,表示可以显示的有效数字的最大数量。
p也可以不写,不写的时候需要把小数点去掉。

printf("|%d|\n", 5);
// 宽度10(默认右对齐)
printf("|%10d|\n", 5);
// 宽度10,左对齐
printf("|%-10d|\n", 5);
// 宽度10,至少显示三位数字,不足的用0补齐
printf("|%10.3d|\n", 5);
// 宽度10,小数部分3位数字
printf("|%10.3f|\n", 12.3f);
printf("|%10.3e|\n", 12.3f);
// 不同的p对g转换显示的影响
printf("|%10.2g|\n", 12.3f);
printf("|%10.3g|\n", 12.3f);
printf("|%10.4g|\n", 12.3f);
|5|
|         5|
|5         |
|       005|
|    12.300|
|1.230e+001|
|        12|
|      12.3|
|      12.3|
转义序列

转义序列可以使字符串中包含一些特殊字符,包括非打印字符和对编译器有特殊意义的字符。

这些转义序列出现在格式串中时,表示在显示时执行的操作。
\a,警报符,产生鸣响;
\b,回退符;
\n,换行符,光标跳到下一行位置;
\t,制表符,光标移动到下一个制表符位置;

使用两个连续的字符%,可以在输出结果中显示%符。

3.2 scanf函数

scanf函数根据特定的格式读取输入,转换后赋值给对应的变量。

scanf(格式串, &变量1, &变量2, ...);

scanf的格式串也可以包括普通字符和转换说明两部分。有时候格式串可以只包含转换说明符。

符号&需要常常放在被赋值的每个变量前面。忽略&符号会出现不可预知的结果,导致程序崩溃。

处理转换说明符

scanf从左往右开始处理格式串中的信息。
对于每一个转换说明,scanf从输入数据中定位适当类型的项,并在必要时跳过空白字符(空格、换行符、制表符)。
读取数据项时,遇到不可能属于此项的字符时才会停止。这个不属于数据项的字符会被放回原处,扫描下一次输入项或下一次调用scanf函数时再次读入。
如果数据项读入成功,就继续处理格式串的剩余部分;如果某一项读取失败,就立即返回不继续读取。

处理普通字符

scanf处理普通字符时,需要看这个普通字符是不是空白字符。
格式串中有一个或多个连续的空白字符,scanf会从输入中读取空白字符,直到遇到第一个非空字符为止。格式串中的一个空白字符可以与输入中任意数量(包括0个)的空白字符匹配。
格式串中的非空字符,如果与scanf收到的下一个字符匹配,就继续往后处理格式串;如果不匹配,scanf就把不匹配的字符放回,然后退出,不再继续读取。下一个scnaf函数会这个从不匹配的字符开始读取。

注意 scanf的格式串末尾如果写了空白字符(尤其是\n),可能会导致交互式程序一直挂起,直到用户输入一个非空白字符为止。因为scanf无法以换行和空格确认格式串处理结束。

4章 表达式

表达式是表示如何计算值的公式。

4.1 算术运算符

一元运算符:正号+,负号-
二元运算符:加法+,减法-,乘法*,除法/,取余%

一元运算符只需要一个操作数,二元运算符需要两个操作数。

注意
  • %要求两个操作数都是整数。
  • 其他二元运算符的操作数既可以是整数也可以是浮点数。混合使用时,结果是float类型。
  • /%的右操作数不能是0。
  • /的两个操作数都是整数时,它会截取整数部分作为结果。
  • /%用于负操作数时,结果难以确定。
    如果有一个操作数是负数,C89标准中,/的结果可以向上取整也可以向下取整;C99中除法的结果总是趋0截断的。
    C89和C99都要确保(a / b) \* b + a % b的结果总是等于a。
  • %的结果的负号与左操作数相同。

由实现定义的行为 C标准故意对C语言的某些部分未加指定,并认为其细节可以由具体实现来定。即由程序在特定平台上编译、链接、执行时用到的软件来定。
写程序的时候,要避免依赖由实现定义的行为。

运算符的优先级和结合性

算数运算符的优先级(1最高):

  1. 正号+,负号-
  2. */%
  3. 加号+、减号-
    不同运算符在计算时有不同的优先级。
    多个运算符出现在同一个表达式时,按运算符的优先级从高到低计算。

相同优先级的运算符,按运算符的结合性确定计算顺序。
从左向右结合的运算符,称为左结合的运算符(二元运算符加、减、乘、除、取余);从右向左结合的运算符,称为右结合的运算符(一元运算符正、负)。

C语言运算符很多,记不住优先级和结合性规则时,可以使用圆括号对表达式进行分组。

4.2 赋值运算符

赋值操作语法:v = e
表示求出e的值,然后复制给v。
如果v和e的类型不同,赋值运算时会把e的值转为v的类型。

整个赋值表达式的值是赋值运算后左操作数的值。
赋值是右结合的运算符。它的左操作数必须是对象,而不是常量或计算的结果。

  • 运算符的副作用
    通常运算符不应该修改他们的操作数,表达式也只是计算他们的结果,不会改变操作数的值。
    有些运算符不仅计算出值,还会有其他副作用。赋值运算符的副作用就是会改变左操作数的值。
复合赋值

利用变量的原有值计算新值然后重新赋值给这个变量,这种操作非常普遍。C语言允许用复合赋值运算符缩短类似的语句。

// 原值计算新值,然后重新赋值
v = v + e
// 复合赋值
v += e

前面提到的二元运算符的计算都有对应的复合赋值运算符:+=-=*=/=%=
复合赋值运算符也是右结合的。

注意 v += e有时候不等同于v = v + e
一是优先级导致的问题:表达式i *= j + k和表达式i = i * j + k不一样。
二是如果v是表达式,且其中发生了对变量的更改时,v += e中更改只会发生一次,但是v = v + e中会更改两次。

自增、自减运算符

变量自增1、自减1的运算也很常见。C语言用++--两种运算符简化这些语句。

自增++、自减--运算符既可以做为前缀运算符,也可以作为后缀运算符。

做为前缀运算符时,其表达式的值为操作数自增后的值。
做为后缀运算符时,其表达式的值是操作数自增前的值,计算完表达式的值后,操作数才自增。

前缀++、前缀--是右结合的,优先级和一元的正号、负号一样。
后缀++、后缀--是左结合的,优先级比一元的正号、负号高。

4.3 不要在子表达式中修改操作数

长的表达式可以利用运算符的优先级和结合性规则划分为子表达式。
但是C语言没有定义子表达式的求值顺序(除非含有逻辑运算符、条件运算符或逗号运算符)。表达式(a+b) * (c - d)中,无法确定(a + b)是否在(c - d)之前求值。

大多数表达式,不管子表达式的求值顺序如何,都是相同的值。但是当子表达式修改了某个操作数的值时,就不一定了:

a = 5;
c = (b = a + 2) - (a = 1);

如果先计算(a = 1),c值就为2;先计算(b = a + 2),c值就为6。

i = 2;
j = i * i++;

很多人会自然地认为j被赋值为4。但是j也可能被赋值为6:

  1. 先取出乘法的右操作数i,为2。之后i自增;
  2. 取左操作数也是i,取到新值3;
  3. i的新值和旧值相乘,结果为6。

取出变量,表示从内存中获取它的值,存在寄存器中。变量的后续变化不会影响已取出的值。

上述在表达式中修改变量的值的语句会导致未定义的行为,不同的编译器给出的编译结果是不同的。应该避免出现未定义的行为。

4.4 表达式可以做为语句

C语言任何表达式都可以用作语句,只要在后面添加分号即可。

// 赋值1,然后取出i的新值,不使用
i = 1;
// 取出i的值,不使用,然后i自减
i--;
// 计算表达式的值,然后丢弃
i * j - 1;

由于+=两个字符在键盘的同一个键上,发生误输入的概率很高,从而出现了什么也不做的表达式语句。


更多相关内容参考: 《C语言程序设计:现代方法》笔记

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值