如何在C语言中自定义assert()的错误提示消息并替换默认输出?
自定义断言失败输出的问题修正
需求说明
期望断言失败时输出如下格式的提示:
Assertion failed! Program: C:\...\assert-test.exe File: C:\...\assert-test.c, Line 21 Expression: denominator != 0.0
尝试通过定义NDEBUG禁用默认assert并自定义断言逻辑,但原有代码存在多处错误,导致无法达到预期效果,以下是问题分析和修正方案:
原代码的核心问题
- 宏逻辑完全颠倒:
NDEBUG的作用是禁用断言,原代码却在定义NDEBUG时启用自定义断言,未定义时反而空实现,完全搞反了逻辑。 - 参数传递错误:
M_Assert宏中传递的buffer是未定义变量,__M_Assert函数无法直接使用。 - C/C++语法混用:使用了C++的
std::cerr,但代码整体是C风格,应该用C标准库的错误输出方式。 - Windows API依赖缺失:调用
GetModuleFileName但未包含windows.h,MAX_PATH也未定义。 - 断言逻辑错误:
__M_Assert中判断的是表达式字符串是否为空,而非断言表达式的真假。 - 语法错误:主函数
return后无返回值,宏定义末尾多余分号。
修正后的完整代码
#include <stdio.h> #include <stdlib.h> #include <windows.h> // 包含Windows API所需头文件 // 处理scanf的跨平台定义 #if defined(_STDC_LIB_EXT1_) || defined(_MSC_VER) #define __STDC_WANT_LIB_EXT1_ 1 #define SCANF scanf_s #else #define SCANF scanf #endif // 自定义断言宏:未定义NDEBUG时启用,定义时禁用 #ifndef NDEBUG #define M_Assert(expr) \ do { \ if (!(expr)) { \ char buffer[MAX_PATH]; \ GetModuleFileName(NULL, buffer, MAX_PATH); \ __M_Assert(buffer, __FILE__, __LINE__, #expr); \ } \ } while(0) #else #define M_Assert(expr) ((void)0) // 空实现,避免语法问题 #endif // 自定义断言输出函数 void __M_Assert(const char* program_path, const char* file, int line, const char* expr) { fprintf(stderr, "Assertion failed!\n"); fprintf(stderr, "Program: %s\n", program_path); fprintf(stderr, "File: %s, Line %d\n", file, line); fprintf(stderr, "Expression: %s\n", expr); abort(); // 终止程序 } int main() { double numerator = 0.0, denominator = 0.0; printf("--- Quotient of two numbers ---\nEnter the nominator: "); if (SCANF("%lf", &numerator) < 1) { fprintf(stderr, "invalid input\n"); return 1; } printf("denominator: "); if(SCANF("%lf", &denominator) < 1) { fprintf(stderr, "invalid input\n"); return 1; } M_Assert(denominator != 0.0); // 调用自定义断言 printf("quotient is %f\n", numerator / denominator); return 0; // 补充返回值 }
关键修正点说明
- 宏逻辑修正:用
#ifndef NDEBUG判断,只有在调试模式(未定义NDEBUG)时才启用自定义断言,发布模式下自动禁用。 - 表达式判断:在宏内先计算
expr的真假,为假时才调用输出函数,符合断言的核心逻辑。 - 缓冲区处理:在宏内定义
buffer局部变量,传递给函数,避免未定义变量问题。 - 纯C输出:用
fprintf(stderr, ...)替代C++的std::cerr,符合C语言规范。 - 依赖补充:添加
windows.h以使用GetModuleFileName和MAX_PATH。 - 语法修复:去掉宏定义末尾的多余分号,补充主函数的返回值
0。
使用说明
- 调试编译时,不要定义
NDEBUG宏,此时断言会生效,失败时输出自定义格式。 - 发布编译时,添加
-DNDEBUG(GCC/Clang)或在项目设置中定义NDEBUG(MSVC),断言会被完全移除,不影响程序性能。
内容的提问来源于stack exchange,提问作者Assel
相关产品推荐
相关产品推荐

