assert是调试期的条件检测宏,在条件为假时终止程序并打印错误信息。
特点:
NDEBUG未定义)生效NDEBUG)编译期断言,在编译时检查条件,不满足则编译失败。
用途:
全局错误变量,标准库函数在出错时设置。
常用函数:
perror():打印错误信息到stderrstrerror():将错误码转换为字符串常见模式:
#include <assert.h>
// 断言宏
assert(expression); // 表达式为假时终止
// 编译期断言(C11)
_Static_assert(constant-expression, "message");
#include <errno.h>
// 错误变量
extern int errno;
// 错误信息函数
void perror(const char *s);
char *strerror(int errnum);
// 返回错误码示例
int func(void) {
// 成功
return 0;
// 失败
errno = EINVAL;
return -1;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
// 编译期断言(C11)
_Static_assert(sizeof(int) == 4, "int must be 4 bytes");
_Static_assert(sizeof(long) >= 4, "long must be at least 4 bytes");
// 错误码定义
#define ERR_SUCCESS 0
#define ERR_INVALID -1
#define ERR_NO_MEMORY -2
#define ERR_NOT_FOUND -3
// 使用assert验证前置条件
int divide(int a, int b) {
assert(b != 0); // 调试期检查除数不为零
return a / b;
}
// 错误码模式示例
int find_element(int arr[], int size, int target, int *index) {
if (arr == NULL || index == NULL) {
return ERR_INVALID;
}
for (int i = 0; i < size; i++) {
if (arr[i] == target) {
*index = i;
return ERR_SUCCESS;
}
}
return ERR_NOT_FOUND;
}
// 错误恢复示例
int safe_divide(int a, int b, int *result) {
if (result == NULL) {
errno = EINVAL;
return ERR_INVALID;
}
if (b == 0) {
errno = EDOM;
return ERR_INVALID;
}
*result = a / b;
return ERR_SUCCESS;
}
void demonstrate_assert(void) {
printf("=== assert示例 ===\n");
int x = 10;
assert(x > 0); // 通过
printf("assert(x > 0) passed\n");
// assert(x < 0); // 会失败并终止程序
printf("divide(10, 2) = %d\n", divide(10, 2));
}
void demonstrate_errno(void) {
printf("\n=== errno示例 ===\n");
FILE *fp = fopen("nonexistent.txt", "r");
if (fp == NULL) {
printf("fopen failed\n");
printf("errno = %d\n", errno);
perror("perror message");
printf("strerror(errno) = %s\n", strerror(errno));
}
}
void demonstrate_error_code(void) {
printf("\n=== 错误码模式 ===\n");
int arr[] = {10, 20, 30, 40, 50};
int size = sizeof(arr) / sizeof(arr[0]);
int index;
int result;
// 成功情况
result = find_element(arr, size, 30, &index);
if (result == ERR_SUCCESS) {
printf("Found 30 at index %d\n", index);
}
// 未找到情况
result = find_element(arr, size, 99, &index);
if (result == ERR_NOT_FOUND) {
printf("99 not found in array\n");
}
// 无效参数情况
result = find_element(NULL, size, 30, &index);
if (result == ERR_INVALID) {
printf("Invalid parameter detected\n");
perror("find_element");
}
}
void demonstrate_error_recovery(void) {
printf("\n=== 错误恢复 ===\n");
int result;
int ret;
// 正常情况
ret = safe_divide(10, 2, &result);
if (ret == ERR_SUCCESS) {
printf("10 / 2 = %d\n", result);
}
// 错误情况
ret = safe_divide(10, 0, &result);
if (ret != ERR_SUCCESS) {
printf("Division by zero detected\n");
printf("Error: %s\n", strerror(errno));
}
}
int main() {
demonstrate_assert();
demonstrate_errno();
demonstrate_error_code();
demonstrate_error_recovery();
return 0;
}
编译命令:
# 调试版本(assert生效)
gcc -Wall -Wextra -o assert_demo assert_demo.c
./assert_demo
# Release版本(assert禁用)
gcc -Wall -Wextra -DNDEBUG -o assert_demo_release assert_demo.c
./assert_demo_release
assert(func++)在Release中func不会递增Q1: assert和错误码有什么区别? A: assert检测程序员的逻辑错误,调试期使用,Release禁用。错误码检测运行时错误(如文件不存在、内存不足),生产环境必须处理。
Q2: 什么时候用assert,什么时候用错误码? A: assert用于验证不变量和前置条件(如参数非空、数组索引有效)。错误码用于可预期的运行时错误(如文件打开失败、网络超时)。
Q3: 为什么说assert参数不能有副作用?
A: Release版本中assert被移除,参数表达式不执行。如assert(func++)在Release中func不会递增,导致程序行为不一致。
Q4: errno有什么局限性? A: errno只在函数失败时有效,成功时不会清零。多线程环境下某些实现errno是宏保证线程安全,但标准不保证。
Q5: 嵌入式系统中如何处理致命错误? A: 根据严重程度:1) 记录日志并继续 2) 重置模块 3) 触发看门狗重启 4) 进入安全状态。关键系统需要冗余设计和故障恢复机制。