一、修饰变量的关键字

a. static(静态关键字,高频重点)

修饰对象 核心作用 存储位置 生存周期 作用域
局部变量 延长生存周期,仅初始化一次 未初始化:bss 段(默认 0);已初始化:data 段 进程终止 当前函数
全局变量 限制作用域,避免重名 data 段 /bss 段 进程终止 本文件
函数 限制调用范围,隐藏实现 代码区 进程终止 本文件
局部静态变量示例(可直接复制运行)
#include <stdio.h>
void test() {
    int normal = 0; // 普通局部变量,栈区,函数结束释放
    static int stat = 0; // 局部静态变量,data段,进程终止释放
    normal++;
    stat++;
    printf("普通局部变量:%d | 局部静态变量:%d\n", normal, stat);
}
int main() {
    test(); // 普通局部变量:1 | 局部静态变量:1
    test(); // 普通局部变量:1 | 局部静态变量:2
    test(); // 普通局部变量:1 | 局部静态变量:3
    return 0;
}

易错点:局部静态变量仅初始化一次,作用域仍局限于当前函数,不能跨函数访问。

b. auto(自动变量)

核心属性 具体说明
修饰对象 仅局部变量(不能修饰全局 / 静态变量)
默认特性 局部变量默认修饰关键字,可省略不写
存储位置 栈区
生存周期 函数执行期间,函数结束后自动释放

示例:int a = 10; 等价于 auto int a = 10;(日常开发中均省略 auto)。

c. volatile(易失变量,进阶)

核心属性 具体说明
核心含义 变量的值可能被程序外部因素(硬件、中断、多线程)修改
核心作用 防止编译器优化,强制每次使用时从内存读取(而非寄存器缓存)
适用场景 多线程编程、中断处理、硬件寄存器操作
易错点 不能省略,否则编译器优化会导致读取到旧值

d. register(寄存器变量)

核心属性 具体说明
核心含义 提示编译器将变量存储到 CPU 寄存器中
寄存器特性 32bit(4bytes),读写速度远快于内存,数量有限、有特定用途
实际用途 软件开发几乎无需手动添加,编译器会自动优化(将频繁使用的变量放入寄存器)
关键禁忌 不能取地址(&),因为寄存器无内存地址

e. const(只读变量,高频重点)

修饰场景 语法示例 核心限制
普通变量 const int a = 10; 初始化后不能修改,保护数据不被误操作
指针(指向内容不可改) const int *p = &a; 指针可指向其他地址,指向的内容不能改
指针(指针本身不可改) int *const p = &a; 指针不能指向其他地址,指向的内容可以改
指针(两者均不可改) const int *const p = &a; 指针和指向的内容均不能改
示例代码(可直接运行)

<stdio.h>

int main() {
    int a = 10, b = 20;
    const int *p1 = &a; // 指向内容不可改
    int *const p2 = &a; // 指针本身不可改
    const int *const p3 = &a; // 两者均不可改

    // *p1 = 20; // 错误:指向内容不可改
    p1 = &b; // 正确:指针可改
    *p2 = 20; // 正确:指向内容可改
    // p2 = &b; // 错误:指针本身不可改
    // *p3 = 20; p3 = &b; // 均错误
    return 0;
}

易错点:const 修饰的普通变量是 “只读变量”(非常量),不能用于定义数组大小(如const int n=10; int arr[n]; 语法错误)。

f. extern(外部声明,多文件必备)

核心属性 具体说明
核心作用 声明其他文件中定义的全局变量 / 函数,让本文件可使用
关键区别 仅声明,不分配内存;定义会分配内存(extern 不能用于定义)
适用场景 多文件编程,调用其他文件的全局变量 / 函数
易错点 声明时不能赋值(如extern int a = 10; 是定义,非声明)
多文件示例(可直接复制运行)
// test1.c(定义全局变量)
int g_num = 100; // 全局变量定义,分配内存

// test2.c(使用全局变量<stdio.h>
extern int g_num; // 外部声明,不分配内存
int main() {
    printf("g_num = %d\n", g_num); // 输出100,正常调用
    return 0;
}

二、递归(函数进阶)

递归核心要点

核心要素 具体说明
定义 函数体内调用函数本身,解决 “重复子问题” 的常用方法
必备条件 1 终止条件(递归出口):否则会陷入死循环
必备条件 2 递归点:将原问题拆解为规模更小的同类子问题
注意事项 递归深度过深会导致栈溢出(建议深度不超过 1000 层)

示例 1:求 n 的前 n 项和(1+2+3+...+n)

递归思路
  1. 原问题:sum(n) = n + sum(n-1)(n 的前 n 项和 = n + n-1 的前 n-1 项和)
  2. 子问题:sum(n-1) = (n-1) + sum(n-2),以此类推
  3. 终止条件:sum(0) = 0(0 的前 n 项和为 0)
代码示例(可直接运行)

<stdio.h>

int sum(int n) {
    if(n == 0) return 0; // 终止条件(递归出口)
    return n + sum(n-1); // 递归点(拆解子问题)
}
int main() {
    int n = 10;
    printf("1到%d的和:%d\n", n, sum(n)); // 输出55
    return 0;
}

示例 2:求 n 的逆序数(递归实现)

代码示例(可直接运行,支持负数)
<stdio<math.h>

// 辅助函数:计算数字的位数
int get_digit (int n) 
{
    if (n == 0) 
        return 1;
    int count = 0;
    while (n != 0) 
        { 
            count++; 
            n /= 10; 
        }
        return count;
    }

// 递归求逆序数
int reverse (int n) 
{
    if (n == 0) 
    return 0; // 终止条件
    // 递归点:个位 * 10^(剩余位数) + 剩余部分逆序数
    return (n%10) * pow (10, get_digit (n/10)) + reverse (n/10);
}

int main () 
{
    int n;
    printf ("请输入一个整数:");
    scanf ("% d", &n);
    int sign = n < 0 ? -1 : 1; 
    // 处理负数,保留符号
    printf ("逆序数:% d\n", sign * reverse (sign * n));
    // 测试用例:输入 1234→4321,输入 - 123→-321,输入 0→0return 0;
}

## 三、变参函数(参数可变的函数)
### 核心要点
| 核心属性 | 具体说明 |
| :------- | :------- |
| 典型示例 | scanf()、printf()(C语言内置变参函数) |
| 函数原型 | `int printf(const char *format, ...);`(`...`表示可变参数列表) |
| 参数决定 | 第一个参数(格式控制串)决定后续参数的个数和类型 |
| 底层依赖 | <stdarg.h>`(自定义变参函数必备) |

### 变参函数参数个数判断示例
| 第一个参数(format) | 参数个数 | 说明 |
| :------------------- | :------- | :--- |
| "hello" | 1个 | 无格式控制符,仅1个参数 |
| "hello %d" | 2个 | 1个%d,需额外传递1个int参数 |
| "hello %d world %c" | 3个 | 1个%d、1个%c,需额外传递2个参数 |

### 自定义变参函数示例(求n个整数的和,可直接运行)
```c
<stdio.h>
<stdarg.h>

// 自定义变参函数:第一个参数n表示后续int参数的个数
int add_n(int n, ...) {
    va_list arg; // 定义可变参数列表
    va_start(arg, n); // 初始化,从n的下一个参数开始
    int sum = 0;
    for(int i =< n; i++) {
        sum += va_arg(arg, int); // 依次获取可变参数(类型为int)
    }
    va_end(arg); // 释放可变参数列表
    return sum;
}

int main() {
    printf("1+2+3 = %d\n", add_n(3, 1, 2, 3)); // 输出6
    printf("10+20+30+40 = %d\n", add_n(4, 10, 20, 30, 40)); // 输出100
    return 0;
}

四、函数的传参顺序

核心要点

传参顺序 具体说明 示例验证
核心结论 C 语言函数传参顺序:从右向左 见下方代码,验证传参顺序
易错点 避免在传参时使用自增(++)、自减(--)运算符,易导致结果异常
示例验证(可直接运行)
#include <stdio.h>
void test(int a, int b) {
    printf("a = %d, b = %d\n", a, b);
}
int main() {
    int x = 10;
    // 传参顺序:先算b = x++(x变为11,b=10),再算a = ++x(x变为12,a=12)
    test(++x, x++); 
    return 0; // 输出:a=12, b=10(验证从右向左传参)
}

五、预处理指令(# 开头,编译前执行)

预处理指令核心分类

指令类型 具体指令 核心作用
宏定义 #define 文本替换(宏常量、宏函数)
条件编译 #ifdef/#ifndef/#if 0 按条件编译代码,避免重复包含、注释代码块
内置宏 LINE/__FUNCTION__等 调试用,获取行号、函数名、编译时间等
特点 以 #开头,无分号,预处理阶段执行 不参与代码执行,仅做代码替换 / 筛选

b. #define 宏定义(最常用)

1. 宏常量
语法 示例 核心作用 易错点
#define 宏名 常量值 #define N 100 程序中所有 N 替换为 100,便于统一修改 末尾不要加分号(否则会一起替换)
2. 宏函数(模拟函数)
核心特点 具体说明
适用场景 函数逻辑简单、语句少,追求执行效率(无函数调用开销)
多条语句处理 用续行符 \ 换行,或用 do {} while (0); 包裹(避免语法冲突)
示例 见下方代码,包含单条 / 多条语句宏函数
宏函数示例(可直接运行)
<stdio.h>
// 1. 单条语句宏函数:求两个数的最大值(加括号避免优先级问题)
#define MAX(a,b) ((a)>(b)?(a):(b))

// 2. 多条语句宏函数:打印两个数的和与差(用do-while包裹)
#define PRINT_SUM_DIFF(a,b) do { \
    int sum = a + b; \
    int diff = a - b; \
    printf("和:%d,差:%d\n", sum, diff); \
} while(0)

int main() {
    printf("最大值:%d\n", MAX(10, 20)); // 输出20
    PRINT_SUM_DIFF(15, 5); // 输出:和:20,差:10
    return 0;
}
3. 宏函数与普通函数的区别(面试高频)
对比维度 宏函数 普通函数
执行开销 小,纯文本替换,无需创建栈帧 大,需创建栈帧、保存返回地址,有调用开销
参数类型 无类型检查,可接收任意类型参数(易出错) 有明确类型,编译器做类型校验(更安全)
优先级问题 存在,需给参数和整体加括号 无,按函数逻辑正常执行
功能复杂度 不适合复杂功能,仅适合简单语句 无限制,可实现任意复杂功能
执行效率 高(无调用开销) 低(有调用和栈维护开销)

c. 条件编译(重点)

1. 头文件保护(避免重复包含,最常用)
实现方式 语法示例 核心作用
方式 1(兼容所有编译器) #ifndef#define XXX_H// 函数声明#endif 头文件被重复包含时,仅展开一次,避免重定义错误
方式 2(简洁,主流编译器支持)// 函数声明、类型定义 与方式 1 作用一致,写法更简洁
示例(mylib.h 头文件,可直接复制使用)
#ifndef __MYLIB_H__
#define __MYLIB_H__

// 函数声明(示例)
int is_leap_year(int year);
int get_days(int year, int month);

#endif // __MYLIB_H__
2. 注释代码块(#if 0 ... #endif)
语法 核心作用 优势
// 被注释的代码printf (" 这#endif 注释大块代码,预处理阶段忽略 可嵌套,比 /* / 更灵活(不会因内部有 / */ 失效)
补充
  • #if 0:条件为假,代码不编译(注释);
  • #if 1:条件为真,代码正常编译(快速启用某段代码)。

d. 系统内置宏(直接使用,调试必备)

内置宏 核心作用 示例输出
LINE 输出当前代码的行号(整数) 15
FUNCTION 输出当前所在的函数名(字符串) main
TIME 输出代码编译的时间(格式:hh:mm:ss) 14:30:25
DATE 输出代码编译的日期(格式:MMM DD YYYY) Jul 01 2026
示例(可直接运行,调试用)

<stdio.h>

void test() {
    printf("当前行号:%d\n", __LINE__);
    printf("当前函数名:%s\n", __FUNCTION__);
    printf("代码编译时间:%s\n", __TIME__);
    printf("代码编译日期:%s\n", __DATE__);
}
int main() {
    test();
    return 0;
}

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐