【C Primer Plus第六版 学习笔记】第十章 数组和指针

有基础,进阶用,个人查漏补缺

  1. const声明可以把数据设置为只读,程序只能从数据中检索值,不能写入新值

  2. 数组必须初始化赋予初值,否则初值就是原来相应内存的现有值。这与储存类型有关,将在12章详细展开。对于其他储存类型,如果声明时未初始化,编译器会自动把他们的值设置为0

  3. 当初始化列表中的值少于数组元素个数时。编译器会把剩余值初始化为0。

    当初始化列表中的值多于数组元素个数时。编译器会报错。

    也就是说,如果不初始化数组,数组元素储存的都是垃圾值;如果初始化了部分数组,剩余元素会被初始化为0

  4. C99中,可以只初始化某个指定数组元素,其他的会被自动初始化为0;传统的C语言中,必须初始化最后一个元素之前的所有元素,才能初始化它

  5. 如果再次初始化指定元素,最后的初始化会取代之前的初始化

  6. 如果声明时未指定数组元素大小,编译器会把数组的大小设置为足够装得下初始化的值,如int a[]={1,[3]=23},则数组a的元素共有4个

  7. 数组元素赋值:要使用循环给数组元素依次赋值

    1. 不允许把数组作为一个单元赋给另一个数组

    2. 除了初始化之外不允许使用花括号列表的形式赋值

      int a[4];
      int b[4] = {1, 3, 4, 5};
      a = b;                     //不允许
      a[4] = b[4]                //下标越界
      a[4] = {1, 3, 4, 5};       //不起作用
      
  8. 数组越界,编译器不会报错(出于C信赖程序员原则),但是一旦数组越界,会导致程序改变其他变量的值,甚至异常中止

  9. 声明数组时只能在方括号中使用整型常量表达式

    int n = 5, m = 8;
    float a1[5];
    float a2[5*2+1];
    float a3[sizeof(int)+1];
    float a4[-4];//不可以,数组大小必须大于0
    float a5[0];//不可以,数组大小必须大于0
    float a6[2.5];//不可以,数组大小必须为整数
    float a7[(int)2.5];//可以,已被强制转化为整数
    float a8[n];//C99前不允许
    
  10. 多维数组的初始化也是基于一维数组

  11. 数组名是数组首元素的地址,即数组a == &a[0]

  12. 在C语言中,指针+1指的是增加一个存储单元;对数组而言,意味着下一个元素的地址,而不是下一个字节的地址。所以必须声明指针的对象类型。

    short dates[4];
    short * pti;
    pti = dates;//把数组地址赋给指针
    dates+2 == &dates[2];//相同的地址
    *(dates+2) == dates[2];//相同的值
    
    *(dates+2)  //第3个元素的值
    *dates+2    //第1个元素的值+2
    
  13. 关于函数形参,只有在函数原型或者函数定义头中,才可以用int ar[]代替int * ar。

    多维数组可以int sum(int ar[][COL]),或者int sum(int [][COL]),或者int sum(int (*ar)[][COL]),不允许int ar[][]。

    注意,在函数形参int ar[ROW][COL]中,ROW会被忽略。

    延伸到n维数组中,最左边方括号中的值会被忽略,因为第一对方括号只用于表示这是一个指针。

    /*声明函数*/
    int sum2d_1(int rows, int cols, int ar[rows][cols]);//ar是一个变长数组
    int sum2d_2(int ar[rows][cols], int rows, int cols);//无效顺序,rows和cols要放在前面
    int sum2d_3(int, int, int ar[*][*]);//可以省略形参名字,但是必须用*代替省略的维度
    
  14. 数组和指针的大小:(还是不懂为什么)(数组指针、指针数组分不清)

    #include <stdio.h>
    #define SIZE 4
    int sun(int ar[], int n);
    int main(void)
    {
    	int marbles[SIZE] = {1, 3, 2, 4};
    	long answer;
    
    	answer = sum(marles, SIZE);
    	printf("The total number of marbles is %ld.\n", answer);
    	printf("The size of marbles is %zd bytes.\n", sizeof marbles);
    	//输出sizeof marbles=4*4=16(marbles内含4个int,每个int占4字节)
    	return 0;
    }
    
    int sum(int ar[], int n)//此处的形参里的数组名字其实只是一个指针,指向数组
    {
    	int i;
    	int total = 0;
    	
    	for(i=0; i<n; i++)
    	{
    		totla += ar[i];
    	}
    	printf("The size of ar is %zd bytes.\n", sizeof ar);
    	//输出sizeof ar=8
    	//因为ar不是数组本身,而是**一个指向marbles数组首元素的指针**,而该系统(x64)用8字节储存地址
    	return total;
    }
    
  15. 利用结合律

    totla += *start;
    start++;
    //相当于
    total += *start++;
    

    一元运算符和++优先级相同,但结合律是从右到左计算,所以start++先计算,在start,即指针start先递增后指向。使用后缀形式,意味着先把指针指向位置上的值加到total上,再递增指针。

    1. 如果是*++start,则先递增指针,再使用地址上的值
    2. 如果是(*start)++,则先使用start指向的值,再递增该数值,而不是递增指针

    在这里插入图片描述

  16. 地址应该和指针类型兼容,如不能把double的地址赋给指向int的指针

  17. 指针操作:

    #include <stdio.h>
    int main(void)
    {
    	int urn[5] = {100, 200, 300, 400, 500};
    	int *ptr1, *ptr2, *ptr3;
    
    	ptr1 = urn;//把首地址赋给ptr1
    	ptr2 = &urn[2];//把第3个元素的地址赋给ptr2,&为取地址符
    	/*所以*/
    	//ptr1为初值地址,*ptr1为初值100
    	//&ptr1为指针ptr1的指针,本段后有详细解释
    
    	ptr3 = ptr1 + 4;//等价于&urn[4],即第5个元素的地址
    	//指针加一个整数,指针必须是第一个运算对象,整数是第二个
    	
    	ptr1++;//相当于ptr1的值加4,因为我们的系统int为4字节,ptr1指向urn[1]
    	//但是,&ptr1的值依旧不变,毕竟变量不会因为数值发生变化就移动位置
    	ptr1--;//恢复为初始值
    
    	ptr2-ptr1;//得2,求差的两个指针分别指向同一个数组的不同元素,求出两个元素之间的距离
    	//ptr2-ptr1 == 2,意思是这两个指针所指向的两个元素相隔两个int,而不是2字节
    
    }
    
  18. 指针的指针:这里参考指针的指针(简单易懂)

    1. 普通情况下的指针,内存分配如下

      int a = 12;
      int *b = &a;
      

      在这里插入图片描述

    2. 指针的指针

      c = &b;
      //即
      int a = 12;
      int *b = &a;
      int **c = &b;
      

    在这里插入图片描述

    双重间接访问

    在这里插入图片描述

  19. 千万不要解引用未初始化的指针

    int *pt;//未初始化的指针
    *pt = 5;//严重错误
    

    把5储存在pt指向的位置,但是未初始化的pt的地址值是一个随机值,所以5不知道将储存在何处。可能会导致擦写数据或代码,或者程序崩溃。

    所以在使用指针之前,必须先用已分配的地址初始化它。

  20. 如果编写的函数不用修改数组,那么在声明数组形参时建议使用const

  21. const:

    1. #define可以创建类似功能的符号常量,但是const的用法更加灵活,可以创建const数组、const指针和指向const的指针

    2. 指向const的指针通常用于函数形参中,表明该函数不会使用指针改变数据

    3. 指针赋值和const的一些注意事项,看无效的那一条就行了

      int a[3] = {1, 2, 3};
      const int locked[3] = {1, 1, 1};
      const int * pc = a;//初始化有效
      //把const数据,或者非const数据的地址初始化为指向const的指针是合法的
      
      //赋值给指向const的指针是合法的
      pc = locked;       //有效
      pc = &a;           //有效
      
      /*只能把非const数据的地址赋值给**普通**指针*/
      int * pnc = a;     //初始化有效
      **pnc = locked;      //无效,否则就可以通过指针修改const int locked[3]的值了**
      pnc = &a[2];       //有效
      	
      #define LIMIT 20
      const int LIM = 50;
      static int data1[LIMIT];    //有效
      static int data2[LIM];      //无效
      const int LIM2 = 2 * LIMIT; //有效
      const int LIM3 = 2 * LIM;   //无效
      
    4. const的其他用法:

      int a[3] = {1, 2, 3};
      
      //用法1:声明并初始化一个不能指向别处的指针
      int * const pc1 = a;  //const的位置是关键,pc1指向数组的开始
      pc1 = &a[2];          //不允许,**指针在初始化时就确定了只能指向数组的开始**
      *pc1 = 11;            //允许,更改a[0]的值 
      
      //用法2:可以使用const两次,该指针不能更改指向位置,也不能更改指向位置上的值
      const int * const pc2 = a;
      pc2 = &a[2];          //不允许
      *pc2 = 11;            //不允许
      
  22. 指针和多维数组

    有多维数组 int a[4][2](首选数组表示法)

    1. a是该数组首元素的地址,即a == &a[0]

      a[0]本身是一个内含2个元素的数组,所以a[0]是该数组首元素的地址,即a[0] == &a[0][0]

      所以*a == a[0] == &a[0][0]

      所以**a == *a[0] == a[0][0]

      所以a是地址的地址

      a[0]+1,其值+4(一个int数组的元素大小为4字节);a+1,其值+8(a为内含有2个元素的int数组的首元素地址,大小为8字节)

    2. a[2][1] == *( *(a+2) + 1):

      a+2 ——二维数组的第3个元素(即第3个一维数组)的地址

      *(a+2) ——二维数组的第3个元素(即第3个一维数组)的首元素(int数据)的值

      *(a+2) + 1 ——二维数组的第3个元素(即第3个一维数组)的的第2个元素的地址

      ((a+2) + 1) ——二维数组的第3个元素(即第3个一维数组)的的第2个元素(int数据)的值,即数据第3行第2列的值

    3. 指向二维数组的指针的声明:

      int (* pz)[2];//先(),再[],所以是pz指向一个内含两个int数据的数组
      int * px[2];//先[],再*,所以px是一个内含两个int数据的数组,有两个指针指向int的值
      
  23. 两个类型的指针不之间能强制类型转换

  24. 过后再看10.7.2 指针的兼容性

  25. 复合字面量:

    1. 具有代表数组和结构内容的作用

    2. 复合字面量的类型名

      int a[2] = {10, 20}; //普通数组声明
      (int [2]){10, 20};   //复合字面量,类型名为int [2]
      //复合字面量会自动计算元素个数
      (int []){10, 20}; 
      
      //多维数组
      (int [2][4]){{1,3,4}, {3,4,5}};
      
    3. 用法:

      1. 使用指针记录地址

        int (* pt1)[3];
        pt1 = (int [2][4]){{1,3,4}, {3,4,5}}; //注意不能先创建再使用
        
      2. 作为实参进行传递,不用专门创建数组

        int sum(const int ar[], int n);
        ...
        int total;
        total = sum((int []){1,3,4,5}, 4);
        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值