C语言的功能强大而丰富,还表现在const
关键字与指针的结合使用上。对不同的问题,它能提供不同的保护。特别强大和有用的是指向常量的指针。后面的文章我们将看到如何用这种技术来阻止函数的使用者修改函数的参数。
指向常量的指针
可以将指针定义为指向常量,这意味着不能通过指针修改它所引用的值。下例声明了一个整数和一个整数常量,然后声明了一个整数指针和一个指向整数常量的指针,并分别初始化为对应的整数:
int num = 5;
const int limit = 500;
int *pi; // 指向整数
const int *pci; // 指向整数常量
pi = #
pci = &limit;
图1-11是它们的内存分配情况。
图1-11:指向整数常量的指针
下面的代码会打印这些变量的地址和值:
printf(" num - Address: %p value: %d\n",&num, num);
printf("limit - Address: %p value: %d\n",&limit, limit);
printf(" pi - Address: %p value: %p\n",&pi, pi);
printf(" pci - Address: %p value: %p\n",&pci, pci);
运行代码会产生类似下面的输出:
num - Address: 100 value: 5
limit - Address: 104 value: 500
pi - Address: 108 value: 100
pci - Address: 112 value: 104
如果只是读取整数的值,那么引用指向常量的指针就没事,读取是完全合法的,而且也是必要的功能,如下所示:
printf("%d\n", *pci);
我们不能解引指向常量的指针并改变指针所引用的值,但可以改变指针。指针的值不是常量。指针可以改为引用另一个整数常量,或者普通整数。这样做不会有问题。声明只是限制我们不能通过指针来修改引用的值。
这意味着下面的赋值是合法的:
pci = #
我们可以解引pci
来读取它,但不能解引它来修改它。
考虑下面的赋值语句:
*pci = 200;
这会导致如下语法错误:
'pci' : you cannot assign to a variable that is const
指针认为自己指向的是整数常量,所以不允许用指针来修改这个整数。我们还是可以通过名字来修改num
变量,只是不能用pci
来修改。
理论上来说,常量的指针也可以如图1-12那样可视化,普通方框表示变量,阴影方框表示常量。pci
指向的阴影方框不能用pci
来修改,虚线表示指针可以引用的数据类型。在上例中,pci
指向limit
。
图1-12:指向常量的指针
把pci
声明为指向整数常量的指针意味着:
pci
可以被修改为指向不同的整数常量;pci
可以被修改为指向不同的非整数常量;- 可以解引
pci
以读取数据; - 不能解引
pci
从而修改它指向的数据。
注意 数据类型和
const
关键字的顺序不重要。下面两个语句是等价的:const int *pci; int const *pci;
指向非常量的常量指针
也可以声明一个指向非常量的常量指针。这么做意味着指针不可变,但是它指向的数据可变。下面是这种指针的例子;
int num;
int *const cpi = #
有了这个声明:
cpi
必须被初始化为指向非常量变量;cpi
不能被修改;cpi
指向的数据可以被修改。
从原理上说,这类指针可以用图1-13来说明。
图1-13:指向非常量的常量指针
无论cpi
引用了什么,都可以解引cpi
然后赋一个新值。下面是两条合法的赋值语句:
*cpi = limit;
*cpi = 25;
然而,如果我们试图把cpi
初始化为指向常量limit
,如下所示:
const int limit = 500;
int *const cpi = &limit;
那么就会产生一个警告:
warning: initialization discards qualifiers from pointer target type
如果这里cpi
引用了常量limit
,那常量就可以修改了。这样不对,因为常量是不能被修改的。
在把地址赋给cpi
之后,就不能像下面这样再赋给它一个新值了:
int num;
int age;
int *const cpi = #
cpi = &age;
如果采用这种做法会产生如下错误信息:
'cpi' : you cannot assign to a variable that is const
指向常量的常量指针
指向常量的常量指针很少派上用场。这种指针本身不能修改,它指向的数据也不能通过它来修改。下面是指向常量的常量指针的一个例子:
const int * const cpci = &limit;
指向常量的常量指针可以用图1-14来说明。
图1-14:指向常量的常量指针
与指向常量的指针类似,不一定只能将常量的地址赋给cpci
。如下所示,我们其实还可以把num
的地址赋给cpci
:
int num;
const int * const cpci = #
声明指针时必须进行初始化。如果像下面这样不进行初始化:
const int * const cpci;
就会产生如下语法错误:
'cpci' : const object must be initialized if not extern
对于指向常量的常量指针,我们不能:
- 修改指针;
- 修改指针指向的数据。
重新赋给cpci
一个新地址:
cpci = #
会导致如下语法错误:
'cpci' : you cannot assign to a variable that is const
像下面这样试图解引指针并赋新值:
*cpci = 25;
会产生如下错误:
'cpci' : you cannot assign to a variable that is const
expression must be a modifiable lvalue
不过,指向常量的常量指针很少用到。
指向“指向常量的常量指针”的指针
指向常量的指针也可以有多层间接引用。在下例中,我们声明一个指向上一节提到的cpci
指针的指针。从右往左读可以帮助我们理解这个声明:
const int * const cpci = &limit;
const int * const * pcpci;
指向“指向常量的常量指针”的指针可以用图1-15来说明。
图1-15:指向“指向常量的常量指针”的指针
下面说明它们的使用。这段代码的输出应该是两个500:
printf("%d\n",*cpci);
pcpci = &cpci;
printf("%d\n",**pcpci);
下表总结了本章讨论的前四种指针。
指针类型 | 指针是否可修改 | 指向指针的数据是否可修改 |
---|---|---|
指向非常量的指针 | 是 | 是 |
指向常量的指针 | 是 | 否 |
指向非常量的常量指针 | 否 | 是 |
指向常量的常量指针 | 否 | 否 |
评论前必须登录!
注册