这种技术也会带来一些新的问题。通常C传递数据的值,其原因是要保护原始数据的完整性。函数使用原始数据的一份拷贝,这样就不会意外的修改原始数据。但是,由于处理数组的函数直接操作原始数据,所以它能够修改原数据。有时候这正是我们需要的,比如给某个数组的所有元素加上同一个值;然而也许其他函数并不希望修改数据,比如只是计算数组中所有元素的和。
10.6.1 对形式参量使用 const
在k&RC中,避免此类错误惟一的方法就是警惕不出错。ANSI C中有另一种方法。
如果设计意图是函数不改变数组的内容,那么可以在函数原型和定义的形式参量声明中使用关键字const。例如sum()的原型的定义应该如下:
int sum (const int ar[],int n); //原型
int sum (const int ar[],int n) //定义
{ ... }
这告知编译器:函数应当把ar所指向的数组作为包含常量数据的数组对待。这样如果你意外的使用诸如ar[i]++之类的表达式,编译器将会发现这个错误并生成一条错误消息,通知您函数试图修改常量。
需要理解的是,这样使用const并不要求原始数据是固定不变的。这只是说明函数在处理数组时,应把数组当作是固定不变的。使用const可以对数组提供保护,就像按值传递可以对基本类型提供保护一样,可以阻止函数修改调用函数中的数据 。
总之,如果函数想修改数组,那么在声明数组参量时就不要使用const;如果函数不需要修改数组,那么在声明数组参量时最好使用const。
请看程序清单10.14中的程序,其中一个函数显示数组,另一个函数对数组的每一个元素乘上一个给定的数值。因为第一个函数不需要修改数组,所以使用const;因为第二个函数需要修改数组,所以不使用const。
程序清单10.14 atf.c程序
/*arf.c --处理数组的函数*/
#include <stdio.h>
#define SIZE 5
void show_array (const double ar[],int n);
void mult_array (double ar[],int n,double mult);
int main(void)
{
double dip[SIZE]={20.0,17.66,8.2,15.3,22.22};
printf("The original dip array: \n");
show_array(dip,SIZE);
mult_array(dip,SIZE,2.5);
printf("The dip array after calling mult_array(): \n");
show_array(dip,SIZE);
return 0;
}
/*显示数组内容*/
void show_array (const double ar[],int n)
{
int i;
for(i=0;i<n;i++)
printf("%8.3f ",ar[i]);
putchar('\n');
}
/*用同一个乘数去乘每个数组元素*/
void mult_array (double ar[],int n,double mult)
{
int i;
for(i=0;i<n;i++)
ar[i]*=mult;
}
输出结果如下:
The original dip array:
20.000 17.660 8.200 15.300 22.220
The dip array after calling mult_array():
50.000 44.150 20.500 38.250 55.550
请注意两个函数都是void类型的。函数mult_array()确实使数组dip得到了新的值,但不是使用rerutn机制实现的。
10.6.2 有关const的其他内容
前面我们讲过可以使用const来创建符号常量:
const double PI=3.14156;
以上也可以使用#define指令实现。但使用const还可以创建数组常量、指针常量以及指向常量的指针。
指向常量的指针不能用于修改数值,考虑以下代码:
double rates[5]={88.99, 100.12, 59.45, 183.11, 340.5};
const double *pd=retes; //pd指向数组开始处
第二行代码把pd声明为指向const double 的指针。这样,就不能使用pd来修改它所指向的数值。
*pd = 29.89; //不允许
pd[2] = 222.22; //不允许
rates[0] = 99.99 //允许,因为rates不是常量
无论使用数组符号还是指针符号,都不能使用pd修改所指向的数据的值。另外需要注意,还可以让pd指向其他地址:
pd++; /*让pd指向rates[1] -这是允许的*/
通常把指向常量的指针用作函数参量,以表明函数不会用这个指针来修改数据。例如,程序清单10.14中函数show_array()的原型可以如下定义:
void show_array(const double *ar,int n);
关于指针赋值和const有一些规则需要注意。
首先,将常量或非常量数据的地址赋给指向常量的指针是合法的:
double rates[5]={88.99, 100.12, 59.45, 183.11, 340.5};
const double locked[4]={0.08, 0.075, 0.0725, 0.07};
const double * pc = rates; //合法
pc = locked; //合法
pc = &rates[3]; //合法
然而,只有非常量数据的地址才可以赋给普通指针:
double rates[5]={88.99, 100.12, 59.45, 183.11, 340.5};
const double locked[4]={0.08, 0.075, 0.0725, 0.07};
double * pc = rates; //合法
pc = locked; //非法
pc = &rates[3]; //合法
这样的规则是合理的。否则,您就可以使用指针来修改被认为是常量的数据。
这些规则的实践结果是:像show_array()这样的函数可以接受普通数组和常量数组的名称作为实际参数,因为两种参数都可以赋给指向常量的指针:
show_array(rares,5); //合法
show_array(locked,4); //合法
但是,像mult_array()这样的函数不能接受常量数组的名称作为参数:
mult_array(rates,5,1.2); //合法
mult_array(locked,4,1.2); //非法
因此,在函数参量定义中使用const,不仅可以修护数据,而且使函数可以使用声明为const的数组。
const还有很多的用法。例如,您可以使用关键字const来声明并初始化指针,以保证指针不会指向别处,关键在于const的位置:
double rates[5]={88.99, 100.12, 59.45, 183.11, 340.5};
double * const pc = rates; //pc指向数组的开始处
pc = &rates[2]; //不允许
*pc = 92.99; //可以,更改rates[0]的值
这样的指针仍然可以用来修改数据,但是它只能指向最初赋给它的地址。
最后,可以使用两个const来创建指针,这样的指针即不可以更改所指向的地址,也不可以修改所指向的数据:
double rates[5]={88.99, 100.12, 59.45, 183.11, 340.5};
const double * const pc = rates;
pc = &rates[2]; //不允许
*pc = 92.99; //不允许