不一样的C语言-const修饰的变量

const修饰的变量

上一篇文章中,笔者讲了auto,register,static,extern修饰符对变量的作用,那么本文中,我们继续来介绍变量的修饰符--const。const是constant的缩写,意为:常量。当变量前面加上const修饰,此时这个变量是一个只读变量。这里应当注意的是,变量还是变量,只不过变量具有了只读的属性。下文中笔者会通过几个简单的例子来分析const修饰符。

一、const修饰基本局部变量

以int类型为例,我们来看看const修饰基本类型变量时,变量会有什么变化。

const int i = 0;
i = 10;

上诉的代码,编译时会发生错误。原因很简单,我们使用const修饰了变量 i ,此时变量 i 就是一个只读变量,只读变量在初始化之后,不能作为左值。那么是否就说明 i 所指向的内存空间就是安全的呢?我们继续看一个例子:

int *p = &i;
*p = 10;
printf("%d',i);

我们说了,i 现在是一个只读变量,不能作为左值,那出现的赋值号的右边总没问题吧,否则就是失去了变量的意义了。既然是合法的,我们试图来解释“int *p = &i”这句的含义,p是一个int类型的指针,指向了变量 i 的内存地址,那么p就指向了变量 i 的内存空间。顺利成章的我们通过p来修改 i 的内存空间为10,那么 i 对应的内存空间就应该为10。事实确实如此,如此看来,这个const有点鸡肋。我们先不着急去评判const的是否值得存在,我们来看看const修饰指针会发生什么有趣的事情。

二、const修饰的全局变量

const修饰的全局变量和局部变量不同,可以说,const修饰的全局变量是一个常量,其存放在只读数据区(rodata),也就是说const修饰的全局变量的内存空间是只读的,当我们试图通过任何方式去修改,都是使得程序出错崩溃。

include 
const int i = 0;
int main()
{
    int *p = &i;
    *p = 10;
    printf("%d",i);
    return 0;
}

上述代码编译时也许编译器不会给你报错,但是运行起来,程序就会出错崩溃,所以不要尝试去修改全局只读变量(常量)。

三、const与指针相遇

int i = 0;
const int *p = &i;
*p = 10;

把*p看做一个整体A,“const int A”定义了一个只读变量A,然后“A = 10”,显然,这就发生了错误,我们试图改变一个只读变量。由此,上面的那段代码编译会报错。接下来我们继续看一个例子:
int i = 0;
int * const p = &i;
*p = 10;

上述的代码,编译会出错吗?答案是不会。原因在于,const修饰的是p,p是指针,也就是说,p的值不能被改变,但是我们操作*p是可以的,*p指代的是变量 i 的内存空间。到此,读者可能对const的认识已经足够好了,那么我们继续看一个更有趣的例子:
typedef int* pint;
int main()
{
    int i = 0;
    const pint p = &i;
    *p = 10;
    
    return 0;
}

这次编译能否通过,同样不下定论,先分析。可能很多读者会把上上个例子拿出来看,“const pint p”相当于“const int *p”,这个我们相当熟悉吗,上上个例子就讲过了,结论是我们无法修改*p。错!别忘记了笔者还写过一个重要的技巧“把类型忽略来看待”,typedef想必大家熟悉,为类型起一个别名。那么此时pint就是一个类型别名,“const pint p”我们忽略类型pint,剩下“const p”,这下拨开乌云见蓝天了把。const修饰的是变量p,与*p没关系,所以编译是正确的。

总结:

C语言的const,对于局部变量而言,是将一个变量修饰为只读变量,既然是一个变量,那么其依然可以变,只不过我们不能通过已经被const修饰的变量名来修改。对于全局变量而言,其修饰的变量保存在代码段的rodata,这段内存是只读的,所以const修饰的全变量是真正意义上的常量。对于const修饰的对象究竟是谁,可以借鉴笔者上面说的忽略类型名的方法,化繁为简来看。显然const可以减少由于程序员的疏忽而改变了一个原本不打算改变的量,在编译的时候就能够发现。当然const不能将居心叵测的程序员拒之门外(C++中就不是如此,另当别论)。

你可能感兴趣的:(C语言深度剖析)