前置知识: C

变量与常量

00:00
6 min Beginner

变量的定义、生命周期、作用域以及常量的多种实现方式。

1. 变量 (Variables)

变量是存储数据的具名内存空间,用于在程序运行过程中存储和操作数据。

1.1 定义与初始化 (Definition & Initialization)

1.1.1 基本定义与初始化

 // 声明变量(分配内存但未初始化)
 int a; // 局部变量,值为随机值
 // 赋值
  a = 10; // 将 10 赋给变量 a
 // 声明并初始化
 int b = 20; // 声明变量 b 并初始化为 20
 // 多变量声明
 int x, y = 5; // y 被初始化为 5,x 未初始化(值为随机)
 // 多变量初始化
 int m = 10, n = 20, p = 30; // 同时声明并初始化多个变量

1.1.2 初始化注意事项

 // 未初始化的局部变量
 int uninitialized; // 值为随机,使用前必须初始化
 // 全局变量会被自动初始化为 0
 int global_var; // 自动初始化为 0
 // 静态变量也会被自动初始化为 0
 static int static_var; // 自动初始化为 0

1.2 变量的作用域 (Scope)

1.2.1 作用域类型

  • 局部变量 (Local): 定义在函数或代码块 {} 内部,仅在定义它的函数或代码块内有效
  • 全局变量 (Global): 定义在所有函数之外,在整个程序的生命周期内有效
  • 形式参数 (Formal Parameters): 函数定义中的参数,作用域仅限于函数内部

1.2.2 作用域示例

 // 全局变量
 int global_var = 100;
 void function1() {
  // 局部变量
  int local_var = 50;
  // 可以访问全局变量
  printf("Global var: %d\n", global_var);
  printf("Local var: %d\n", local_var);
 }
 void function2(int param) {
  // 形式参数
  printf("Parameter: %d\n", param);
  // 可以访问全局变量
  printf("Global var: %d\n", global_var);
  // 不能访问 function1 中的局部变量
  // printf("Local var from function1: %d\n", local_var); // 错误!
 }
 int main() {
  function1();
  function2(200);
  return 0;
 }

1.2.3 作用域嵌套

 int main() {
  int outer = 10;
  {
  int inner = 20;
  printf("Outer: %d, Inner: %d\n", outer, inner); // 可以访问外部变量
  // 覆盖外部变量
  int outer = 30;
  printf("Inner outer: %d, Inner: %d\n", outer, inner); // 访问内部的 outer
  }
  // 不能访问内部变量
  // printf("Inner: %d\n", inner); // 错误!
  printf("Outer: %d\n", outer); // 访问外部的 outer
  return 0;
 }

1.3 存储类 (Storage Classes)

1.3.1 存储类概述

存储类作用域生命周期初始化值说明
auto局部块结束随机值默认存储类
static局部或文件程序结束0保持值,限制访问
extern全局程序结束0引用外部变量
register局部块结束随机值建议存储在寄存器

1.3.2 auto 存储类

 void function() {
  auto int x = 10; // 显式声明为 auto
  int y = 20; // 隐式为 auto
  // 函数结束后,x 和 y 被销毁
 }

1.3.3 static 存储类

 // 全局静态变量:仅限当前文件访问
 static int file_static = 100;
 void counter() {
  // 局部静态变量:函数结束后不销毁
  static int count = 0;
  count++;
  printf("Count: %d\n", count);
 }
 int main() {
  counter(); // 输出: Count: 1
  counter(); // 输出: Count: 2
  counter(); // 输出: Count: 3
  return 0;
 }

1.3.4 extern 存储类

 // file1.c
 extern int global_var; // 声明外部变量
 void print_global() {
  printf("Global var: %d\n", global_var);
 }
 // file2.c
 int global_var = 50; // 定义全局变量
 int main() {
  print_global(); // 输出: Global var: 50
  return 0;
 }

1.3.5 register 存储类

 void fast_calculation() {
  register int i; // 建议将 i 存储在寄存器中
  for (i = 0; i < 1000000; i++) {
  // 大量计算,使用 register 可能提高速度
  }
 }

2. 常量 (Constants)

常量是在程序运行期间其值不可更改的量,用于存储固定不变的数据。

2.1 字面常量 (Literals)

2.1.1 整数常量

 100 // 十进制
 0x1F // 十六进制 (31)
 010 // 八进制 (8)
 0b1010 // 二进制 (10,C99 及以上支持)
 // 后缀
 100U // 无符号整数
 100L // 长整数
 100UL // 无符号长整数
 100LL // 长 long 整数

2.1.2 浮点常量

 3.14 // 双精度浮点数
 3.14f // 单精度浮点数
 3.14L // 长双精度浮点数
 2.5e-3 // 科学记数法 (0.0025)

2.1.3 字符常量

 'A' // 普通字符
 '\n' // 换行符
 '\t' // 制表符
 '\\' // 反斜杠
 '\0' // 空字符

2.1.4 字符串常量

 "Hello C" // 字符串常量
 "Line 1\nLine 2" // 多行字符串

2.2 宏定义常量 (#define)

2.2.1 基本用法

 // 定义常量
 #define MAX_BUFFER 1024
 #define PI 3.14159
 #define MESSAGE "Hello World"
 // 使用常量
 int buffer[MAX_BUFFER];
 double area = PI * radius * radius;
 printf("%s\n", MESSAGE);

2.2.2 带参数的宏

 // 定义带参数的宏
 #define MAX(a, b) ((a) > (b) ? (a) : (b))
 #define SQUARE(x) ((x) * (x))
 // 使用宏
 int max_val = MAX(10, 20); // 结果: 20
 int square_val = SQUARE(5); // 结果: 25

2.2.3 宏的注意事项

 // 宏只是文本替换,要注意括号
 #define MULTIPLY(a, b) a * b
 // 问题:MULTIPLY(2 + 3, 4) 会被替换为 2 + 3 * 4 = 14,而不是 (2 + 3) * 4 = 20
 // 正确的定义
 #define MULTIPLY(a, b) ((a) * (b))

2.3 const 常量

2.3.1 基本用法

 // 定义 const 常量
 const int DAYS_IN_WEEK = 7;
 const double GRAVITY = 9.8;
 const char* COMPANY_NAME = "Tech Corp";
 // 尝试修改 const 常量会导致编译错误
 // DAYS_IN_WEEK = 8; // 错误!

2.3.2 const 与指针

 // 指向常量的指针(不能通过指针修改所指向的值)
 const int* p1;
 int const* p2; // 与上面等价
 // 常量指针(指针本身不能改变)
 int* const p3 = &x;
 // 指向常量的常量指针(既不能修改值,也不能修改指针)
 const int* const p4 = &x;

2.3.3 const 与宏的区别

特性const 常量#define
类型检查
内存占用占用不占用(文本替换)
作用域块级全局(从定义处到文件结束)
调试可在调试器中查看无法查看(已被替换)

2.4 枚举常量 (enum)

2.4.1 基本用法

 // 定义枚举
 enum Days {
  SUN, // 默认为 0
  MON, // 默认为 1
  TUE, // 默认为 2
  WED, // 默认为 3
  THU, // 默认为 4
  FRI, // 默认为 5
  SAT // 默认为 6
 }
 // 使用枚举
 enum Days today = WED;
 printf("Today is day %d\n", today); // 输出: Today is day 3

2.4.2 自定义枚举值

 // 自定义枚举值
 enum Months {
  JAN = 1, // 1
  FEB, // 2
  MAR, // 3
  APR, // 4
  MAY, // 5
  JUN, // 6
  JUL, // 7
  AUG, // 8
  SEP, // 9
  OCT, // 10
  NOV, // 11
  DEC // 12
 }
 // 不连续的枚举值
 enum Status {
  SUCCESS = 0,
  WARNING = 100,
  ERROR = 200
 }

2.4.3 枚举的优势

  • 提高代码可读性
  • 提供类型检查
  • 避免魔法数字
  • 便于维护

3. 左值与右值 (L-values & R-values)

3.1 基本概念

  • 左值 (L-value): 指向内存位置的表达式,可以出现在赋值语句的左侧
  • 右值 (R-value): 存储在内存中某个地址的数值,只能出现在赋值语句的右侧

3.2 示例

 int x = 10; // x 是左值,10 是右值
 x = 20; // x 是左值,20 是右值
 int y = x; // y 是左值,x 是右值(x 的值被读取)
 // 错误示例:右值不能作为左值
 // 10 = x; // 错误!10 是右值
 // (x + y) = 5; // 错误!x + y 是右值

3.3 左值引用与右值引用 (C++ 特性)

注意:左值引用和右值引用是 C++ 的特性,C 语言不支持

4. 变量与常量的最佳实践

4.1 变量使用建议

  • 命名规范: 使用有意义的变量名,遵循驼峰命名法或下划线命名法
  • 初始化: 所有变量在使用前必须初始化
  • 作用域最小化: 变量的作用域应尽可能小,减少命名冲突和副作用
  • 类型选择: 根据实际需要选择合适的数据类型,避免内存浪费
  • 存储类选择: 根据变量的使用场景选择合适的存储类

4.2 常量使用建议

  • 优先使用 const: 对于需要类型检查的常量,优先使用 const
  • 宏的合理使用: 对于简单的常量或需要文本替换的场景,使用宏
  • 枚举的使用: 对于一组相关的整数常量,使用枚举提高可读性
  • 命名规范: 常量名通常使用全大写字母,单词之间用下划线分隔

4.3 代码示例

 // 良好的变量使用示例
 void calculate_area(double radius) {
  // 作用域最小化
  const double PI = 3.14159;
  double area = PI * radius * radius;
  printf("Area: %.2f\n", area);
 }
 // 良好的常量使用示例
 #define MAX_USERS 100
 const int DEFAULT_TIMEOUT = 30;
 enum LogLevel {
  LOG_ERROR,
  LOG_WARNING,
  LOG_INFO,
  LOG_DEBUG
 }
 void log_message(enum LogLevel level, const char* message) {
  switch (level) {
  case LOG_ERROR:
  printf("[ERROR] %s\n", message);
  break;
  case LOG_WARNING:
  printf("[WARNING] %s\n", message);
  break;
  // 其他 case...
  }
 }

5. 常见问题与解决方案

5.1 未初始化变量

问题: 使用未初始化的局部变量会导致程序行为不确定 解决方案: 总是在定义变量时初始化

 // 错误示例
 int x;
 printf("%d\n", x); // 输出随机值
 // 正确示例
 int x = 0;
 printf("%d\n", x); // 输出 0

5.2 全局变量的滥用

问题: 过多使用全局变量会导致代码难以维护和调试 解决方案: 尽量使用局部变量,通过函数参数和返回值传递数据

5.3 常量修改尝试

问题: 尝试修改 const 常量或字面常量 解决方案: 尊重常量的不可变性,如需修改应使用变量

 // 错误示例
 const int MAX = 100;
 MAX = 200; // 错误!
 // 正确示例
 int max = 100;
 max = 200; // 正确

5.4 宏的副作用

问题: 带参数的宏可能产生副作用 解决方案: 确保参数是简单表达式,或使用函数替代

 // 有副作用的宏
 #define SQUARE(x) (x * x)
 int i = 5;
 int result = SQUARE(i++); // 结果: 5 * 6 = 30,且 i 变为 7
 // 改进:使用函数
 int square(int x) {
  return x * x;
 }
 int i = 5;
 int result = square(i++); // 结果: 5 * 5 = 25,且 i 变为 6

6. 代码优化技巧

6.1 变量优化

  • 寄存器变量: 于频繁使用的变量,考虑使用 register 存储类
  • 静态变量: 于需要保持状态函数,使用静态变量
  • 类型优化: 据实际范围选择合适的整数类型(如 intshortlong

6.2 常量优化

  • 编译时常量: 使用 const#define 定义常量,便于编译器优化
  • 枚举优化: 使用枚举替代魔法数,提代码可读性和可维护
  • 字符串常量: 相同的字符串常量会被存储常量池中,避免重复存储

6.3 内存使用优化

  • 局部变量: 优先使用局部变量函数结束后自动释放内存
  • 静态变量: 合理使用静态变量,避免频繁的内存分配释放
  • 常量存储: const 常量可能被存储只读内存区域,提安全性

更新日志 (Changelog)

  • 2026-04-05: 拆分变量与常量详解
  • 2026-04-05: 扩写内容增加详细的代码示例、使用方法、最佳实践和常见问题解决方案

知识检测

学习进度

-- 已学文档
--% 知识覆盖率

学习推荐

专注模式