【0】概述

const是constant 的缩写,是C语言的关键字之一,是一个类型修饰符(type specifier)。const是恒定不变的意思也翻译为常量、常数等。很不幸,正是因为这一点,很多人都认为被 const 修饰的值是常量。这是不精确的,精确的说应该是只读的变量,其值在编译时不能被使用,因为编译器在编译时不知道其存储的内容。或许当初这个关键字应该被替换为 readonly。那么这个关键字有什么用处和意义呢?

它限定变量为只读变量,即变量保存在只读静态存储区。编译时,如何尝试修改只读变量,则编译器提示出错,就能防止误修改。使用const在一定程度上可以提高程序的安全性和可靠性。const 推出的初始目的,正是为了取代预编译指令,消除它的缺点,同时继承它的优点。

【1】作用

1.1 const 修饰的只读变量

例如:

1 const int Max=100;
2 int Array[Max];

这里请在 Visual C++6.0 里分别创建.c 文件测试一下。你会发现在.c 文件中,编译器会提示出错,这也从侧面证实在 C 语言中,const 修饰的 Max 仍然是变量,只不过是只读属性罢了

1.2节省空间,避免不必要的内存分配,同时提高效率

编译器通常不为普通 const 只读变量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的值,没有了存储与读内存的操作,使得它的效率也很高。

例如:

1 #define M 3 //宏常量
2 const int N=5;     //此时并未将 N 放入内存中
3 ......
4 int i=N; //此时为 N 分配内存,以后不再分配!
5 int I=M; //预编译期间进行宏替换,分配内存
6 int j=N; //没有内存分配
7 int J=M; //再进行宏替换,又一次分配内存!

const 定义的只读变量从汇编的角度来看,只是给出了对应的内存地址,而不是象#define一样给出的是立即数,所以,const 定义的只读变量在程序运行过程中只有一份拷贝(因为它是全局的只读变量,存放在静态区),而#define 定义的宏常量在内存中有若干个拷贝。#define 宏是在预编译阶段进行替换,而 const 修饰的只读变量是在编译的时候确定其值。#define 宏没有类型,而 const 修饰的只读变量具有特定的类型。

1.3修饰一般变量

一般常量是指简单类型的只读变量。这种只读变量在定义时,修饰符 const 可以用在类型说明符前,也可以用在类型说明符后。例如:

1 int   const   i=2;    
2 //或    
3 const   int   i=2;

1.4,修饰数组

定义或说明一个只读数组可采用如下格式:

1 int const a[5]={1, 2, 3, 4, 5};
2 //或
3 const int a[5]={1, 2, 3, 4, 5};

1.5,修饰指针

1 const int *p;          // p 可变,p 指向的对象不可变
2 int const *p;         // p 可变,p 指向的对象不可变
3 int *const p;         // p 不可变,p 指向的对象可变
4 const int *const p; //指针 p 和 p 指向的对象都不可变

给出一个记忆和理解的方法:

先忽略类型名(编译器解析的时候也是忽略类型名),我们看 const 离哪个近。“近水楼台先得月”,离谁近就修饰谁。

1 const int *p;          //const 修饰*p,p 是指针,*p 是指针指向的对象,不可变
2 int const *p;         //const 修饰*p,p 是指针,*p 是指针指向的对象,不可变
3 int *const p;         //const 修饰 p,p 不可变,p 指向的对象可变
4 const int *const  p; //前一个 const 修饰*p,后一个 const 修饰 p,指针 p 和 p  指向的对象都不可变

const int *a
这里const 修饰的是int,而int定义的是一个整值
因此*a 所指向的对象 值 不能通过 *a 来修改,但是 可以重新给 a 来赋值,使其指向不同的对象
eg:

1  const int *a = 0;
2  const int b = 1;
3  int c = 1;
4  a = &b //ok! 额外:注意不能通过a 来修改 b值
5  a = &c //ok! 额外:虽然c本身不是一个常量
6  *a = 2 //erro! 为题就在这里,不能修改通过 *a 所指向的对象值,最后赋值得对象是c,因此不能通过*a 来修改c值。

int const *a

 

和 const int *a 的意义是相同的 他们两个的作用等价

int *const a  
这里const修饰的是 a ,a代表的是一个指针地址因此不能赋给a其他的地址值,但可以修改a指向的值这有点和cont int *a相反的意味

const int * const a 
这个代表a所指向的对象的值以及它的地址本身都不能被改变

1.6,修饰函数的参数

const 修饰符也可以修饰函数的参数,当不希望这个参数值被函数体内意外改变时使

用。例如:

void Fun(const int i);

告诉编译器 i 在函数体中的不能改变,从而防止了使用者的一些无意的或错误的修改。

1.7,修饰函数的返回值

const 修饰符也可以修饰函数的返回值,返回值不可被改变。例如:

const int Fun (void);

在另一连接文件中引用 const 只读变量:

extern const int i;         //正确的声明
extern const int j=10; //错误!只读变量的值不能改变。

【3】Q&A

Q:

constdefine的区别?

A:

两者都可以用来定义常量,但是const定义时,定义了常量的类型,所以更精确一些(其实const定义的是只读变量,而不是常量)。#define只是简单的文本替换,除了可以定义常量外,还可以用来定义一些简单的函数,有点类似内置函数。const和define定义的常量可以放在头文件里面。(小注:可以多次声明,但只能定义一次)

关于const的点滴补充:
1、const 对象的地址只能赋值给指向const 对象的指针
2、指向const 对象的指针可以 被赋 以 一个非const 对象的地址 
3、指向const 的指针常被用作函数的形式参数,保证被传递给函数的实际对象在函数得实际对象在函数中不会被修改
4、常量在定义后就不能被修改,所以它必须被初始化。未初始化的常量定义将导致编译错误(上面都是在说明const得问题,所以没有赋值,实际语句中要赋值的)