第十九章:控制流变体
级别 3 -- 经验
非局部跳转、信号和结构化错误处理——理解 C 的控制流逃逸机制以及 Auto 如何用更安全的模式替代它们。
程序并不总是遵循直线路径。函数提前返回、循环跳出、错误通过调用栈传播。C 提供了几种非标准控制流机制:goto、setjmp/longjmp 和信号处理器。这些功能强大但危险。Auto 用结构化的替代方案取代了其中的大部分。
19.1 详细示例
考虑一个必须在多个层面处理错误的解析器:
c
// C 深入:使用 goto 进行错误处理
#include <stdio.h>
#include <stdlib.h>
int parse_input(const char *input) {
if (!input) goto error_null;
if (*input == '\0') goto error_empty;
int value = atoi(input);
if (value <= 0) goto error_negative;
printf("Parsed: %d\n", value);
return value;
error_null:
fprintf(stderr, "Error: null input\n");
return -1;
error_empty:
fprintf(stderr, "Error: empty input\n");
return -2;
error_negative:
fprintf(stderr, "Error: non-positive value\n");
return -3;
}C 程序员使用 goto 进行集中式错误清理。这是 C 中少数被接受的 goto 用法之一。模式是:跳转到一个释放资源并报告错误的标签。
Auto 的方法。 Auto 使用 Result 类型进行错误传播:
auto
// Auto:使用 Result 类型进行结构化错误处理
type ParseResult {
value int
ok bool
error str
}
fn ParseResult.ok(val int) ParseResult {
ParseResult(val, true, "")
}
fn ParseResult.err(msg str) ParseResult {
ParseResult(0, false, msg)
}
fn parse_input(input str) ParseResult {
if input == "" {
return ParseResult.err("empty input")
}
let value int = int(input)
if value <= 0 {
return ParseResult.err("non-positive value")
}
ParseResult.ok(value)
}