关于Keil编译器使用C90的说明及如何切换成C99规则
前言
前几天打开keil编译器编译了一个代码,提示我的一个变量没有定义,我写了如下的代码:
int ret = loop_task();提示我的ret没有定义,后面仔细研究了一下发现是keil编译器默认的编译规则问题,keil默认使用C90的规则,所有变量在使用前就已经定义完成,这样可以提升编译器的编译效率,随用随定义的规则是C99引入的,代码灵活,但是编译效率不如C90规则下的,但是就习惯性来说,C99下的规则更符合代码编写逻辑,大部分工程师开发的代码都是基于C99规则下的,如果C90规则不习惯,我们可以修改KEIL的默认编译规则为C99下的。
原因分析
1. C90 标准限制
Keil 默认使用的 C90 标准(ANSI C 1990) 要求:
变量必须在代码块({})的开头声明,不能在中间定义变量。
这样可以让编译器在编译时提前知道所有变量,提高编译效率。
C99 标准(ANSI C 1999) 允许变量在任意位置声明,但 Keil 默认不支持该特性。
2. Keil 编译器(如 armcc 或 C51)默认遵循 C90
例如在 Keil C51(用于 8051)或 Keil MDK-ARM(用于 STM32)中,默认编译器是 armcc,它默认遵循 C90。
在 C90 规则下,下面的代码会报错:
c
int main() {
int a = 10;
a = a + 1; // ✅ 正确,变量已提前声明
int b = a + 5; // ❌ 错误!C90 不允许在中间定义变量
return 0;
}
正确写法(C90 兼容):
int main() {
int a = 10;
int b; // 变量必须在代码块开头声明
a = a + 1;
b = a + 5; // ✅ 这样就符合 C90 规范
return 0;
}
解决方案:启用 C99 支持
如果想在 Keil 中使用 边定义边使用,可以尝试开启 C99 或更新标准:
Keil MDK-ARM(ARM 编译器):
在 Keil uVision 中,进入 Project -> Options for Target -> C/C++ 选项卡。
找到 Language Standard,选择 C99 Mode 或 gnu99 Mode。
Keil C51(8051 编译器):
由于 Keil C51 仍然基于旧版 C 标准,不支持 C99,需要手动调整代码结构,先声明变量再使用。
C90和C99区别
在Keil中使用 C90 和 C99 编译标准时,编译效率的区别主要体现在 代码结构、内存管理、编译器优化 以及 语言特性 上。我们可以从多个角度来分析这两者的编译效率差异。
特性 | C90 | C99 |
---|---|---|
变量声明位置 | 必须在函数体开始时声明所有变量 | 允许在函数体中的任意位置声明变量 |
内联函数 | 不支持内联函数 | 支持 inline 关键字用于声明内联函数 |
布尔类型 | 没有 bool 类型,通常用 int 模拟 | 支持 bool 类型,定义在 <stdbool.h> 头文件中 |
复合字面量 | 不支持复合字面量 | 支持复合字面量,如 {1, 2, 3} |
变长数组(VLA ) | 不支持变长数组 | 支持变长数组,数组的大小可以在运行时动态决定 |
单一声明 | 不允许单一声明多个变量 | 支持单一声明多个变量 |
结构体类型的限定符 | 结构体成员必须单独声明 | 支持结构体成员的类型限定符(例如 const) |
限制符 | 对于 long long 类型的支持有限 | long long 类型得到正式支持,表示 64 位整数 |
函数声明方式 | 函数声明必须在函数定义之前 | 允许函数声明后定义(或直接在同一个文件中定义) |
支持的库功能 | 不支持动态分配的内存和部分标准库功能 | 引入了 <stdbool.h>、<tgmath.h>、<inttypes.h> 等 |
宏定义常量 | 宏常量和常量表达式需通过 #define 定义 | 宏常量和常量表达式支持 const 或 enum 作为替代 |
宽字符类型 | 没有 wchar_t 和 wchar 类型 | 支持 wchar_t 类型(用于宽字符) |
条件编译 | #if 和 #ifdef 仅支持常量表达式 | 支持 #if 和 #ifdef 以及常量表达式和 sizeof |
类型限定符 | 不支持 restrict 关键字 | 支持 restrict 关键字,优化指针别名消除 |
指针算术的范围检查 | 不做严格检查 | 对指针算术进行了更严格的范围检查 |
变量初始化 | 需要在数组或结构体中逐项赋值 | 支持结构体和数组的 指定初始化,即可以不按照顺序赋值 |
编译选项 | 对编译器的要求相对简单,兼容老旧编译器 | 编译器对 C99 特性有更高要求,需要支持 C99 模式 |
注意:
如果基于C90规则开发的代码在C99编译器中是可以正常编译的,但是如果基于C99规则开发的代码,在C90编译器中是不支持的,可能会有特性不支持造成错误