C语言const关键字详解

C语言的const关键字

const 在实际编程中用得并不多,const 是 constant 的缩写,意思是“恒定不变的”!它是定义只读变量的关键字,或者说 const 是定义常变量的关键字。

说 const 定义的是变量,但又相当于常量;说它定义的是常量,但又有变量的属性,所以叫常变量。用 const 定义常变量的方法很简单,就在通常定义变量时前面加 const 即可,如:

const int a = 10;

const 和变量类型 int 可以互换位置,二者是等价的,即上条语句等价于:

int const a = 10;

那么用 const 修饰后和未修饰前有什么区别呢?它们不都等于 10 吗?

用 const 定义的变量的值是**不允许改变的,即不允许给它重新赋值,即使是赋相同的值也不可以。**所以说它定义的是只读变量。这也就意味着必须在定义的时候就给它赋初值。

如果定义的时候未初始化,我们知道,对于未初始化的局部变量,程序在执行的时候会自动把一个很小的负数存放进去。这样后面再给它赋初值的话就是“改变它的值”了,即发生语法错误。

用 const 修饰的变量,无论是全局变量还是局部变量,生存周期都是程序运行的整个过程。全局变量的生存周期为程序运行的整个过程这个是理所当然的。而使用 const 修饰过的局部变量就有了静态特性,它的生存周期也是程序运行的整个过程。我们知道全局变量是静态的,静态的生存周期就是程序运行的整个过程。但是用const修饰过的局部变量只是有了静态特性,并没有说它变成了静态变量。

我们知道,局部变量存储在栈中,静态变量存储在静态存储区中,而经过 const 修饰过的变量存储在内存中的“只读数据段”中。只读数据段中存放着常量和只读变量等不可修改的量。

数组的长度不能是变量: 虽然 const 定义的是只读变量,就相当于是定义一个常量。但是只读变量也是变量,所以 const 定义的变量仍然不能作为数组的长度。C++ 扩展了 const 的含义,在 C++ 中用 const 定义的变量也可作为数组的长度。

const 和指针

const 也可以和指针变量一起使用,这样可以限制指针变量本身,也可以限制指针指向的数据。const 和指针一起使用会有几种不同的顺序,如下所示:

int a = 1;
int b = 2;
// 情况一
const int *p1 =  &a;
int const *p2 = &a;
// 指针所指向的数据是只读的,也就是 p1、p2 本身的值可以修改(指向不同的数据),但它们指向的数据不能被修改。
// 即: 
// *p1 = 3; 报错,不能修改指向的数据
// p1 = &b; 有效,可以修改变量的指向

// 情况二
int * const p3 = &a; 
// 指针是只读的,也就是 p3 本身的值不能被修改;
// 即:
// p3 = &b; 报错,不能修改指向的地址
// *p3 = 3; 有效,可以修改指向地址的值

// 情况三
// 指针本身和它指向的数据都是只读的
const int * const p4;
int const * const p5;

const 离变量名近就是用来修饰指针变量的,离变量名远就是用来修饰指针指向的数据,如果近的和远的都有,那么就同时修饰指针变量以及它指向的数据。

const 和函数形参

const 通常用在函数形参中,如果形参是一个指针,为了防止在函数内部修改指针指向的数据,就可以用 const 来限制。例如c语言标准库中的函数

size_t strlen ( const char * str );
int strcmp ( const char * str1, const char * str2 );
char * strcat ( char * destination, const char * source );
char * strcpy ( char * destination, const char * source );

使用 const 限制函数参数,不但可以防止数据被修改,还可以在调用的时候给予提示。

const 和非 const 类型转换

const char *char *是不同的类型,不能将const char *类型的数据赋值给char *类型的变量。但反过来是可以的,编译器允许将char *类型的数据赋值给const char *类型的变量。

char *指向的数据有读取和写入权限,而const char *指向的数据只有读取权限,降低数据的权限不会带来任何问题,但提升数据的权限就有可能发生危险。

所以下面操作行不通:

#include 

int main(){
    const char *str1 = "a";
    char *str2 = str1; // 报错,不能给const变量增加权限
    return 0;
}

const 和 define

很多人在学习 const 的时候都会混淆它与 define 的区别。从功能上说它们确实很像,但它们又有明显的不同:

  1. define是预编译指令,而const是普通变量的定义。define定义的宏是在预处理阶段展开的,而const定义的只读变量是在编译运行阶段使用的。
  2. **const定义的是变量,而define定义的是常量。**define定义的宏在编译后就不存在了,它不占用内存,因为它不是变量,系统只会给变量分配内存。但const定义的常变量本质上仍然是一个变量,具有变量的基本属性,有类型、占用存储单元。
  3. **const定义的是变量,而宏定义的是常量,所以const定义的对象有数据类型,而宏定义的对象没有数据类型。**所以编译器可以对前者进行类型安全检查,而对后者只是机械地进行字符替换,没有类型安全检查。这样就很容易出问题,即“边际问题”或者说是“括号问题”。

与C++区别

在c++中,一个const不必创建内存空间,而在c中,一个const总是需要一块内存空间。

c++中用const定义了一个常量后,将其写入符号表(symbol table),这使得它成为一个编译期间的常量,没有给变量分配内存,也没有了存储与读内存的操作,使得它的效率也很高。

int main(){
    const int a  = 2;
    int* p = (int*)(&a);
    *p = 30; // 直接修改const常量对应的内存空间中的值
    cout<<&a<

运行结果:

0x7fffc7920804
0x7fffc7920804
2
30

通过指针修改const常量对应的内存空间中的值,这种修改不会影响到常量本身的值,因为编译器根本不会去进行内存空间的读取。这就是c++的常量折叠(constant folding),即将const常量放在符号表中,而并不给其分配内存。编译器直接进行替换优化。

除非需要用到const常量的存储空间,编译器迫不得已才会分配一个空间,但之后const常量的值仍旧从符号表中读取,所以不会影响const常量的值。

而在C语言中

int main()
{
    const int a  = 2;
    int* p = (int*)(&a);
    *p = 30;
    printf("%x\n",&a);
    printf("%x\n",p);
    printf("%i\n",a);
    printf("%i\n",*p);
    return 0;
}

运行结果

18b1c0e4
18b1c0e4
30
30

c语言通过指针引用可以修改const修饰变量的值。

c 与 c++ 的 const 异同总结

  • c语言全局const会被存储到只读数据段。c++中全局const当声明extern或者对变量取地址时,编译器会分配存储地址,变量存储在只读数据段。两个都受到了只读数据段的保护,不可修改。
  • c语言中局部const存储在堆栈区,只是不能通过变量直接修改const只读变量的值,但是可以跳过编译器的检查,通过指针间接修改const值。而c++则是:
    • 对于基础数据类型,把它放到符号表中,不分配内存,当对其取地址时,会分配内存。
    • 对于基础数据类型,如果用一个已经初始化的变量初始化const变量,会分配内存。
    • 对于自定数据类型,比如类对象,那么也会分配内存。
  • c中const默认为外部连接,c++中const默认为内部连接。当c语言两个文件中都有const int a的时候,编译器会报重定义的错误。而在c++中,则不会,因为c++中的const默认是内部连接的。

参考资料

const关键字及其作用(用法),C语言const详解
c语言中的const的作用及解析
c和c++关于const的一些区别

你可能感兴趣的:(C语言,c语言)