• C Primer Plus(十)


    第十章 数组和指针

    10.1 数组

    数组是由一系列类型相同的元素构成。
    数组声明中包括数组元素的数目和元素的类型。

    10.1.1 初始化

    有时需要使用只读数组,也就是程序从数组中读取数值,但是程序不向数组中写数据。在这种情况下,初始化的时候可以使用关键字const。
    与普通变量相似,在初始化之前数组元素的数值是不定的。
    当数值数目少于数组元素数目时,多余的数组元素被初始化为0。如果不初始化数组,数组元素和未初始化的普通变量一样,其中存储的是无用的数值;
    但是如果部分初始化数组,未初始化的元素则被设置为0。
    如果初始化列表中的项目个数大于数组大小,编译器会认为这是一个错误。
    您可以省略括号中的数字,从而让编译器自动匹配数组大小和初始化列表中的项目数目。

    1. #include<stdio.h>
    2. int main(void)
    3. {
    4. const int days[]={31,28,31,30,31,30,31,31,30,31,30,31};
    5. int index;
    6. for(index=0;index<sizeof days/sizeof days[0];index++)
    7. printf("month %2d has %d days.\n",index+1,days[index]);
    8. return 0;
    9. }

    由于人工计算容易出错,可以让计算机来计算数组的大小。运算sizeof给出其后的对象或类型的大小。sizeof days是整个数组的大小(以字节为单位),sizeof days[0]是一个元素的大小。

    10.1.2 指定初始化项目(C99)

    此特性允许选择对某些元素进行初始化。例如:
    int arr[6]={[5]=212};
    程序如下:

    #include<stdio.h>
    #define MONTHS 12
    int main(void)
    {
    int days[MONTHS]={31,28,[4]=31,30,31,[1]=29};
    int index;
    for(index=0;index<MONTHS;index++)
    printf("month %2d has %d days.\n",index+1,days[index]);
    return 0;
    }

    如果多次对一个元素进行初始化,则最后的一次有效。
    C不支持把数组作为一个整体来进行赋值,也不支持用花括号起来的形式进行赋值(初始化的时候除外)。
    注意使用数组的时候,索引不能超过数组的边界。编译器不检查索引的合法性。不检查边界能够让C程序的运行速度更快。
    声明数组时在方括号内只能使用整数常量表达式。sizeof表达式被认为是一个整数常量,而一个const值却不是整数常量。并且该表达式的值必须大于0。
    C99引入变长数组主要是为了使C更适于做数值计算。

    10.2 多维数组

    二维数组的声明方法:float rain[5][12];
    rain是一个包含5个元素的数组,每个元素的类型是float[12];rain[0][0]是一个float数。
    也可以把rain看作是一个二维数组,它包含有5行,每行12列。改变第二个下标,可以沿着一行移动,改变第一个下标,可以沿着一列垂直移动。
    初始化二维数组可以例如float[5][12]={{.....},{.....},{.....},{.....},{.....}};
    每个数值列表都用花括号括起来。第一个列表给赋值给数组的第一行。初始化时候也可以省略内部的花括号,只保留最外面的一对花括号。只要保证数值的个数正确,若不够,则后面的元素被初始化为0。
    同样适用于三维数组。

    10.3 指针和数组

    数组标记实际上是一种变相使用指针的形式。数组名同时也是该数组首元素的地址。如果fizny是一个数组,则finzy==&finzy[0];两者都是常量。
    在C中对一个指针加1的结果是对该指针增加1个存储单元(它指向的对象的字节大小)。对于数组而言,地址会增加到下一个元素的地址,而不是下一个字节。
    finzy+2==&finzy[2];*(finzy+2)==finzy[2];
    注意区分*(finzy+2)和*finzy+2:间接运算符*的优先级高于+,因此后者等价于:*(finzy)+2,前者为数组第三个元素的值。
    在函数原型中,可以用int *ar代替 int ar[];
    将数组名传递给函数的形参时,数组大小无法得知。可以有两种方法结解决
    1.在函数代码中协商固定的数组大小。2.把数组大小作为第二个参数传递给函数。
    原型允许省略名称,定义函数时不可以省略。

    10.3.1 使用指针参数

    向函数传递数组信息时,可以传递两个指针,一个指明数组的起始地址,一个指明数组的结束地址。例如

    1. #include<stdio.h>
    2. #define SIZE 10
    3. int sump(int *start,int *end);
    4. int main(void)
    5. {
    6. int marbles[SIZE]={20,10,5,39,4,16,19,26,31,20};
    7. long answer;
    8. answer=sump(marbles,marbles+SIZE);
    9. printf("The total number of marbles is %ld.\n",answer);
    10. return 0;
    11. }
    12. int sump(int *start,int *end)
    13. {
    14. int sum=0;
    15. while(start<end)
    16. sum+=*start++;
    17. return sum;
    18. }

    end实际指向的位置实在数组最后一个元素之后。C保证在为数组分配存储空间的时候,指向数组之后的第一个位置的指针也是合法的。
    *start++:一元运算符*和++具有相等的优先级,但它在结合时是从右向左进行的。尽管*start++合法,但为了清晰起见应该使用*(start++)。
    只有当start是指针变量时,才可以使用++这样的表达式。

    10.4 指针操作

    下面例举了可对指针变量执行的基本操作:
    赋值----可以把一个地址赋给指针。通常用数组名或地址运算符&来进行地址赋值。(地址应该和指针类型兼容)
    求值----运算符*可取出指针指向地址中存储的数值。
    取指针地址----指针变量同其它变量一样具有地址和数值,使用运算符&可以得到存储指针本身的地址。
    将一个整数加给指针----整数会和指针所指类型的字节数相乘,然后所得的结果会加到初始地址上。
    增加指针的值----可以通过一般加法或增量运算符来增加一个指针的值。
    从指针中减去一个数----指针必须是第一个操作数,或者是一个指向整数的指针。
    减小指针的值----指针可以做减量运算。
    求差值----通常对分别指向同一个数组内两个元素的指针求差值,以求出元素之间的距离,差值的单位是相应类型的大小。
    比较----前提是两个指针具有相同的类型。
    C保证指向数组元素的指针和指向数组后的第一个地址的指针是有效的。但如果指针超出了这个范围,后果将是未知的。
    不能对未初始化的指针取值。这样的话可能会覆盖程序数据或者代码,甚至导致程序的崩溃。
    当创建一个指针时,系统只飞陪了用来存储指针本身的内存空间,并把分配用来存储数据的内存空间。

    10.5 保护数组内容

    因为向函数传递的是数组,可能会破坏原始数据的完整性。
    为此,可以在函数原型和定义的形式参量声明中使用关键字const。

    10.5.1 有关const的其它内容

    指向常量的指针不能用于修改数值。
    double rates[5]={88.99,100.12,59.45,183.11,340.5};
    const double *pd=rates;
    *pd=29.89;                //不允许
    pd[2]=222.22;           //不允许
    rates[0]=99.99;         //允许
    pd++                       //允许
    将常量或非常量数据的地址赋给指向常量的指针是合法的。
    只有非常量数据的地址才可以赋给普通的指针,否则,您就可以使用指针来修改被认为是常量的数据。
    const double locked[4]={0.08,0.075,0.0725,0.07}; double *pnc; pnc=locked;      //非法的

    也可以使用关键字const来声明并初始化指针,以保证指针不会指向别处,关键在于const的位置。例如 double *const pc=rates;
    也可以使用两个const来创建指针,这个指针既不可以更改所指向地址,也不可以修改所指向数据。 const double *const pc=rates;

    10.6 指针和多维数组

    int zippo[4][2];
    数组名zippo是数组首元素的地址,所以zippo的值和&zippo[0]相同。另一方面zippo[0]本身也是包含两个整数的数组,因此zippo[0]的值和其首元素的地址&zippo[0][0]相同。zippo[0]是一个整数大小对象的值,而zippo是两个整数大小对象的地址。因为整数和两个整数组成的数组开始于同一个地址,因此zippo和zippo[0]具有相同的数值。
    对一个指针加1,会对原来的数值加上一个对应类型大小的数值。因此zippo+1和zippo[0]+1的结果不同。
    *(zippo[0])代表其首元素zippo[0][0]的值,*zippo代表其首元素zippo[0]的值,即&zippo[0][0]。**zippo等价于*&zippo[0][0]即zippo[0][0]。
    zippo[2][1]的等价指针符号表示为*(*(zippo+2)+1);

    10.6.1 指向多维数组的指针

    int (* pz)[2]; //pz指向一个包含两个int值的数组
    int * pz[2];   //创建一个指针数组

    int *pt; int ar[3][2];int **pd;
    pd=&pt;                //合法,pd是指向指针的指针,&pt指向指针pt,pt是一个指向int元素的指针
    *pd=ar[0];            //合法,*pd指向一个元素的指针,ar[0]是指向ar[0][0]的指针。
    pd=ar;                 //非法

    int * p1;
    const int * p2;
    const int ** pp2;
    p1=p2;             //非法
    p2=p1;            //合法
    pp2=&p1         //非法
    把const指针赋给非const指针是错误的,但是把const指针赋给const指针是允许的,这样的赋值有一个前提:只进行一层间接运算。
    声明多维数组函数的方法: void somefunction(int (* pt)[4]);或者 int sum2(int ar[][4],int rows);
    也可以用 int sum2(int ar[3][4],int rows);但是3将被忽略。
    一般地,声明N维数组的指针时,除了最左边的方括号可以留空之外,其它都需要填写数据。

    10.7 变长数组(VLA)

    C99标准引入了变长数组,它允许使用变量定义数组各维。例如
    int quarters=1;
    int regions=5;
    double sales[regions][quarters];
    变长数组的限制:VLA必须是自动存储类的,这意味着它们必须在函数内部或作为函数参量声明,而且声明时不可以进行初始化。
    变并不表示在创建数组后,您可以修改其大小。它的意思是说其维数大小可以用变量来指定。
    int sum2d(int rows, int cols, int ar[rows][cols]);
    C99标准规定,可以省略函数原型中的名称;但是如果省略,则需要用星号来代替省略的维数。
    int sum2d(int , int , int ar[*][*]);

    10.8 符合文字

    下面是一个复合文字,创建了一个包含两个int值的无名称数组:
    (int [2]){10,20};
    初始化一个复合文字时也可以省略数组大小
    (int []){50,20,90};
    由于这些复合文字没有名称,因此不可能在一个语句中创建它们,然后在另一个语句中使用。而是必须在创建它们的同时通过某种方法来使用它们,一种方法是使用指针保存其位置。
    int *pt1;
    pt1=(int [2]){10,20};

  • 相关阅读:
    北京东城区社保转出业务个人办理图解流程,需要的朋友可以参考
    微信自定义菜单代码实现
    那你就是不对称加密了
    数字签名和数字认证
    微信认证开发教程
    Eclipse配置Maven
    php随笔
    让SAE下的wordpress支持文件上传
    HTTP Header 详解
    浅谈JS DDoS攻击原理与防御
  • 原文地址:https://www.cnblogs.com/tuifeideyouran/p/3120280.html
Copyright © 2020-2023  润新知