一、什么是const
常类型,使用类型修饰符const说明的类型,常类型的变量或者对象的值是不能被更新的。
const含义:只要一个变量前用const来修饰,就意味着该变量里的数据只能被访问,而不能被修改,也就是意味着“只读”(readonly)
二、const的优点
- 定义常量:
const int n=100;
- 类型检查:const常量与#define宏定义常量区别:
- const定义变量类型只有整数或者枚举,并且常量表达初始化才为常量表达式
- 预编译指令只是对值进行简单的替换,不能进行类型检查,相比之下,被const修饰的变量有自己的类型,编译器可以通过安全检查
- 其余情况下const常量只是一个被const限定的变量,不能与常量混肴
- 可以保护被修饰的东西,防止意外修改,增强程序的健壮性
- 可以避免不必要的内存分配,编译器通常不为被const修饰的变量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。
三、const的用法:
3.1 修饰全局变量
此时全局变量只能使用不可修改,直接修改(编译报错)与修改地址(程序异常)都不行
#include <stdio.h> //const修饰全局变量 //此时全局变量只能使用不可修改,直接修改(编译报错)与修改地址(程序异常)也不行 const int a = 100; void test() { printf("a = %d\n", a);//直接使用 //a = 200;//直接修改,编译报错,显示a是只读的 //printf("a = %d\n", a); //int *p = &a; //*p = 300;//通过地址修改,程序异常,有些编译器可能只会显示警告,并不会报错,且程序能正常运行,但是不会显示任何结果 //printf("a = %d\n", a); } int main() { test(); return 0; }
全局变量的作用域是整个文件,我们应该尽量避免使用全局变量,因为一旦有一个函数改变了全局变量的值,它也会影响到其他引用这个变量的函数,导致除了bug后很难发现,如果一定要用全局变量,我们应该尽量的使用const修饰符进行修饰,这样防止不必要的人为修改。
3.2 修饰局部变量
不可直接修改变量的值(编译报错),但是可以通过地址进行修改
const int n = 5; int const n = 5;
这两种写法是一样的,都是表示变量n的值不能被改变了,需要注意的是,用const修饰变量时,一定要给变量初始化,否则之后就不能再进行赋值了。
#include<stdio.h> //const修饰普通局部变量 //不可直接修改(编译报错),但是可以通过地址进行修改 void test() { const int b = 100; printf("b = %d\n", b); //b = 200;//直接修改,编译报错 //printf("b = %d\n", b); int *p = &b; *p = 300;//通过地址进行修改 printf("b = %d\n", b); } int main() { test(); return 0; }
接下来看看const用于修饰常量静态字符串,例如:
const char* str = "fdsafdsa";
如果没有const的修饰,我们可能会在后面有意无意的写str[4]=’x’类似这样的语句,这样会导致对只读内存区域的赋值,然后程序会立刻异常终止。有了const,这个错误就能在程序被编译的时候就立即检查出来,这就是const的好处。让逻辑错误在编译期被发现。
3.3 修饰指针变量的类型(即常量指针)
const修饰指针变量的类型,可直接修改变量值,不可修改指针指向的地址里的内容(编译报错),可以修改指针的指向
常量指针是指针指向的内容是常量,可以有以下两种定义方式:
const int * n; int const * n;
可以发现,无论是在int前还是在int后,const一直位于*之前
#include<stdio.h> void test() { int c = 100; const int *p = &c; printf("c = %d\n",c); c = 111;//可以直接修改变量的值 printf("c = %d\n",c); //*p = 200;//不可修改指针指向的地址里的内容,编译报错 //printf("c = %d\n",c); int d =300; p = &d;//可以修改指针的指向 printf("*p = %d\n",*p); } int main() { test(); return 0; }
3.4 修饰指针变量(即指针常量)
可以直接修改变量的值,可以修改指针指向地址的内容,但是不能修改指针的指向(编译报错)
指针常量是指指针本身是个常量,不能在指向其他的地址,写法如下:
int *const n;
#include<stdio.h> void test() { int c = 200; int * const p = &c;//const修饰指针,即指针常量 printf("*p = %d\n",*p); c = 300; printf("c = %d\n",c);//直接修改值 printf("*p = %d\n",*p); *p = 400;//修改指针指向地址里的内容 printf("*p = %d\n",*p); int d =500; //p = &d;//不能修改指针的指向,编译报错 //printf("*p = %d\n",*p); } int main() { test(); return 0 ; }
区分常量指针和指针常量的关键就在于*的位置,以*为分界线,如果const在*的左边,则为常量指针,如果const在*的右边则为指针常量。
如果我们将*读作‘指针’,将const读作‘常量’的话,内容正好符合:
int const * n; 常量+指针-->常量指针 int *const n; 指针+常量 -->常量指针
3.5 指向常量的常指针
是以上两种的结合,指针指向的位置不能改变并且也不能通过这个指针改变变量的值,但是依然可以通过其他的普通指针改变变量的值。
const int* const p;
#include<stdio.h> void test() { int c = 200; const int * const p = &c;//即修饰指针变量类型又修饰指针变量 printf("*p = %d\n",*p); c = 300;//可以直接修改值 printf("c = %d\n",c); printf("*p = %d\n",*p); //*p = 400;//不能修改指针指向地址里的内容,编译报错 //printf("*p = %d\n",*p); //int d =500; //p = &d;//不能修改指针指向,编译报错 //printf("*p = %d\n",*p); } int main() { test(); return 0; }
四、修饰函数的参数
根据常量指针与指针常量,const修饰函数的参数也是分为三种情况
- 防止修改指针指向的内容
void StringCopy(char *strDestination, const char *strSource);
- 其中 strSource 是输入参数,strDestination 是输出参数。给 strSource 加上 const 修饰后,如果函数体内的语句试图通过解引用的方式改动 strSource 的内容,编译器将指出错误。
- 防止修改指针指向的地址
void swap ( int * const p1 , int * const p2 )
- 指针p1和指针p2指向的地址都不能修改。
五、修饰函数的返回值
如果给以“指针传递”方式的函数返回值加 const 修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const 修饰的同类型指针。
例如函数
const char * GetString(void)
如下语句将出现编译错误:
char *str = GetString();
正确的用法是
const char *str = GetString();