c 语言里 const

99ANYc3cd6
预计阅读时长 15 分钟
位置: 首页 C语言 正文

const 是 C 语言中一个非常重要的关键字,它的全称是 "constant"(常量),它的核心作用是告诉编译器,某个变量的值在初始化之后不允许被修改


const 的基本语法和作用

const 关键字可以用来修饰变量,使其成为一个只读的常量。

基本语法

const 数据类型 变量名 = 初始值;

或者

数据类型 const 变量名 = 初始值;

这两种写法在效果上是等价的,但第一种 const 数据类型 的写法更常见、更易读。

示例

#include <stdio.h>
int main() {
    // 定义一个整型常量 PI,其值不能被修改
    const double PI = 3.14159;
    // 以下这行代码会编译失败!
    // PI = 3.14; // error: assignment of read-only variable 'PI'
    printf("PI 的值是: %f\n", PI);
    // 也可以定义一个字符常量
    const char GREETING[] = "Hello, World!";
    // GREETING[0] = 'h'; // 同样会编译失败
    return 0;
}

关键点

  • const 修饰的变量必须在定义时进行初始化,因为之后就不能再赋值了。
  • const 变量仍然有自己的存储空间(它不是一个真正的编译期常量,#define),只是编译器会阻止你通过变量名去修改它。

const 的核心价值:为什么使用它?

使用 const 不仅仅是为了让变量“不能变”,它更多的是一种编程契约和自我保护机制

  1. 提高代码的可读性: 当其他开发者(或者未来的你)看到 const 关键字时,立刻就能明白这个变量的值是不应该被改变的,这有助于理解代码的意图。

  2. 防止意外修改: 在一个复杂的项目中,你可能不小心修改了一个本不该被修改的变量(例如一个配置项、一个数学常量)。const 会让编译器在编译时就帮你发现这种错误,避免了潜在的运行时 Bug。

  3. 为编译器优化提供信息: 编译器知道 const 变量的值不会改变,因此可以进行一些优化,如果多次使用同一个 const 变量,编译器可能会用它的初始值直接替换掉所有的使用,而不是反复从内存中读取。

  4. 是实现“安全编程”的基础const 是 C++ 等现代语言中“安全编程”理念的核心,它允许你通过参数传递承诺“我不会修改这个数据”,从而保护传入的数据不被意外篡改。


const 的进阶用法:与指针的结合

const 与指针结合使用时,情况会变得复杂一些,因为它关系到“谁不能被修改”,这里主要有四种情况,理解它们是掌握 const 的关键。

我们用一个简单的例子来分析:

int a = 10;
int b = 20;

指向常量的普通指针 (常量指针)

const int *ptr1 = &a;
// 或者 int const *ptr1 = &a; (效果相同)
  • 含义ptr1 是一个指针,它指向一个 int 类型的常量。
  • 规则
    • 不允许通过 ptr1 修改它所指向的值
    • 允许 ptr1 指向其他变量(即可以修改指针本身的值)。
  • 示例
    *ptr1 = 100; // 错误!不能通过 ptr1 修改 a 的值
    ptr1 = &b;   // 正确!可以让 ptr1 指向 b

指向普通常量的指针 (指针常量)

int * const ptr2 = &a;
  • 含义ptr2 是一个常量指针,它指向一个 int 类型的变量。
  • 规则
    • 允许通过 ptr2 修改它所指向的值
    • 不允许 ptr2 指向其他变量(即不能修改指针本身的值)。
  • 示例
    *ptr2 = 100; // 正确!可以通过 ptr2 修改 a 的值
    ptr2 = &b;   // 错误!不能让 ptr2 指向 b

指向常量的常量指针

const int * const ptr3 = &a;
  • 含义ptr3 是一个常量指针,它指向一个 int 类型的常量。
  • 规则
    • 不允许通过 ptr3 修改它所指向的值
    • 不允许 ptr3 指向其他变量
  • 示例
    *ptr3 = 100; // 错误!
    ptr3 = &b;   // 错误!

指向非常量的普通指针

int *ptr4 = &a;
  • 含义:这是最普通的指针,没有任何 const 限制。
  • 规则
    • 允许通过 ptr4 修改它所指向的值
    • 允许 ptr4 指向其他变量
  • 示例
    *ptr4 = 100; // 正确!
    ptr4 = &b;   // 正确!

记忆技巧

一个简单的口诀可以帮助你区分前两种情况:

星号左边是 const,指向的是常量,内容不能改。 星号右边是 const,指针本身是常量,地址不能改。

const int * p1; // 星号左边是 const -> *p1 不能改
int * const p2;  // 星号右边是 const -> p2 不能改

const 在函数参数中的应用

这是 const 最实用的场景之一,尤其是在处理指针或数组作为函数参数时。

保护传入的数据

当你不希望函数修改传入的数组或结构体时,使用 const 可以提供安全保障。

#include <stdio.h>
#include <string.h>
// 好的实践:使用 const 保护源字符串,防止函数内部意外修改
void print_string(const char *str) {
    // str[0] = 'x'; // 错误!编译器会阻止这行代码
    printf("字符串内容: %s\n", str);
}
// 好的实践:使用 const 保护数组内容
void print_array(const int arr[], int size) {
    for (int i = 0; i < size; i++) {
        // arr[i] = 0; // 错误!
        printf("%d ", arr[i]);
    }
    printf("\n");
}
int main() {
    char my_str[] = "Hello";
    int my_nums[] = {1, 2, 3, 4, 5};
    print_string(my_str);
    print_array(my_nums, 5);
    return 0;
}

const 修饰函数返回值

const int get_status() {
    return 200; // 返回一个常量值
}
int main() {
    int status = get_status();
    // status = 500; // 这行代码没问题,因为 status 本身是普通变量
    // 但如果你尝试这样做:
    // const int *s = get_status();
    // *s = 500; // 错误!因为 get_status() 返回的是一个临时常量,不能通过解引用来修改
    // 更常见的做法是直接赋值给一个普通变量,如上面的 status。
    return 0;
}

修饰返回值的主要目的是防止返回值(如果是指针或引用)被意外修改,这在 C++ 中更常见,在 C 中也用于增加代码的清晰度和安全性。


const#define 的区别

特性 const 变量 #define
类型 有数据类型,编译器会进行类型检查 没有类型,只是简单的文本替换
安全 类型安全,编译器能发现错误 不安全,只是替换,可能产生意想不到的结果
作用域 遵循变量的作用域规则(块作用域) 从定义处到文件末尾全局有效,无作用域概念
调试 存在符号表中,可以被调试器看到 在预处理阶段就被替换,调试时看不到宏名
存储 通常分配内存(除非是常量折叠) 不分配内存,只是代码片段
复杂定义 可以定义复杂类型(如数组、结构体) 只能做简单的文本替换,不能定义类型

在现代 C 语言编程中,优先使用 const,而不是 #define 来定义常量。const 更安全、更强大、更易于调试。


const 是 C 语言中一个强大且不可或缺的工具,它不仅仅是创建一个“不能变的变量”,更是实现代码安全、清晰和高效的重要手段。

  • 基础const 修饰的变量初始化后不能被修改。
  • 核心价值:提高可读性、防止 Bug、协助编译器优化。
  • 进阶:与指针结合时,要区分“内容不能改”和“地址不能改”。
  • 实践:在函数参数中广泛使用 const 来保护传入的数据。
  • 对比:优先使用 const 而非 #define 来定义常量。

熟练掌握 const 是从 C 语言新手迈向高手的重要一步。

-- 展开阅读全文 --
头像
float内存占用究竟是多少?
« 上一篇 昨天
dede商品价格跳转如何设置?
下一篇 » 昨天

相关文章

取消
微信二维码
支付宝二维码

目录[+]