示例
//Visual Studio中加上这句才可以使用scanf() //否则只能使用scanf_s() #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdbool.h> #define SIZE 5 void show_array(const double ar[], int n); void mult_array(double ar[], int n, double mult); //argc: 参数个数 argv[]: 参数数组 int main(int argc, char *argv[]) { 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); system("pause"); return 0; } /* 显示数组的内容 */ void show_array(const double ar[], int n) { //形参ar加上const后,在函数中修改数组元素的值,将会引起报错。 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; }
const的其他内容
1、使用const创建变量
const double PI = 3.14159;
const int nochange = 12;
//不可修改数组的值
const int days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
虽然用#define指令可以创建类似功能的符号常量,但是const的用法更加灵活。可以创建const数组、const指针和指向const的指针。
C99标准中在一条声明中多次使用同一个限定符,多余的限定符将被忽略,例如const const const int n = 6;等效于const int n = 6;这允许我们编写类似下面的代码:
typedef const int zip;
const zip q = 8;
void ofmouth(int * const a1, int * restrict a2, int n); //C99之前的风格
void ofmouth(int a1[const], int a2[restrict], int n); //C99允许,在声明函数形参时,指针表示法和数组表示法都可以使用这两个限定符。
duble stick(double ar[static 20]); //C99允许,参数是一个指向数组首元素的指针,且该数组至少有20个元素。
2、使用const保护数组
#define MONTHS 12
const int days[MONTHS] = {31,28,31,30,31,30,31,31,30,31,30,31};
days[9] = 44; /* 编译错误 */
double rates[5] = {88.9,100.12,59.45,183.11,340.5};
const double * pd = rates; //pd指向数组首元素
*pd = 29.89; // 不允许
pd[2] = 222.22; //不允许
rates[0] = 99.99;//允许,因为rates未被const限定
pd++; //允许
指向const的指针通常用于函数形参中,表明该函数不会使用指针改变数据。例如
void show_array(const double *ar, int n);
关于指针赋值和const需要注意一些规则。首先,把const数据或非const数据的地址初始化为指向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];//有效
然而,只能把非const数据的地址赋给普通指针:
double * pnc = rates; //有效
pnc = locked; //无效
pnc = &rates[3];//有效
这个规则非常合理,否则,通过指针就能改变const数组中的数据。
show_array(rates, 5); //有效
show_array(locked, 4); //有效
因此,对 函数的形参使用const不仅能保护数据,还能让函数处理const数组。
另外,不应该把const数组名作为实参传递给mult_array()这样的函数:
mult_array(rates, 5, 1.2); //有效
mult_array(locked, 4, 1.2); //不要这样做
C标准规定,使用非const标识符(如,mult_array()的形参ar)修改const数据(如,locked)导致的结果是未定义的。
const还有其他的用法。例如,可以声明并初始化一个不能指向别处的指针,关键是const的位置:
double * const pc = rates; //pc指向数组的开始
pc = &rates[2]; //不允许,因为该指针不能指向别处
*pc = 92.99; //没问题 -- 更改rates[0]的值
可以用这种指针修改它所指向的值,但是它只能指向初始化时设置的地址。
最后,在创建指针时还可以使用const两次,该指针既不能更改它所指向的地址,也不能修改指向地址上的值:
const double * const pc = rates;
pc = &rates[2]; //不允许
*pc = 92.99; //不允许
3、在指针和形参声明中使用const
const float * pf; //pf指向的值不能被修改,而pf本身的值可以改变
float * const pt; //pt本身的值不能更改,但它所指向的值可以改变
const float * const ptr; //指针本身及它指向的值都不能改变
float const * pfc; //与const float * pfc;相同
把const放在类型名之后、*之前,说明该指针不能用于改变它所指向的值。简而言之,const放在*左侧任意位置,限定了指针指向的数据不能改变;const放在*的右侧,限定了指针本身不能改变。
4、对全局数据使用const
使用全局变量是一种冒险的方法,因为这样做暴露了数据,程序的任何部分都能更改数据。如果把数据设置为const,就可避免这样的危险,因此用const限定符声明全局数据很合理。可以创建const变量、const数组和const结构。
然而,在这文件间共享const数据要小心。可以采用两个策略。第一,遵循外部变量的常用规则,即在一个文件中使用定义式声明,在其他文件中使用引用式声明(用extern关键字):
/* file1.c --定义了一些外部const变量 * const double PI = 3.14159; const char * MONTHS[12] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
/* file2.c --使用定义在别处的外部const变量 */ extern const double PI; extern const * MONTHS [];另一种方案是,把const变量放在一个头文件中,然后在其他文件中包含该头文件:
/* constant.h --定义了一些外部const变量 */ static const double PI = 3.14159; static const char * MONTHS[12] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
/* file1.c --使用定义在别处的外部const变量 */ #include "constant.h" /* file2.c --使用定义在别处的外部const变量 */ #include "constant.h"这种方案必须在头文件中用关键字static声明全局const变量。如果去掉static那么在file1.c和file2.c中包含constant.h将导致每个文件中都有一个相同标识符的定义式声明,C标准不允许这样做(然而,有些编译器允许)。实际上,这种方案相当于给每个文件提供了一个单独的数据副本。由于每个副本只对该文件可见,所以无法用这些数据和其他文件通信。不过没关系,它们都是完全相同(每个文件都包含相同的头文件)的const数据(声明时使用了const关键字),这不是问题。