C常量与指针

C语言的功能强大而丰富,还表现在const关键字与指针的结合使用上。对不同的问题,它能提供不同的保护。特别强大和有用的是指向常量的指针。后面的文章我们将看到如何用这种技术来阻止函数的使用者修改函数的参数。

指向常量的指针

可以将指针定义为指向常量,这意味着不能通过指针修改它所引用的值。下例声明了一个整数和一个整数常量,然后声明了一个整数指针和一个指向整数常量的指针,并分别初始化为对应的整数:

int num = 5;
const int limit = 500;
int *pi;                 // 指向整数
const int *pci;         // 指向整数常量

pi = #
pci = &limit;

图1-11是它们的内存分配情况。

enter image description here

图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来说明。

enter image description here

图1-15:指向“指向常量的常量指针”的指针

下面说明它们的使用。这段代码的输出应该是两个500:

printf("%d\n",*cpci);
pcpci = &cpci;
printf("%d\n",**pcpci);

下表总结了本章讨论的前四种指针。

指针类型 指针是否可修改 指向指针的数据是否可修改
指向非常量的指针
指向常量的指针
指向非常量的常量指针
指向常量的常量指针
赞(3)

评论 抢沙发

评论前必须登录!

 

C指针