找回密码
 register

QQ登录

只需一步,快速开始

查看: 34|回复: 0

[C语言] 保护数组中的数据(10.6):对形式参数使用const

[复制链接]

[C语言] 保护数组中的数据(10.6):对形式参数使用const

[复制链接]
  • 打卡等级:热心大叔
  • 打卡总天数:121
  • 打卡月天数:17
  • 打卡总奖励:120
  • 最近打卡:2025-02-18 02:05:23
Waylee

主题

0

回帖

1万

积分

仙帝

积分
13983
Waylee 2025-2-11 11:32 | 显示全部楼层 |阅读模式 | Safari| iPhone iOS 18.3.0

马上注册,查看网站隐藏内容!!

您需要 登录 才可以下载或查看,没有账号?register

×

10.6 保护部数组中的数据

编写一个处理基本类型(如,int)的函数时,要选择是传递int类型的值还是传递指向int的指针。通常都是直接传递数值,只有程序需要在函数中改变该数值时,才会传递指针。对于数组别无选择,必须传递指针,因为这样做效率高。如果一个函数按值传递数组,则必须分配足够的空间来储存原数组的副本,然后把原数组所有的数据拷贝至新的数组中。如果把数组的地址传递给函数,让函数直接处理原数组则效率要高。传递地址会导致一些问题。C通常都按值传递数据,因为这样做可以保证数据的完整性。如果函数使用的是原始数据的副本,就不会意外修改原始数据。但是,处理数组的函数通常都需要使用原始数据,因此这样的函数可以修改原数组。有时,这正是我们需要的。例如,下面的函数给数组的每个元素都加上一个相同的值:

void add_to(double ar[], int n, double val)
{
    int i;
    for (i = 0; i < n; i++)
        ar[i] += val;
}

因此,调用该函数后,prices数组中的每个元素的值都增加了2.5:
add_to(prices, 100, 2.50);
该函数修改了数组中的数据。之所以可以这样做,是因为函数通过指针直接使用了原始数据。

然而,其他函数并不需要修改数据。例如,下面的函数计算数组中所有元素之和,它不用改变数组的数据。但是,由于ar实际上是一个指针,所以编程错误可能会破坏原始数据。例如,下面示例中的ar[i]++会导致数组中每个元素的值都加1:

int sum(int ar[], int n) // 错误的代码
{
  int i;
  int total = 0;
  for (i = 0; i < n; i++)
    total += ar[i]++;
  // 错误递增了每个元素的值
  return total;
}

10.6.1 对形式参数使用const

在K&R C的年代,避免类似错误的唯一方法是提高警惕。ANSI C提供了一种预防手段。如果函数的意图不是修改数组中的数据内容,那么在函数原型和函数定义中声明形式参数时应使用关键字const。例如,sum()函数的原型和定义如下:

int sum(const int ar[], int n); /* 函数原型 */
int sum(const int ar[], int n) /* 函数定义 */
{
    int i;
    int total = 0;
    for( i = 0; i < n; i++)
        total += ar[i];
    return total;
}

以上代码中的const告诉编译器,该函数不能修改ar指向的数组中的内容。如果在函数中不小心使用类似ar[i]++的表达式,编译器会捕获这个错误,并生成一条错误信息。
这里一定要理解,这样使用const并不是要求原数组是常量,而是该函数在处理数组时将其视为常量,不可更改。这样使用const可以保护数组的数据不被修改,就像按值传递可以保护基本数据类型的原始值不被改变一样。一般而言,如果编写的函数需要修改数组,在声明数组形参时则不使用const;如果编写的函数不用修改数组,那么在声明数组形参时最好使用const。
程序清单10.14的程序中,一个函数显示数组的内容,另一个函数给数组每个元素都乘以一个给定值。因为第1个函数不用改变数组,所以在声明数组形参时使用了const;而第2个函数需要修改数组元素的值,所以不使用const。

程序清单10.14 arf.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数组的值,但是并未使用return机制。

const的其他内容

我们在前面使用const创建过变量:
const double PI = 3.14159;
虽然用#define指令可以创建类似功能的符号常量,但是const的用法更加灵活。可以创建const数组、const指针和指向const的指针。
程序清单10.4演示了如何使用const关键字保护数组:

#define MONTHS 12
...
const int days[MONTHS] = {31,28,31,30,31,30,31,31,30,31,30,31};

如果程序稍后尝试改变数组元素的值,编译器将生成一个编译期错误消息:
days[9] = 44;        /* 编译错误 */

指向const的指针不能用于改变值。考虑下面的代码:

double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};
const double * pd = rates;     // pd指向数组的首元素

第2行代码把pd指向的double类型的值声明为const,这表明不能使用pd来更改它所指向的值:

*pd = 29.89;       // 不允许
pd[2] = 222.22;    // 不允许
rates[0] = 99.99;  // 允许,因为rates未被const限定

无论是使用指针表示法还是数组表示法,都不允许使用pd修改它所指向数据的值。但是要注意,因为rates并未被声明为const,所以仍然可以通过rates修改元素的值。另外,可以让pd指向别处:
pd++; /* 让pd指向rates[1] -- 没问题 */
指向const的指针通常用于函数形参中,表明该函数不会使用指针改变数据。例如,程序清单10.14中的show_array()函数原型如下:
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 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 *pnc = rates; // 有效
pnc = locked;            // 无效
pnc = &rates[3];         // 有效

这个规则非常合理。否则,通过指针就能改变const数组中的数据。
应用以上规则的例子,如show_array()函数可以接受普通数组名和const数组名作为参数,因为这两种参数都可以用来初始化指向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_arry()的形参ar)修改const数据(如,locked)导致的结果是未定义的。
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;        //不允许
您需要登录后才可以回帖 登录 | register

本版积分规则

雪舞知识库 | 浙ICP备15015590号-1 | 萌ICP备20232229号|浙公网安备33048102000118号 |天天打卡

GMT+8, 2025-2-22 02:08 , Processed in 0.105513 second(s), 7 queries , Redis On.

Powered by XueWu Licensed

Copyright © Tencent Cloud.

快速回复 返回顶部 返回列表