目录一、故事开场你给自己取了个名字二、标识符——你给东西取的名字定义起名规则必须遵守否则编译器报错起名建议不会报错但会被人骂三、关键字——C语言自己保留的禁词定义C语言标准关键字清单C99/C11共37个用关键字起名会怎样四、预定义标识符——站在巨人的肩膀上定义常见的预定义标识符和标识符、关键字的区别五、三角关系对比一张表说清一句话记忆法六、实战踩坑经验坑1在STM32项目里重新定义了标准库函数坑2在头文件里误用了关键字坑3下划线开头的标识符坑4main 其实是个预定义标识符七、总结一、故事开场你给自己取了个名字假设你转学到了一个新班级。开学第一天老师说每个人给自己取一个英文名方便大家叫你。你给自己取了Alex。但老师接着说有几个名字不能取——班长、校长、班主任。这些是固定的职位称呼你不能拿来当自己的名字。接着你又发现班里有一些公共称呼值日生、课代表——谁当了谁用不专属于某个人。这个场景恰好对应了C语言里的三个基本概念班级场景C语言概念你给自己取名Alex标识符——你自己起的名字不能用的名字班长、校长关键字——C语言保留的词公共称呼值日生、课代表预定义标识符——系统预置的通用名字是不是一下子就通了下面一个个展开说。二、标识符——你给东西取的名字定义标识符Identifier就是你给变量、函数、结构体、宏等起的名字。写C代码的时候你几乎每时每刻都在起名int led_pin 13; // led_pin 是标识符 void delay_ms(int t) { ... } // delay_ms 是标识符, t 也是标识符 #define BUTTON_PIN 12 // BUTTON_PIN 是标识符 struct SensorData { ... }; // SensorData 是标识符起名规则必须遵守否则编译器报错规则说明✅ 正确❌ 错误只能包含字母、数字、下划线不能有空格、、#、$等特殊符号led_pinled pin不能以数字开头必须以字母或下划线开头pin11pin不能是关键字不能和C语言保留字重名my_intint大小写敏感LED和led是两个不同的标识符——起名建议不会报错但会被人骂虽然编译器只检查上面的硬规则但写代码是给人看的建议习惯养成int a; // ❌ 鬼知道 a 是什么意思 int led_pin; // ✅ 一看就知道是LED引脚 int timer_count; // ✅ 清清楚楚 void func1(); // ❌ 哪个func1干啥的 void uart_send(); // ✅ 明确说是串口发送嵌入式开发小贴士STM32的HAL库里大量使用匈牙利命名法和下划线风格比如HAL_GPIO_WritePin()。跟着项目风格走不要自创一套。三、关键字——C语言自己保留的禁词定义关键字Keywords是C语言标准里规定好、有特殊含义的词你不能拿来当标识符。这些词是C语言的语法砖块——编译器拿它们来识别程序结构。C语言标准关键字清单C99/C11共37个按功能分类看着更清晰数据类型8个char short int long float double signed unsigned控制语句12个if else switch case default for while do break continue goto return存储类型5个auto register static extern const类型定义3个typedef struct union enum // 4个加上面共12 // 准确说是这些 typedef struct union enum sizeof void volatile标准C关键字完整清单按字母顺序方便查阅auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile whileC99新增inline、_Bool、_Complex、_Imaginary用关键字起名会怎样int int 5; // ❌ 编译错误int 是关键字不能用 float return; // ❌ 编译错误return 是关键字编译器直接甩一个语法错误。这就好比你在班级里非要给自己取名叫校长——老师说不行。嵌入式相关的常见关键字关键字在STM32开发中的典型用法volatile__IO宏展开就是这个告诉编译器这个变量可能被中断/硬件修改别优化static限制函数的作用域在当前文件或让局部变量在两次调用间保持值const定义只读数据比如查表防止意外修改extern声明在其他文件中定义的全局变量比如 stm32f4xx.h 用这个链接寄存器地址踩坑经验刚学的时候最容易误用void——void main()在某些编译器下可以通过但标准C不允许。正确的写法是int main(void)。四、预定义标识符——站在巨人的肩膀上定义预定义标识符Predefined Identifiers是编译器或标准库已经声明好的名字你可以用但不能重新定义或者说重新定义会有严重后果。它们跟关键字的区别在于关键字你不能拿来用预定义标识符你可以用但不建议自己再定义一个同名的。常见的预定义标识符标准库函数名printf scanf malloc free memset memcpy strlen这些名字已经在stdio.h、stdlib.h、string.h等头文件里声明过了。你如果自己在代码里写int printf 5; // ❌ 编译能过但链接器报错或者覆盖了库函数STM32 HAL库里的预定义标识符HAL_GPIO_WritePin() // HAL库的GPIO写函数 HAL_UART_Transmit() // HAL库的串口发送函数 TIM2 // 定时器2的外设句柄 GPIOA // GPIOA端口这些名字是ST公司写HAL库时已经定义好的。你在main.c里如果写int HAL_GPIO_WritePin; // ❌ 语法上允许但会导致链接错误编译器不会报错因为它不是关键字但链接器会懵你到底想用我的HAL_GPIO_WritePin还是自己的那个intC语言预定义的宏编译器和语言标准内置的__FILE__ // 当前文件名 __LINE__ // 当前行号 __DATE__ // 编译日期 __TIME__ // 编译时间 __func__ // 当前函数名C99起这些也是预定义标识符的一种——你没法改它们的值但可以直接拿来用printf(发生错误文件%s行号%d\n, __FILE__, __LINE__); // 输出发生错误文件main.c行号42这在嵌入式调试中非常实用——程序崩溃时通过串口打印出错的文件和行号定位问题快得多。和标识符、关键字的区别概念你能用它命名吗你能修改它的含义吗关键字❌ 绝对不行❌ 本来就是语法的一部分预定义标识符✅ 语法允许但强烈不建议❌ 改了会导致严重问题普通标识符✅ 随便取✅ 按你的需求来五、三角关系对比一张表说清对比维度关键字预定义标识符用户自定义标识符谁定义的C语言标准编译器/标准库/框架你程序员能否用作变量名❌ 编译器直接报错⚠️ 语法允许但后果严重✅ 随便用能否修改含义❌❌改了会出问题✅数量固定C89:32个C99:37个随库函数增加而增加无限只要符合规则典型例子intifreturnprintfmallocHAL_GPIO_WritePinled_pinmy_func新手常见错误拿关键字当变量名拿库函数名当变量名取名太随意abtmp一句话记忆法关键字不能碰——编译器拦着你不让你用。预定义标识符可以碰但别碰——语法上允许但后果自负。自定义标识符大胆用——但在用之前想清楚这个名字别人看得懂吗。六、实战踩坑经验坑1在STM32项目里重新定义了标准库函数// ❌ 错误示范 char* memset; // 声明了一个名叫 memset 的变量然后你在别处调用了memset(buffer, 0, 100)——链接器报错说找不到memset的函数定义。排查了半天才发现是变量声明把函数名给挡住了。坑2在头文件里误用了关键字// ❌ 错误示范 #define int my_int // 试图把 int 替换成 my_int预处理器会先处理#define然后编译器看到的是my_int my_var;——这倒不会报错但你所有用到int的地方全废了。这种宏定义是灾难性的。坑3下划线开头的标识符C语言标准规定以下划线开头的标识符通常保留给编译器/标准库使用。int _my_var; // ⚠️ 语法允许但建议不要这样写 int __my_var; // ❌ 双下划线开头的绝对不要用在STM32的HAL库里你经常看到__HAL_RCC_GPIOA_CLK_ENABLE()—— 这是ST公司自己用的他们作为库开发者可以用。你要写自己的应用代码别用下划线开头。坑4main其实是个预定义标识符// ❌ 虽然语法上main不是关键字但你不能在别处用main当变量名 int main; // 编译能过但链接器会疯掉main是C语言规定的程序入口它有特殊的地位。严格来说它是预定义标识符而不是关键字因为它可以出现在某些表达式中而不报错。但请你把它当成关键字来看待——别用它命名其他东西。七、总结回到开头的班级比喻标识符≈ 你自己取的名字 → 大胆取但要取得好关键字≈ 校长班长这些禁词 → 编译器不让你用预定义标识符≈ 值日生课代表 → 你可以用但你最好别用来命名自己的东西否则整个班就乱套了给初学者的三个习惯建议起名要有意义——cnt比c好timer_overflow_count比cnt更好不要挑战关键字——编译器报错了别问为什么就是不能这么用不要覆盖库函数——你觉得printf这个名字好但C标准库已经用了最后送一句起个好名字代码就成功了一半。另一半是别踩上面那些坑。