跳转至

第2章 · 基本数据类型及其运算

本页最后更新于 ,内容可能过时

本章题目对应教材第 2 章,按 2.1 基本数据类型、2.2 输入输出、2.3–2.4 数据运算与表达式的顺序排列。

2.1 基本数据类型与类型转换

1 · 类型转换与整除

设有:int i = 3.0/2.0; double d = 3/2; 则 i 和 d 的值分别为( )。

  • A. 1, 1.0
  • B. 2, 1.0
  • C. 1, 1.5
  • D. 2, 1.5
答案

A

解析

先算右边,后转换——

  • 3.0/2.0:两个都是 double → 结果是 double 的 1.5;赋给 int i 时截断 → i = 1。
  • 3/2:两个都是 int → 整数除法,结果就是 int 的 1(余数 1 被丢弃);赋给 double d → d = 1.0。

关键:运算结果的类型由"参与运算的两个操作数的类型"决定,而不是由左边的变量决定。

易错:以为 double d = 3/2; 会得到 1.5。除法按 int 计算,1.5 没有出现。

想得到 1.5 必须写 3.0/2、3/2.0 或 (double)3/2。


2 · 类型转换与整除

若 int n; float f=13.8; 则执行 n=(int)f%3; 后,n 的值是( )。

  • A. 1
  • B. 4
  • C. 4.333333
  • D. 执行报错
答案

A

解析
  1. (int)f → 13.8 截断得 13;
  2. 13 % 3 → 13 = 4×3 + 1,余数 1;
  3. 赋给 n → n = 1。✅

易错:% 的优先级高于 =,且 % 只能用于整型——所以必须先 (int) 转换。若写成 f%3(float 直接取模)那才是编译错误(选项 D)。


3 · 转义字符与混合类型表达式

设有说明:char c='\010'; int x=10; float y=1.2; 则表达式 (int)(1.0f*x-c+y/2) 的值是( )。

  • A. 1
  • B. 2
  • C. 3
  • D. 4
答案

B

解析

逐步算——

  1. '\010' 是八进制转义,10₈ = 8,所以 c = 8。
  2. 1.0f * x = 1.0 * 10 = 10.0(float)
  3. 10.0 - c → c 被提升为 10.0 - 8 = 2.0
  4. y / 2 = 1.2 / 2 = 0.6
  5. 2.0 + 0.6 = 2.6
  6. (int)2.6 = 2(截断,不是四舍五入)✅

易错:

  1. 把 '\010' 当成十进制 10(八进制转义以 \0 开头,\010 = 8)
  2. 以为 (int) 会四舍五入(C 的强制转换一律向零截断)

2.2 格式输出和输入

4 · double 配 %f:格式串类型不匹配

如下程序,scanf 输入:1<B>.234<CR>(其中 <B> 表示空格,<CR> 表示回车),输出( )。

#include<stdio.h>

int main() {
    double x;
    scanf("%f", &x);
    printf("%.2f", x);
    return 0;
}
  • A. 1.234
  • B. 1.23
  • C. 1.00
  • D. 以上答案均不是
答案

D

解析

问题出在 %f。scanf 的 %f 要求的是 float*,而 &x 是 double*——两者类型不匹配。

scanf 会按 float(4 字节)去写这 8 字节的 double,读进来的数据是错的,所以输出既不是 1.23 也不是 1.234,只能是"以上答案均不是"。

正确写法是 scanf("%lf", &x)(printf 则 %f 与 %lf 都行,因为可变参数会做默认实参提升)。

scanf 里 double 必须写 %lf。


5 · 同一段程序换一组输入数字

有如下程序,scanf 输入:1234<B>.5678<CR> 后(其中 <B> 表示空格,<CR> 表示回车),printf 输出( )。

#include<stdio.h>

int main() {
    double x;
    scanf("%f", &x);
    printf("%.2f", x);
    return 0;
}
  • A. 1234.5678
  • B. 1234.56
  • C. 1234.00
  • D. 以上答案均不是
答案

D

解析

同一段程序,只是输入的数字换了。

scanf("%f", &x) 里的 %f 要 float*,&x 却是 double*,类型不匹配导致读入的数据本身就是错的,跟输入的是 1 .234 还是 1234 .5678 无关,四个具体数值选项(1234.5678 / 1234.56 / 1234.00)一个都不对。

改成 scanf("%lf", &x) 才会输出 1234.56。


6 · 四个输入输出语句谁不会出错

请指出以下能够正确运行的输入输出语句。

A)

int a, *b;
scanf("%d", b);

B)

char d[2]={'c', 'h'};
printf("%s\n", d);

C)

float a = 5.0;
printf("%d\n", &a);

D)

float c;
scanf("%f", c);
答案

C

解析

先把三个错项排掉——A:b 定义了但从未初始化,直接 scanf("%d", b) 就是往一个野指针写数据(*b 处应补 &a,正确写法是 scanf("%d", &a));

B:char d[2] 存不下 "abc" 那类字符串所需的结束符 '\0',%s 找不到终止符会一直往后续内存读下去(应补上 '\0');

D:scanf("%f", c) 漏了取地址符 &,c 是变量值不是指针,运行时崩溃。

C 是唯一"不会立刻出错"的:&a 是 float*,%d 会把它当整数打印,即打印地址。

严格说

严格说,用 %d 去打印一个指针属于实现定义行为(64 位机器上 int 4 字节、指针 8 字节,理论上可能截断),所以 C 并不是"标准意义上完全正确"。

但本题问的是"能够正确运行",A/B/D 是当场崩溃或未定义行为,只有 C 能跑通,本题答案为 C。


7 · 能正确输出内容的代码片段

下列代码片段可以正确输出内容的是?

  • A. printf("%d", 'A'-'a');
  • B. scanf("%d", NULL);
  • C. char str[3]="abc"; printf("%s",str);
  • D. printf("%f", "123.45");
答案

A

解析

A 正确:字符常量参与算术运算时按 int 用,'A'-'a' = 65 - 97 = -32,用 %d 输出完全合法。

B 把 NULL 当作 int* 传给 scanf,等于往地址 0 写数据,运行时崩溃。

C 的数组长度不足:char str[3] 只有 3 字节,装不下 "abc" 加结束符 '\0' 共 4 字节,字符串没有终止符,printf("%s") 会越界一直读到内存里的随机 \0 为止——能"编译过",但输出内容不可预期。

D 用 %f 去打一个 char*("123.45" 是字符串字面量的地址),格式与参数类型不匹配,属未定义行为。


2.3–2.4 数据运算与表达式

8 · 自增自减与短路求值

假设 a = -1, b = 1, c = 0,则执行 a++&&++b||c++; 后,a、b、c 三个变量的值分别是( )。

  • A. 0,1,1
  • B. 0,1,0
  • C. 0,2,0
  • D. 0,2,1
答案

C

解析

先把优先级理清楚:++ 高于 && 高于 ||,所以表达式是 (a++) && (++b) || (c++),按结合性写成 ((a++) && (++b)) || (c++)。

  1. a++:先取 a 的原值 -1(非 0 即真),再把 a 变成 0。结果 = 真。
  2. 左边为真,&& 必须继续 → 求值 ++b:b 先加 1 变成 2,结果 = 2(真)。
  3. A && B 整体为真;|| 遇到左边为真 → 短路,右边的 c++ 完全不执行。
  4. 所以最终 a = 0、b = 2、c = 0(保持原值)。✅

易错:忘了 || 会短路,把 c 算成 1(选 A 或 D)。另外 -1 在条件里是真,不是假。


9 · 自增自减与短路求值

设 int a = 0, b = 1, c = 2, x; 执行 x = ++a ? b++ : c++; 后,a、b、c、x 的值分别是( )。

  • A. a=1, b=2, c=3, x=2
  • B. a=1, b=2, c=2, x=2
  • C. a=1, b=2, c=2, x=1
  • D. a=1, b=1, c=3, x=3
答案

C

解析

?: 的条件部分先求值:

  1. ++a:a 先自增为 1,值 1 为真;
  2. 三目运算符只求值一个分支:取 b++,c++ 不执行;
  3. b++ 的值是 b 的原值 1,然后 b 变成 2;
  4. 所以 x = 1,b = 2,c = 2(不变)。✅

易错:

  1. 把 b++ 的值当成自增后的 2(选 A)
  2. 以为两个分支都会算(选了带 c=3 的选项)

10 · 求值顺序与未定义行为

若以下程序执行后,输出为( )。

int i = 3, j;
j = (++i) + (i++);
  • A. i = 4, j = 8
  • B. i = 5, j = 9
  • C. i = 5, j = 10
  • D. 结果未定义
答案

D

解析

同一个表达式里对 i 既前置自增又后置自增,而它们之间没有任何序列点(sequence point)。

C 标准明确规定这类代码属于 未定义行为(undefined behavior, UB)——不同编译器、不同优化级别下结果可以不一样,甚至可能崩溃。

这是本课程的一条核心结论:不要在一个表达式里对同一变量多次自增/自减。

易错

按"先算 ++i 再算 i++"推演出一个具体数字(比如选 B)。这类写法只能取"未定义/不确定"。

标准把"两个序列点之间对同一对象修改两次以上"或"修改后又读取其值"判为 UB,标准没有给出确定结果。


11 · 求值顺序与未定义行为

对于 int i = 3; 执行 printf("%d,%d\n", i, i++); 输出为( )。

  • A. 3,4
  • B. 3,3
  • C. 4,4
  • D. 结果不确定
答案

D

解析

函数实参的求值顺序是未指定的(标准只说不存在序列点),编译器可以从右往左求值,也可以从左往右。再加上 i 和 i++ 之间没有序列点,整个表达式是未定义行为。

在实践中:

  • 若从右往左求值 → i++ 先算,i 变成 4;再取 i 得 4 → 输出 4,3;
  • 若从左往右求值 → 取 i = 3;再算 i++ 得 3 → 输出 3,3。

结论:结果不确定(D)。 这也正是很多教材/编译器警告的来源。

易错:选 B(按从左到右的直觉判断)。printf 的参数求值顺序标准没有规定。


12 · 逗号运算符与优先级

设有:int a=1, b=2, c=3, x; 则执行 x = (a+1, b++, ++c); 后 x 的值为( )。

  • A. 2
  • B. 3
  • C. 4
  • D. 报错
答案

C

解析

括号里是一个逗号表达式。逗号表达式的值取最后一个子表达式的值,这里是 ++c:

x = (a + 1, b++, ++c);   // x 拿到的是 ++c 的值

c 从 3 自增为 4,所以 ++c 的值是 4,x = 4。

括号里前两项照常执行。a+1 算出来没用上,b 自增为 3。

gcc 实测执行后:x = 4, a = 1, b = 3, c = 4。


与选项 A(2)的差别全在那对括号上。

逗号运算符的优先级低于赋值。若题面把括号丢掉,写成

x = a+1, b++, ++c;       // 等价于 (x = a+1), (b++), (++c);

整句就变成三个并列的表达式。x 只拿到 a+1 = 2,与 b++、++c 无关。


逗号表达式有两条规则:

  • (a, b, c) 的值 = 最后一个子表达式的值;
  • 逗号运算符优先级最低(低于赋值)。所以 x = a+1, b++ 中,x 拿到的是 a+1 的值,不是 b++ 的值。

13 · 浮点数比较

如果 a=3.0, b=2.1, c=0.9,则下列表达式的值为 0 的是( )。

  • A. a>b
  • B. a-b==c
  • C. b++>c
  • D. c=(a>b)
答案

B

解析

逐项算——

  • A:3.0 > 2.1 为真 → 值 1。
  • B:a-b = 3.0 - 2.1。浮点数无法精确表示 2.1、0.9,实际结果是 0.8999999999999999,而 c 是 0.90000000000000002。两者不相等 → 值为 0。✅
  • C:b++ > c,先用 b 的原值 2.1 > 0.9 为真 → 值 1(之后 b 变成 3.1,但表达式值仍是 1)。
  • D:c = (a>b) → a>b 为 1,赋值给 c 使 c = 1.0,整个赋值表达式的值是 1.0 → 不是 0。

浮点数不能直接用 == 判断相等,应改为 fabs(a-b) < 1e-6(EPS 容差比较)。

同一写法在另一道程序题里也出现过:if (avg == 60.0),题面写明"若学生改为 if (avg == 60.0),不给分"。


14 · 运算符优先级

下列运算符中优先级最高的是__。

  • A. <
  • B. +
  • C. %
  • D. ++
答案

D

解析

++ 是单目(一元)运算符,优先级属于最高那一档,远高于所有二元运算符;其余三项的次序是 % > + > <。

选 C 是只记得算术比关系高,忘了单目运算符优先级最高。

一元运算符(++ -- ! & * + -)的优先级高于任何二元运算符。


15 · 逗号与关系运算符左结合

设 a 为整型变量,与表达式 10<a<15 相等的表达式是__。

  • A. 1
  • B. a==11||a==12||a==13||a==14
  • C. 0
  • D. !(a<10) && !(a>=15)
答案

A(1)

解析

关系运算符左结合,所以 10<a<15 实际是 (10<a)<15:10<a 先算出 0 或 1,这个 0/1 再去和 15 比,恒为真,整个表达式的值永远是 1。

B、D 表达的是"10<a<15"的数学含义(会随 a 变化,D 在 10≤a<15 时为 1、否则为 0),

但题目问的是"与该表达式的值相等",而 10<a<15 的值恒为 1,所以取 A;D 与"a 在 10 到 15 之间"等价,但与 10<a<15 的值不总是相同。要写"a 在 10 到 15 之间"需要写成 10<a && a<15。


16 · 自增运算符与求值顺序

以下代码的输出是什么?

int a = 5, b = 3;
printf("%d\n", (a++) + (++b));
  • A. 8
  • B. 9
  • C. 10
  • D. 未定义行为
答案

B(9)

解析

a++ 取原值 5(a 随后变成 6),++b 先加 1 使 b = 4 再取新值 4,所以 5 + 4 = 9。

关键结论:这不是未定义行为——两个自增作用在不同变量上,不存在"同一对象在两个序列点之间被修改两次"的问题,结果完全确定。

选 D 是把"表达式里出现自增"直接等同于 UB;UB 的判定条件是同一个变量被无序修改。


17 · 逻辑运算与德摩根定律

下列哪个逻辑表达式等价于 !(a > b && a < c)?

  • A. (a <= b) || (a >= c)
  • B. (a >= b) || (a <= c)
  • C. (a > b) || (a < c)
  • D. !(a < b && a > c)
答案

A

解析

德摩根定律:!(X && Y) = !X || !Y,其中 !(a > b) 就是 a <= b、!(a < c) 就是 a >= c,合起来正是 A。

B 把两个比较符的方向弄反了——! 取反时比较符也要一起翻(> 变 <=);

C 既没取反、又把 && 换成了 ||;D 只把里层的两个比较反着写,外层的 ! 和 && 原封不动,整体不等价。


18 · 逻辑运算与德摩根定律

设有 int x, y;,循环语句 while (!x && !y) 中的循环条件表达式等价于( )。

  • A. !(x == 0 && y == 0)
  • B. !(x == 0 || y == 0)
  • C. !(x != 0 || y != 0)
  • D. x = 0 && y = 0
答案

C

解析

先把 !x 读成"x == 0",等价地写成 !(x != 0);!y 同理是 !(y != 0)。

于是 !x && !y = !(x != 0) && !(y != 0),对整个式子用德摩根定律提取外层取反 → !(x != 0 || y != 0),即 C。

A 的取反方向错了(把 && 的取反写成了 &&);D 用的是赋值号 = 而不是关系符 ==,本身就写错了。


19 · 绝对值的逻辑表达式(德摩根)

请指出以下能够表示 |a+b| < 5 的逻辑表达式。

  • A. (a+b) < 5 && (a+b) < -5
  • B. !((a+b) >= 5 && (a+b) <= -5)
  • C. !((a+b) >= 5 || (a+b) <= -5)
  • D. (a+b) < 5 && !((a+b) < -5)
答案

C

解析

先把数学条件翻成"夹逼"形式:|a+b| < 5 ⟺ -5 < a+b < 5;它的反面是"a+b >= 5 或 a+b <= -5"(碰到任一边界就越界)。

取反回去即 !((a+b) >= 5 || (a+b) <= -5),所以是 C。

  • A 错:用 && 把两个互斥条件串起来,恒为假。
  • B 错:德摩根展开时忘了把 && 换成 ||。
  • D 错:!((a+b) < -5) 等价于 a+b >= -5,只挡住下界、漏了上界。

范围判断的两个边界:|x| < k 先列出 >= k 或 <= -k 两个边界,取反时 && 与 || 要互换。


20 · 复合赋值的右结合

有以下语句:

int a = 6;
a += a -= a * a;
printf("%d", a);

则输出结果是( )。

  • A. -132
  • B. -60
  • C. -24
  • D. 0
答案

B(-60)

解析

复合赋值运算符 +=、-= 右结合,所以 a += a -= a * a 等价于:

a += (a -= (a * a));

从最右边往里算:

  1. a * a = 6 × 6 = 36(此刻 a 还没被改);
  2. a -= 36 → a = 6 - 36 = ``-30,这个赋值表达式的值就是 a 的新值 -30;
  3. 再算 a += -30:注意此时式子左边的 a 已经是 -30 了(上一步刚改过),所以 a = -30 + (-30) = ``-60。✅

易错

第 3 步左边用了 a 最开始的 6,即按从左到右算成 a + a - a*a = 6 + 6 - 36 = -24(选 C)。

复合赋值的左侧在"赋值那一刻"读的是变量当时的当前值,而内层已经把它改成 -30 了。

选 D 的 0 是按"先算成 36 再减 36"得到;A 的 -132 是符号或顺序算错。

一串复合赋值连着写时,从最右往左算,每算一层都在更新同一个变量。

附带说明:严格按 C 标准,这一句里 a 被"无序地读又写",gcc 15.2.0 编译时会给出 -Wsequence-point 警告(operation on 'a' may be undefined)。

按"右结合 + 从右往左算"推出来的 -60 与 gcc 实测输出一致,答案取 B。


21 · 逻辑短路求值(&& 高于 ||)

若 a、b、c 均为 int 型变量,则执行以下语句后:

a = b = c = 1;
++a || ++b && ++c;

(4) a 值为( )。

  • A. 不正确
  • B. 0
  • C. 2
  • D. 1

(5) b 值为( )。

  • A. 1
  • B. 2
  • C. 不正确
  • D. 0

(按 (4)(5) 两小问作答,共用一个程序段。)

答案

(4) C(a = 2)  (5) A(b = 1)

解析

本题只需判断一件事——优先级:&& 高于 ||。所以

++a || ++b && ++c;

实际是

++a || (++b && ++c);

而不是"从左到右依次算"。接着按短路规则走:

  1. ++a:a 先自增为 2,表达式的值是 2(非 0 即真);
  2. || 的左边已经为真 → 短路,右边整个 (++b && ++c) 一个字符都不执行;
  3. 于是 b 保持原值 1,c 也保持原值 1。

所以 (4) 选 C、(5) 选 A。✅

易错

按"从左往右"想成 ((++a || ++b) && ++c) 或者以为 || 两边都会算,于是把 b 算成 2((5) 选 B)。只要 || 左边为真,右边就是死代码。

对比来看:若把原式换成 ++a && ++b || ++c(外层变成了 ||),由于 && 左边为真,++b 会被求值,此时 b 等于 2。这道题的关键是看清 && 和 || 谁在外层。

另外复查一个小地方:(4) 的选项 A 和 (5) 的选项 C 都写着"不正确",这其实是在问"该值是否可确定"——本题没有任何未定义行为(三个不同变量各改一次),结果是完全确定的。


22 · 复合赋值与取余

假定有以下变量定义,则能使赋值为 3 的表达式是( )。

int k = 7, x = 12;
  • A. x %= (k %= 5)
  • B. x %= (k - k % 5)
  • C. x %= k - k % 5
  • D. (x %= k) - (k %= 5)
答案

D

解析

两条规则:

  1. op= 形式的赋值表达式的值 = 赋值之后该变量的新值
  2. % 的优先级高于 -(括号可以改变顺序)

逐项算:

选项 展开过程 表达式的值
A 括号里 k %= 5 → k = 7 % 5 = 2;再 x %= 2 → x = 12 % 2 = 0 0
B 括号里 k - k % 5 = 7 − 2 = 5;x %= 5 → x = 12 % 5 = 2 2
C 没有括号,但 % 本来就比 - 高 → 和 B 完全等价 2
D 先 x %= k → x = 12 % 7 = 5,该子表达式的值是 5;再 k %= 5 → k = 2,该子表达式的值是 2;相减 3 ✅

题目问的是"表达式的值"。D 里两个括号的值分别是 x、k 的新值 5 和 2,相减得 3。

B、C 两个选项的值相同,都是 2。

易错

把 x %= (k - k % 5) 看成 x %= (k - (k % 5)) 之后忘了 % 的优先级,误算成 (7 - 7) % 5 = 0;或者忘记 x 会先被改掉,以为 D 里后面的减法仍用 x = 12。

附带说明:严格按 C 标准,D 是个边角情况——x %= k 读了 k,同一表达式的另一半又写了 k,两者之间没有序列点,gcc 15.2.0 编译时会给出 -Wsequence-point 警告(operation on 'k' may be undefined)。

gcc 实测按从左往右求值,结果稳定为 3,与给出的答案一致,答案取 D。


23 · 逗号表达式与复合赋值

执行下面语句后,x 的值为( )。

int i, j, x = 0;
i = 3, j = i + 6;
x -= j += i;
  • A. 15
  • B. -12
  • C. 10
  • D. 3
答案

B(-12)

解析

分两段看:

  1. i = 3, j = i + 6; —— 这是一条逗号表达式语句(逗号运算符优先级最低,低于 =),它依次执行两个赋值:i = 3、j = 3 + 6 = 9。(把这里的逗号当作两条独立语句处理,结果也一样。)
  2. x -= j += i; —— 右边先算:j += i → j = 9 + 3 = 12,这个子表达式的值就是 j 的新值 12;再 x -= 12 → x = 0 - 12 = ``-12。✅

易错

  • 把 j += i 读成不起作用的 j + i → 得 -3,选项里没有;
  • 忽略 x 初值为 0,按"0 减一个数再取绝对值"算 → 选 12 或 A;
  • 把两个复合赋值当成同时进行 → 算不清 j 到底用 9 还是 12。

a op= b 的值 = 赋值之后 a 的新值;x -= j += i 这种嵌套从最右边开始算。