• C Primer Plus(6) 中文版 第10章 数组和指针 10.6 保护数组中的数据


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

    调用该函数后,prices数组中的每个元素都增加了val。
    该函数修改了数组中的数据。之所以可以这样做,是因为通过指针直接使用了原始数据。
    然而,其他函数并不需要修改数据。由于ar实际上是一个指针,所以编程错误可能会破坏原始数据。
    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。例如:
    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 += a[i];
        }    
        return total;

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

    /* displays array contents */
    void show_array(const double ar[], int n)
    {
        int i;
        
        for (i = 0; i < n; i++)
            printf("%8.3f ", ar[i]);
        putchar('\n');
    }

    /* multiplies each array member by the same multiplier */
    void mult_array(double ar[], int n, double mult)
    {
        int i;
        
        for (i = 0; i < n; i++)
            ar[i] *= mult;

    /* 输出:

    */

    因为两个函数的返回类型都是void,因此并未使用return来改变值的机制。
    10.6.2 const的其他内容
    利用const创建变量:
    const double PI = 3.1415;
    #define指令可以创建类似功能的符号常量,但是const的用法更加灵活。可以创建const数组、const指针和指向const的指针。
    使用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指向数组的首元素
    把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的指针通常用于函数形参中,表明该函数不会使用指针改变数据。例如:
    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 = {0.08, 0.075, 0.0725, 0.07};
    const double *pc = rates; //有效
    pc = locked; //有效
    pc = &rate[3]; //有效
    这个规则非常合理。否则,通过指针就能改变const数组中的数据。
    show_array( rates, 5 ); //有效
    show_array( locked, 4 ); //有效
    另外,不应该把const数组名作为实参传递给mult_array()这样的函数:
    multi_array( rates, 5, 1.2 ); //有效
    multi_array( locked, 4, 1.2 ); //不要这样做
    C标准规定,使用非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; //不允许 

  • 相关阅读:
    Web APIs——BOM
    springboot+vue.js+Elementui大众校友社交系统java校友会网站
    Python与CAD系列基础篇(十一)图形旋转、镜像、缩放
    新能源科学与工程专业概述
    kubectl
    Lru-k在Rust中的实现及源码解析
    数码打印流程【方向不干胶、纸卡、瓦楞、单张 {不涉及画册}】
    BM14 链表的奇偶重排
    npm、nrm两种方式查看源和切换镜像
    声音信号处理笔记(一)
  • 原文地址:https://blog.csdn.net/weixin_40186813/article/details/126251214