#error 和 #warning
#error
#error 指令用于在编译时显示指定的错误信息,使得程序 ill-formed,然后中止编译。
它常用于“前提检查”,例如编译器版本不满足、目标平台不在支持列表、关键宏缺失等。与运行时检查不同,#error 会把问题提前到构建阶段暴露出来。
#warning
#warning 指令用于在编译时显示指定的警告信息,不影响程序的有效性,继续编译。
这里需要提一嘴:#warning 在 C23 才进入标准;更早版本里它通常是编译器扩展。若项目要求严格 C11/C17 兼容,可改用条件编译 + 注释提示,或使用构建系统输出警告。
适用场景
#error 适合“必须阻止构建继续”的约束,#warning 适合“可以继续,但希望显式提醒”的约束。二者都属于预处理阶段能力,不依赖程序运行路径。
示例
#if __STDC__ != 1
# error "Not a standard compiler"
#endif
#if __STDC_VERSION__ >= 202311L
# warning "Using C23"
#endif
#include <stdio.h>
int main(void) {
printf("Hello, world!\n");
return 0;
}2
3
4
5
6
7
8
9
10
11
12
13
14
可能的输出(示例):
<输出与输入或平台相关,请以实际运行为准>
组合使用建议
一个常见写法是“硬约束用 #error,软约束用 #warning”。例如:不支持的平台直接拒绝构建;可选优化特性缺失时只提醒并回退到兼容路径。这样既能保证正确性底线,也能保留构建弹性。
消息内容建议
#error / #warning 的消息最好写成“条件 + 建议动作”的形式,例如“需要 C23,请在构建选项中启用 -std=c23”。这样构建日志不仅告诉你哪里失败,还能直接给出修复路径,排错速度会快很多。
兼容回退写法
当需要兼容旧标准且又想保留提醒效果时,可把 #warning 放在特性判断分支里,并为不支持该指令的环境提供无副作用回退路径。核心思路是:硬失败始终由 #error 保证,提醒信息可按实现能力降级。
分层诊断建议
把诊断分成“配置层”和“源码层”通常更清楚:目标平台、标准版本、ABI 前提放在集中配置头里用 #error/#warning 管控,业务源码文件只消费配置结果。这样可以避免同一条约束散落在多个文件重复判断,也能让失败信息更加统一。
条件守卫顺序建议
诊断条件通常应先判断“能力是否可见”,再判断“当前配置是否合法”。例如先确认某特性宏是否存在,再决定是否触发 #error 或 #warning。把守卫顺序写清楚后,可以减少因宏未定义导致的二次报错,让构建日志更聚焦。
#if !defined(APP_CFG_READY)
# error "APP_CFG_READY is required"
#endif
#if defined(APP_USE_SIMD) && !defined(APP_HAS_SIMD)
# warning "SIMD requested but not available; fallback enabled"
#endif2
3
4
5
6
7
运行结果:该代码块主要用于语法或结构说明,单独运行通常无终端输出。
诊断信息应保持稳定可检索
同一类失败建议使用稳定前缀或统一措辞,这样日志平台和 CI 规则可以直接按关键字聚合问题。#error / #warning 的价值不只在“让本次构建停下或提醒”,更在于长期积累时能快速定位同类故障源。