检查输入有效性(指针是否为NULL)和检查字符串长度是否为0
检查输入有效性(指针是否为NULL)和检查字符串长度是否为0
这两个检查针对的是完全不同的边界情况,都是必要的防御性编程措施:
1. 空指针检查 if(!src)
- 目的:防止解引用空指针
- 场景:当调用者传入
NULL
时 - 风险:
strlen(NULL)
会导致程序崩溃(段错误) - 处理:直接返回错误码
-1
- 必要性:⭐️⭐️⭐️⭐️⭐️(关键安全措施)
2. 空字符串检查 if(len == 0)
- 目的:处理长度为0的特殊情况
- 场景:当传入
""
时 - 风险:
src + len - 1
会指向src[-1]
(非法内存访问)while(len--)
会执行len=-1
次(逻辑错误)
- 处理:直接打印 “(empty)” 并返回
- 必要性:⭐️⭐️⭐️⭐️(关键逻辑完整性)
为什么两者都需要?
检查类型 | 输入示例 | 不检查的后果 | 修正后行为 |
---|---|---|---|
空指针检查 | test_func(NULL) | 程序崩溃(段错误) | 返回 -1 |
空字符串检查 | test_func("") | 非法内存访问(未定义行为) | 打印 “(empty)” |
错误路径演示(无空字符串检查)
// 当 src = "" 时:
int len = 0; // strlen("")=0
const char* s = src + 0 - 1; // = src - 1(非法内存位置!)while(0-- != 0) { // 先判断0!=0→false,但len变为-1// 循环不会执行
}
// 但s已经指向非法位置
实际应用场景
-
用户输入处理:
char input[100]; fgets(input, sizeof(input), stdin); test_func(input); // 用户可能直接按回车(空字符串)
-
网络数据解析:
char* packet = receive_network_data(); test_func(packet); // 可能收到空包或无效指针
-
API边界防护:
// 库函数必须处理所有可能的输入 int public_api(char* str) {return test_func(str); }
完整安全处理流程
int test_func(char* src) {// 第一层:空指针防护(防止崩溃)if(!src) {log_error("NULL pointer passed");return -1;}// 第二层:获取长度int len = strlen(src);// 第三层:空字符串处理(防止逻辑错误)if(len == 0) {printf("(empty)");return 0; // 合法输入,正常返回}// 第四层:内存分配检查char* dest = malloc(len + 1);if(!dest) {log_error("Memory allocation failed");return -2; // 区分内存错误}// ... 后续操作 ...
}
为什么其他语言不需要?
对比其他语言的处理:
语言 | 空指针处理 | 空字符串处理 |
---|---|---|
C/C++ | 需手动检查 | 需手动检查 |
Java | 自动抛NullPointerException | 可正常处理 “” |
Python | 自动抛TypeError | 可正常处理 “” |
Go | 自动panic | 可正常处理 “” |
在系统级编程中(特别是C/C++),这种显式检查是必要的,因为:
- 没有运行时自动检查
- 直接内存访问风险高
- 未定义行为可能导致严重漏洞
总结
两个检查缺一不可:
if(!src)
保护的是程序的生命周期(防崩溃)if(len==0)
保护的是程序的逻辑正确性(防未定义行为)
这正是C语言编程的核心原则:
“永远不要信任外部输入,用防御性代码处理所有边界情况”