C语言const修饰符探秘

时间:2022-12-20 21:07:10

前言

C语言是我接触的第一门程序设计语言,当时还很傻很天真,后来迅速被各种高级语言*,但是不得不说,C的地位真的无可撼动。

const修饰符在C语言中很常用,但是最近读代码的时候常常搞不清楚,搜索了一番,做个总结。

整体认知

const是常量修饰符,代码中设法阻止变量被改变,这个时候可以使用const关键字。必须在声明const变量就初始化,因此,类似与这样的声明是错误的:
const int i;
i = 8;

编译器会报:error: assignment of read-only variable ‘i’。
我们必须要这样:
const int i = 8;

const修饰符的典型作用

  1. 最基本的,保护被修饰的作用,防止以外修改
  2. 编译器通常不为const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储和读取内存的操作,效率很高
  3. 使程序中的错误在编译的时候就被报出(因为2)。
下面会一一举例说明。

const修饰符的修饰对象探秘

修饰普通变量

const int i = 8;
int const i = 8;

上面两者是等价的,都是声明了一个值为5的常量。

修饰指针

const int *p;
int const *p;

上面两个是等价的,都声明了一个const int类型的指针,也就是说,程序里面不能改变*p的值,但是可以改变p的值(即可以改变指针的指向)。
int * const p = &n;

这个是声明了一个const的指针,const是修饰指针的。也即p的值不允许改变(指针就一定得指向n那块内存,不能改变p的指向),但是*p的值可以改变。

小技巧

  • const在*的左侧,const修饰指针所指的变量
  • const在*的右侧,const修饰指针本身

带const修饰的函数形参

首先要明确一点,非指针参数(值传递)传给函数的是参数的一份拷贝,本身就不会改变参数的值,所以加上const是没有意义的,例如:
#include <stdio.h>

void add(const int a)
{
a = a + 1;
}

int main()
{
int temp = 34;
add(temp);
printf("%d",temp);
return 0;
}

虽然编译过不去,但是,就算不加const,在函数体中成功尝试修改了a,但是在本例中temp的值也是不变的,因为函数中修改的是a的一个拷贝(值传递)。 先看这个函数:
void foo(const int * p)
{
*p += 1;
}

编译时就会报错,因为我们试图修改一个常量的值。
带const的形参是不允许在函数体内修改其值的(只读)。 不过一旦这样写就没辙了:
void foo(const int * p)
{
int *r = p;
*r += 1;
}

虽然编译器编译时会告诉你:warning: initialization discards qualifiers from pointer target type,说你赋值丢了一个属性修饰符,但是运行时还是可以改变p所指向内存的值。
再看这样一个例子:
void foo(int *const p1) {
int a = 1;
p1 = &a;
}

显然编译不过去。这样做本来是想传进来一个const的指针,但实际上,这样是没有意义的。指针作为参数时也是传进去一个指针的拷贝。所以即使不加const,函数体中也是修改的这个“指针的拷贝”。这个函数实际上和本小节开头的例子是没区别的(但是这个指针的拷贝所指向的内容可是和被拷贝的东西是一样的)。 可以用下面的图说明: C语言const修饰符探秘
和这个是类似的: C语言const修饰符探秘