• 【C语言学习】《C Primer Plus》第10章 数组和指针


    学习总结

     

    1、数组初始化方式:

    int a[]={1,2,3}

    int a[SIZE]={1,2,3} //SIZE是宏定义,数组初始化个数不能大于SIZE,否则报错;当个数小

    //SIZE,自动补0;只定义不初始化,默认值是当前存储单元中已有的数值。

    int a[SIZE/不定长]={1,[3],2} //C99支持通过[int]=x来定义具体位置值,跳过的默认值为0。

     

    2、通过const修饰的数组为只读数组,数组的每个元素当成常量来处理,如:

    const int a[2]={1,2};

    const int b[2];

    a[1]=3; //编译不通过,无法修改,已有值2;

    b[1]=3; //编译不通过,无法修改,已有默认存储单元数值。

     

    3、数组赋值方式:

    int a[]={1,2,3}; //允许

    int a[2];a[1]=2; //允许

    int a[2]={1,2};int b[2];b=a //不允许

    int a[2];a[2]={1,2}; //不起作用

     

    4、数据边界问题:数组的计数器是从0开始的,在标准C中,编译器是不会检查索引的合法性,因为C认为编译器在运行时逐个检查每个索引的合法代码会导致程序运行速度变慢,因此C会认为程序员的代码是正确的,但C太天真了,事情没有完美的,肯定存在人为出错因素,因此问题就产生了。不同编译器对于这种问题会出现不同的错误,所以要特别注意。

     

    5、指定数组大小在C99之前是不允许通过变量定义,如

    int n=2;

    a[n]={1,2}; //C99前不允许,C99允许

     

    6、二维数组初始化:

    int a[2][3]={1,2,3,4,5,6}

    int a[2][3]={{1,2,3},{4,5,6}}

    按照二维表格排布,2相当于行数,3相当于列数。如a[2][3]={1,2,3,4,5,6}布局如下:

    1 2 3

    4 5 6

    如a[3][2]={1,23,4,5,6}布局如下:

    1 2

    3 4

    5 6

     

    7、指针提供了一种用来使用地址的符号方法。由于计算机的硬件指令很大程度上依赖于地址,所以指针能够类似于计算机底层的表达,使工作更加高效。特别的,指针能够很有效的处理数组,数据实际上是一种变相使用指针的形式:

    #include <stdio.h>
    int main(){
            int a[2]={1,2};
            printf("a=%p
    ",a);
            printf("a[0]=%p
    ",&a[0]);
            return 0;
    }

    打印结果:

    a=0x7fff38352af0

    a[0]=0x7fff38352af0

    其实,数组名是该数组首元素的地址。

     1 #include <stdio.h>
     2 int main(){
     3         int a[2]={1,2},*p;
     4         p=a;
     5         printf("&a=%p
    ",&a);
     6         printf("p=%p
    ",p);
     7         printf("a=%d
    ",*a);
     8         printf("p=%d
    ",*p);
     9         printf("---------------
    ");
    10         printf("a=%p
    ",(a+1));
    11         printf("a=%d
    ",*(a+1));
    12         p++;
    13         printf("p=%p
    ",p);
    14         printf("p=%d
    ",*p);
    15         return 0;
    16 }

    打印结果:

    &a=0x7fffa1a350a0

    p=0x7fffa1a350a0

    a=1

    p=1

    ---------------

    a=0x7fffa1a350a4

    a=2

    p=0x7fffa1a350a4

    p=2

    从以上程序可以看出,数组指针加1,并非指针地址值加1,而是指向数组下一个元素的地址。

     

    8、指针的基本操作:赋值、求值或取值、取指针地址、将一个整数加给指针、增加指针的值、从指针中减去一个整数、减小指针的值、求差值、比较。

     1 #include <stdio.h>
     2 
     3 int main(){
     4         int urn[5]={100,200,300,400,500};
     5         int *ptr1,*ptr2,*ptr3,*ptr4;
     6 
     7         printf("指针赋值
    ");
     8         ptr1 = urn;
     9         printf("ptr1=%p
    ",ptr1);
    10         ptr2 = &urn[2];
    11         printf("ptr2=%p
    ",ptr2);
    12 
    13         printf("指针求值或取值
    ");
    14         printf("(int)ptr1=%d
    ",*ptr1);
    15         printf("(int)ptr2=%d
    ",*ptr2);
    16 
    17         printf("取指针地址
    ");
    18         printf("&ptr1=%p
    ",&ptr1);
    19         printf("&ptr2=%p
    ",&ptr2);
    20 
    21         printf("将一个整数加给指针或增加指针的值
    ");
    22         ptr1++;
    23         printf("ptr1=%p
    ",ptr1);
    24         printf("(int)ptr1=%d
    ",*ptr1);
    25 
    26         printf("从指针中减去一个整数或减少指针的值
    ");
    27         ptr2--;
    28         printf("ptr2=%p
    ",ptr2);
    29         printf("(int)ptr2=%d
    ",*ptr2);
    30 
    31         printf("求差值");
    32         ptr3 = &urn[1];
    33         ptr4 = &urn[4];
    34         printf("ptr4-ptr3=%d
    ",ptr4-ptr3);
    35 
    36         printf("比较两个指针本身的值(非指针指向数据的值)");
    37         printf("ptr4>ptt3=%d
    ",ptr4>ptr3);
    38 
    39         return 0;
    40 }

    运行结果:

    指针赋值

    ptr1=0x7fff886c8570

    ptr2=0x7fff886c8578

    指针求值或取值

    (int)ptr1=100

    (int)ptr2=300

    取指针地址

    &ptr1=0x7fff886c8568

    &ptr2=0x7fff886c8560

    将一个整数加给指针或增加指针的值

    ptr1=0x7fff886c8574

    (int)ptr1=200

    从指针中减去一个整数或减少指针的值

    ptr2=0x7fff886c8574

    (int)ptr2=200

    求差值

    ptr4-ptr3=3

    比较两个指针本身的值(非指针指向数据的值)

    ptr4>ptt3=1

     

    9、函数形参为数组时,一般是通过传递指针,如果是使用数组形参,那么函数中必须分配足够存放一份原数组拷贝的存储空间。为了避免函数不对原数组进行修改,可以通过const关键字对形参进行修饰,需要理解的是这样使用const并不要求原始数组固定不变的;这只是说明函数在处理数据时,应把数组当作是固定不变的。使用const可以对数组提供保护,就像值传递可以对基本类型提供保护一样。

    int sum(int ar[],int n) //可以修改数组

    int sum(const int ar[],int n) //不可修改数组

     

    10、前面学习过可以使用const关键字来创建符号常量(const int PI=3.14159;),也可以是用#defined PI 3.15159来定义,但使用const还可以创建数组常量、指针常量以及指向常量的指针,使用const关键在于修饰的对象,修饰的对象不一样,效果也不同。

    场景1:修饰数组

    const int a[]={1,2,3,4,5};  //整个数组为常量数组,不可以修改。

    a[0]=10;  //不允许

    a[1]=11;  //不允许

    场景2:修饰常量指针(指向常量的指针,常量为形容词,指针为名词,这样看,常量指针本质是指针,常量修饰它,表示这个指针乃是一个指向常量的指针)

    int a[]={1,2,3,4,5};

    const int *p=a;   //p指向数组的开始处

    p[0]=10;  //不允许,因为指针指向常量,所以不能通过指针修改

    a[0]=10;  //允许,但能通过数组本身修改,因为数组本身不是常量(特别注意)

    场景3:修饰指针常量(指针是形容词,常量是名词。这回是以常量为中心的一个偏正结构短语。那么,指针常量的本质是一个常量,而用指针修饰它,那么说明这个常量的值应该是一个指针)

    int a[]={1,2,3,4,5};

    int * const p=a;

    p[0]=10; //允许,数组非常量数组

    p++;    //不允许,p为常量,不允许修改

    p=&a[3]; //不允许,p为常量,不允许修改

     

    11、二维数组a[4][2],其中a为数组首元素的地址,在本例中a首元素本身又是包含两个int的数组,如下所示:

    a[0][0]=11=0x7fff908c0c60 a[0][1]=12=0x7fff908c0c64

    a[1][0]=21=0x7fff908c0c68 a[1][1]=22=0x7fff908c0c6c

    a[2][0]=31=0x7fff908c0c70 a[2][1]=32=0x7fff908c0c74

    a[3][0]=41=0x7fff908c0c78 a[3][1]=42=0x7fff908c0c7c

    a=0x7fff908c0c60         //二维数组首地址,a指向的对象是一个int数组(这里是两个int)

    *a=0x7fff908c0c60        //*a就是一个int数组,跟a指向的是同一个int元素

    *a+1=0x7fff0f1eebb4      //*a指向对象是一个int元素,加1就是向当前递增一个int

    a+2=0x7fff908c0c70       //a指向的是一个int数组,加2就是向前地址两个int数组

    *(a+2)=0x7fff908c0c70     //先是a向前地址两个int数组,再指向当前一维数组的首地址

    *(a+2)+1=0x7fff908c0c74   //同*a

    *(*(a+2)+1)=32           //取出上面地址的值

     

    12、数组指针和指针数组的区别(参考百度网友解析):

    int a[3][4]这个无需多说,就是一个二维数组。

    int (*p)[4]就相当于int p[][4],它就是一个二维数组的指针,可以指向一个第二维度为4的二维数组。而a就是这样的数组,因而下面是合法的。

    p=a;

    int *p[3]是指针数组。说白了,就是定义了三个指针,分别为p[0],p[1],p[2]。可以将他们单独拿来使用。

    int a1,a2,a3;

    p[0]=&a1;

    p[1]=&a2;

    p[2]=&a3;

     

    13、相对于数值类型的赋值,指针之间的赋值会更加的严格:

    int n=5;

    int *pi;

    double x;

    double pd;

    x=n; //通过

    pd=pi; //不通过

    -------------------------例子分割线------------------------------------

    int *pt;

    int (*pa)[3];

    int ar1[2][3];

    int ar2[3][2];

    int **p2;

    pt = &ar1[0][0]; //pt是指向整数的指针,&a[0][0]也是指向整数的指针,所以合法

    pt = ar1[0];    //ar1[0]同样是指向整数的指针,所以合法

    pt = ar1;      // ar1是一个指向包含3位int数组的指针,和pt不一样,所以不合法

    pa = ar1;      //pa是一个指向包含3位int数组的指针,和ar1一样,所以合法。

    pa = ar2;      //ar2是一个指向包含2位int数组的指针,和pa不一样, 所以不合法

    p2 = &pt;     //p2是指向整数指针的指针,pt是指向整数的指针,所以&pt与p2类型相同

    *p2 = ar2[0];  //*p2是指向整数的指针,而ar2[0]同样是指向整数的指针,所以合法

    P2 = ar2;     //ar2是指向一个包含2位int数组的指针,与P2不一样,所以不合法。

    -------------------------例子分割线------------------------------------

    int *p1;

    const int *p2;

    const int **pp2;

    p1 = p2;    //非法把const指针赋值给非const指针

    p2 = p1;    //合法把非const指针赋值给const指针

    pp2= &p1;  //非法通过两层运算吧非const指针赋值给const指针,这样会引起赋值不安全详见P271例子。

    14、编程题(题12)

     1 #include <stdio.h>
     2 
     3 int main(){
     4         double d[3][5];
     5         int i;
     6         double sum1,sum2,sum3,maxDouble;
     7         printf("enter 5 double for first doubleArr:
    ");
     8         for(i=0;i<5;i++){
     9                 scanf("%lf",&d[0][i]);
    10         }
    11 
    12         printf("enter 5 double for second doubleArr:
    ");
    13         for(i=0;i<5;i++){
    14                 scanf("%lf",&d[1][i]);
    15         }
    16 
    17         printf("enter 5 double for third doubleArr:
    ");
    18         for(i=0;i<5;i++){
    19                 scanf("%lf",&d[2][i]);
    20         }
    21 
    22         printf("thank you for your scanf.
    
    ");
    23 
    24         sum1=0;
    25         for(i=0;i<5;i++){
    26                 sum1+=d[0][i];
    27                 if(i==0)
    28                         maxDouble=d[0][1];
    29                 if(d[0][i]>maxDouble)
    30                         maxDouble=d[0][i];
    31         }
    32         printf("average value of first doubleArr is %f
    ",sum1/5);
    33 
    34         sum2=0;
    35         for(i=0;i<5;i++){
    36                 sum2+=d[1][i];
    37                 if(d[1][i]>maxDouble)
    38                         maxDouble=d[1][i];
    39         }
    40         printf("average value of second doubleArr is %f
    ",sum2/5);
    41 
    42         sum3=0;
    43         for(i=0;i<5;i++){
    44                 sum3+=d[2][i];
    45                 if(d[2][i]>maxDouble)
    46                         maxDouble=d[2][i];
    47         }
    48         printf("average value of third doubleArr is %f
    ",sum3/5);
    49 
    50         printf("average value of all double data is %f
    ",(sum1+sum2+sum3)/3);
    51         printf("the max double is %f
    ",maxDouble);
    52 
    53 
    54         return 0;
    55 }

    运行结果:

    enter 5 double for first doubleArr:

    11

    12

    13

    14

    15

    enter 5 double for second doubleArr:

    21

    22

    23

    24

    25

    enter 5 double for third doubleArr:

    31

    32

    33

    34

    35

    thank you for your scanf.

     

    average value of first doubleArr is 13.000000

    average value of second doubleArr is 23.000000

    average value of third doubleArr is 33.000000

    average value of all double data is 115.000000

    the max double is 35.000000

  • 相关阅读:
    Ruby
    WebGL的第二个小程序
    wegGL的第一个小程序
    Node.js介绍
    接口隔离原则(Interface Sepreation Principle)
    参数
    字段/属性
    接口和抽象类
    javascript中的事件
    线性回归算法-4.多元线性回归算法
  • 原文地址:https://www.cnblogs.com/wcd144140/p/4616130.html
Copyright © 2020-2023  润新知