阅读程序练习
1
自增运算符的位置对返回值的影响。
2*
sizeof
是不求值表达式,因此第一个x++
不会对变量x
产生副作用。 在几乎所有的现代计算机中,int
包含 4 个字节。
3*
左移运算符 (
<<
) 的优先级严格低于加法运算符 (+
)。
4*
等号/不等号运算符 (
=
/!=
) 的优先级严格低于偏序比较运算符 (<
/>
/<=
/>=
)。
5
C++ 没有求幂 (
**
) 运算符,故a**b
解析为a * (*b)
6*
编译时,注释 (
/*
) 替换先于运算符的解析
7
三目运算符是右结合的。
8*
在 C++ 中,三目运算符和赋值运算符具有相同的优先级,且右结合。
9*
在 C 中,三目运算符从不返回左值。
10*
三目运算符不会执行错误分支
11*
在 C 中,三目运算符的优先级严格高于赋值运算符,故此题同 #9。
12
vector 等容器在作为局部变量时默认调用构造函数。
13
C++11 前模板定义不能使用
>>
,需添加空格。
14
statement-codes
#include <bits/stdc++.h>
using std::cin;
using std::cout;
typedef std::string foo;
namespace test {
typedef int foo;
std::vector <foo> bar;
}
int main() {
test::bar.resize(1);
cout << '[' << test::bar.back() << ']' << '\n';
return 0;
}
命名空间 test 中会使用
test
中的foo
,即int
。
15
statement-codes
#include <bits/stdc++.h>
using std::cin;
using std::cout;
typedef std::string foo;
namespace test {
typedef int foo;
std::vector <::foo> bar;
}
int main() {
test::bar.resize(1);
cout << '[' << test::bar.back() << ']' << '\n';
return 0;
}
::
前缀表示无名空间,即外部的std::string
16
代码同上,使用 C++03
编译
C++11 前,
<:
被强制分析为[
的代用记号,故上述程序无法成功编译。
17*
十六进制转义序列
\x35
表示字符'5'
。
18*
'F'
是合法十六进制数位,这会导致十六进制转义序列超限而无法编译。
19
C++ 中没有负整数常量,
-2147483648
表示对2147483648
取负。而2147483648
无法用int
表示。
20
“
long long
后缀” 要么使用ll
,要么使用LL
,大小写不能混用。
21*
0xff = 255
22*
0xff + 2 = 257
23*
以
e
或E
结束的十六进制常量在后面紧跟+
或-
时,编译器会贪心认为其是浮点数而报错。
24
略
25
一对孤立的
--
在一起时总是会被认为是自减运算符。
26*
通过加空格可以避免错误解析;一元运算符具有较高的优先级。
27
贪心的编译器总会将完整的
--
给左边 (a
),故a --- b
解析为(a--) - b
。
28
a----
被解析为( (a--) -- )
,而后置自减运算符返回右值,故无法对其再自减。
29
n = (++n) + (n++) + (++n) + (n++);
经典的 UB,不同的编译器会返回各种各样的结果。
30
在 C++11 中,
++n
中对n
的副作用按顺序早于对n
的值计算,而对n
的值计算按顺序早于赋值操作。
31*
在 C++11 中,
n++
中对n
的值计算按顺序早于对n
的副作用,也按顺序早于赋值操作,但对n
的副作用和赋值操作是无顺序的。
32*
函数各参数的求值是*顺序不确定的。
33
UINT_MAX + 1
会导致int
溢出 (俗称 “爆int
”),因此结果会舍去最高位,变为 0。
34*
有符号整数溢出是 UB。
35
移位数量大于位长为 UB。
36*
左移一个负数是 UB。
37**
右移负数不是 UB,是向上取整 (算术右移)。
38*
C++17 前,
??/
被视作\
的 “三标符”;转义序列\\
表示单个反斜杠\
。
39
“代用记号” (双标符) 的替换在处理注释和字符串之后。
40*
无副作用的无限循环是 UB。
41*
该无限循环有副作用 (返回
n
),故不是 UB;Goldbach 猜想在 范围内成立。
42*
#define 是简单的字符替换,因此该宏会展开为
((bar()) < (car()) ? (bar()) : (car()))
,不难得知此时函数bar()
会被调用两次。而函数即使是inline
,也不会多次调用bar()
。
43*
#
运算符会立即将形参转化为字符串常量。
44
bar
。
45*
宏替换会连续应用,直到找不到对应的宏为止。
46*
递归替换抑制规则表明,如果在应用宏替换时遇到递归过程中出现过的宏名,则会保留宏名不再进行替换。
47*
标识符
a
的替换轨迹为a
→b
→c
→d
→b
,在第二次替换到b
时应用递归替换抑制规则,故a
的最终结果是b
而不是a
。
48
宏运算符
##
用于拼接参数。
49
##
可以将+
和=
拼接成+=
。
50*
##
不能用于拼接注释。
51*
##
拼接运算符时,只有它们能组成一个完整的运算符时,该替换才能进行。而a**b
解析为a * (*b)
,在这里**
不是一个完整的运算符。
52
特殊宏
__LINE__
返回当前行号。
53*
“行号” 可以通过
#line
指令更改。
54*
“行号” 始终解析为十进制。
55
continue
的功能是跳转到循环体末尾而不是第二次循环的开头。
56
switch
块内可以定义变量。
57*
switch
的本质是标签跳转 (goto
),而标签跳转不允许交叉初始化 (cross initialization),除非进入作用域的所有变量声明时不带初始化器和构造/析构函数。
58*
无限定查找不会找到命名空间
std
中的sort
函数。
59*
ADL 法则
(阿毒瘤法则)(实参依赖查找) 可以找到实参std::greater
所在的命名空间std
中的sort
函数。
60*
ADL 法则全称实参依赖查找,参数的命名空间不会再通过函数名进行查找。
Results
总题数:60,正确:26,错误:34,进度:100.0%,正确率:43.3%