什么是标识符、关键字和预定义标识符?三者的区别,看完这篇再也不会搞混
2026/6/9 2:17:19 网站建设 项目流程

目录

一、故事开场:你给自己取了个名字

二、标识符——你给东西取的名字

定义

起名规则(必须遵守,否则编译器报错)

起名建议(不会报错但会被人骂)

三、关键字——C语言自己保留的"禁词"

定义

C语言标准关键字清单(C99/C11共37个)

用关键字起名会怎样?

四、预定义标识符——站在巨人的肩膀上

定义

常见的预定义标识符

和标识符、关键字的区别

五、三角关系对比(一张表说清)

一句话记忆法

六、实战踩坑经验

坑1:在STM32项目里重新定义了标准库函数

坑2:在头文件里误用了关键字

坑3:下划线开头的标识符

坑4:main 其实是个预定义标识符

七、总结


一、故事开场:你给自己取了个名字

假设你转学到了一个新班级。

开学第一天,老师说:"每个人给自己取一个英文名,方便大家叫你。"

你给自己取了"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
大小写敏感LEDled是两个不同的标识符

起名建议(不会报错但会被人骂)

虽然编译器只检查上面的硬规则,但写代码是给人看的,建议习惯养成:

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 while

C99新增: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还是自己的那个int?

C语言预定义的宏(编译器和语言标准内置的):

__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公司自己用的,他们作为库开发者可以用。你要写自己的应用代码,别用下划线开头。

坑4:main其实是个预定义标识符

// ❌ 虽然语法上main不是关键字,但你不能在别处用main当变量名 int main; // 编译能过,但链接器会疯掉

main是C语言规定的程序入口,它有特殊的地位。严格来说它是预定义标识符,而不是关键字(因为它可以出现在某些表达式中而不报错)。但请你把它当成关键字来看待——别用它命名其他东西。


七、总结

回到开头的班级比喻:

  • 标识符≈ 你自己取的名字 → 大胆取,但要取得好
  • 关键字≈ "校长""班长"这些禁词 → 编译器不让你用
  • 预定义标识符≈ "值日生""课代表" → 你可以用,但你最好别用来命名自己的东西,否则整个班就乱套了

给初学者的三个习惯建议:

  1. 起名要有意义——cntc好,timer_overflow_countcnt更好
  2. 不要挑战关键字——编译器报错了别问为什么,就是不能这么用
  3. 不要覆盖库函数——你觉得printf这个名字好,但C标准库已经用了

最后送一句:

起个好名字,代码就成功了一半。另一半是别踩上面那些坑。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询