Xi Ruoyao 7 gadi atpakaļ
vecāks
revīzija
62d2f496ef
1 mainītis faili ar 15 papildinājumiem un 9 dzēšanām
  1. 15 9
      content/post/exam-ub.md

+ 15 - 9
content/post/exam-ub.md

@@ -8,6 +8,11 @@ Even a stupid problem can lead to some thoughts.
 """
 +++
 
+{{% alert note %}}
+本文中对 C 语言标准章节的引用以 [N1570][N1570] 为准。
+[N1570]:/assets/std/c1x-n1570.pdf
+{{% /alert %}}
+
 # 某笔试题中的未定义行为
 
 昨天看到一道笔试题,要求找出以下程序中的所有错误(代码已经手动格式化)。
@@ -43,7 +48,7 @@ int *frequency(int size)
 
 然而这么改就够了吗?当然不是!分析一下 `size * sizeof(int)` 这个东西,
 它是一个整数乘法表达式,右边的子表达式 `sizeof(int)` 根据语言标准具有
-`size_t` 类型,而且它是无符号类型(6.5.3.4p2, 7.17p2)。而 `int`
+`size_t` 类型,而且它是无符号类型(6.5.3.4p5, 7.19p2)。而 `int`
 是带符号类型(6.7.2p2, p5),把它们两个乘起来的时候... ... 根据标准,
 会执行常规算术运算自动类型转换(usual arithmetic conversions)
 (6.5.5p3),对于整数运算来说,它的规则如下(6.3.1.8p1):
@@ -82,7 +87,8 @@ int *frequency(int size)
 
 > 以下规则适用于任何可以使用 `int` 或 `unsigned int` 的表达式:
 > 
-> * 如果对象或表达式具有整数类型,且其等级低于 `int` 和 `unsigned int`。
+> * 如果对象或表达式具有整数类型,且其等级小于等于 `int` 和 `unsigned int`,
+> 且本身又不是 `int` 或 `unsigned int`。
 > * 如果是具有 `_Bool`、`int`、`singed int` 或 `unsigned int` 的位段。
 >
 > 如果 `int` 能表示原始值的所有类型,则将其值转换为 `int`;否则,
@@ -91,10 +97,10 @@ int *frequency(int size)
 > 整数提升转换一定保留原始值不变,包括符号。正如之前章节已经讨论的,
 > `char` 默认是否带符号由实现决定。
 
-分析我们的例子,如果 `size_t` 的等级小于 `int` 的等级,则它会被提升成
+分析我们的例子,如果 `size_t` 的等级小于等于 `int` 的等级,则它会被提升成
 `unsigned int` ,而它的等级等于 `int` 的等级,于是 `int` 被转换成
 `unsigned int` ,结果就是两个 `unsigned int` 相乘。如果 `size_t`
-的等级大于等于 `int` 的等级,则 `int` 会被转换成 `size_t`,就是两个
+的等级大于 `int` 的等级,则 `int` 会被转换成 `size_t`,就是两个
 `size_t` 相乘,总之都是无符号乘无符号。很“好”,不会触发未定义行为!
 
 然而我之前翻译的文章中早就说过,不触发未定义行为不代表没 bug 。
@@ -226,7 +232,7 @@ array = calloc(u_size, sizeof(int));
 {{% /alert %}}
 
 然而,无论是 `malloc` 还是 `calloc` 分配失败的时候都会返回 `NULL`
-(7.20.3.1p3, 7.20.3.3p3),如果解引用空指针,又会产生未定义行为,
+(7.22.3.2p3, 7.22.3.4p3),如果解引用空指针,又会产生未定义行为,
 所以要把这种情况判断掉:
 
 ```c
@@ -249,7 +255,7 @@ while (scanf("%d", &i) == 1)
 
 至于这个错误怎么处理我也不知道(没有接口文档)。然而这就够了吗?
 如果用户干脆输入一个 `int` 无法表示的大数呢?语言标准规定
-(7.19.6.2p10):
+(7.21.6.2p10):
 
 > 只要遇到了一个 `%` 限定符,输入项(或者对于 `%n` 限定符来说,
 > 已经输入的字符个数)会被转换成与该限定符匹配的类型。
@@ -259,13 +265,13 @@ while (scanf("%d", &i) == 1)
 > **如果该对象的类型与限定符不一致,或者转换结果不能被该对象表示,
 > 则行为是未定义的。**
 
-再阅读一下 `%d` 限定符的说明(7.19.6.2p11):
+再阅读一下 `%d` 限定符的说明(7.21.6.2p11):
 
 > d: 匹配一个或许带符号的十进制整数,其格式与 `strtol` 函数在 `base`
 > 参数的值为 10 时对主体序列 (subject sequence) 的预期相同。
 > 对应的参数应该是指向带符号整数的指针。
 
-什么是“主体序列”?再次翻阅标准(7.20.1.4p3):
+什么是“主体序列”?再次翻阅标准(7.22.1.4p3):
 
 > 如果 `base` 在 2 到 36 之间(含),期望的主体序列是一个包含数字和字母,
 > 表示一个基为 `base` 的整数的字符序列,它可以在开头带有一个正号或负号,
@@ -341,7 +347,7 @@ return array;
 
 What the f\*\*k? 返回一个野指针让调用者怎么用?
 调用者几乎无论如何都会触发未定义行为。那么就得把 `free` 删掉。
-当然这里肯定又有人说会泄露内存,那调用者去 `free` 内存就了,
+当然这里肯定又有人说会泄露内存,那调用者去 `free` 内存就可以了,
 [`asprintf`][1] 和 [`strdup`][2] 等许多标准函数都是这么干的。
 如果非要说这样做不好,需要改函数接口,这是 [另一个问题][3] 了。