一、修饰变量的关键字
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)
递归思路
- 原问题:
sum(n) = n + sum(n-1)(n 的前 n 项和 = n + n-1 的前 n-1 项和)
- 子问题:
sum(n-1) = (n-1) + sum(n-2),以此类推
- 终止条件:
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;
}
所有评论(0)