关于C90标准中未识别预处理指令处理规则的技术问询
你在自制C90预处理器时遇到的这个未识别预处理指令的问题,刚好戳中了C90和后续C标准(比如C23)在预处理规则上的一个关键差异,我来帮你把标准要求和实现逻辑理清楚:
首先明确C90的核心规则:
在C90(ISO/IEC 9899:1990)的预处理语法定义里,
group-part(预处理单元的组成部分)完全没有后续标准里的non-directive或text-line概念。它只允许三种元素:
- 可选的
pp-tokens(一个或多个预处理标记,包括#这类标点/操作符)- 换行符
- 合法的
if-section或control-line(这两类都必须以#开头,且是标准明确定义的预处理指令,比如#define、#ifdef之类)
回到你的核心疑问:能不能把# hi这种行拆成#标记+hi标识符,像处理+ hi、; hi那样保留到预处理输出里?
答案是绝对不行——这违反了C90的预处理语法规则:
当预处理器看到一行以#开头时,它只能尝试将其解析为标准定义的control-line或if-section的一部分,这是C90对#起始行的唯一合法解析路径。如果解析失败(比如# hi不是任何标准指令),这属于预处理语法错误,C90标准强制要求实现必须对这类错误进行诊断(也就是输出警告或错误提示)。
再看你测试的GCC和Clang的行为:
你用-std=c90 -pedantic -E测试时,两个编译器都抛出"invalid preprocessing directive"错误,这完全符合C90的要求——标准只要求必须诊断错误,至于是否停止处理(比如GCC不生成输出文件)属于实现定义的行为,但诊断是硬性要求。
而@ hi的情况和这个完全不同:@不是预处理指令的起始标记,预处理器会把它当作普通的预处理标记拆分出来(虽然@在C90里不是合法的标识符或操作符,但预处理器阶段只负责拆分标记,不检查标记的语义合法性,所以会原封不动保留到输出里,直到编译阶段才会报错)。
最后给你自制预处理器的明确建议:
如果你要做一个严格符合C90标准的预处理器,遇到# hi这类行时:
- 必须输出诊断信息(警告或错误)
- 可以选择停止处理,或者继续处理(但继续的行为属于实现定义,建议优先选择报错停止,符合用户预期)
- 绝对不能把它当作普通标记保留到输出里——这会违反C90的预处理语法规则。
内容来源于stack exchange

