C语言【学习篇】六

指针

  • 变量与地址:变量名—抽象出来某块空间的别名 指针—绝对地址
  • 指针与指针变量:指针其实就是地址,因此指针并不改变。但是为了存储指针(理解为存储地址)那么就需要指针变量
    //举例解读指针变量的存储逻辑
    int i = 10;
    int *p = &i;//p存放i的地址,且i的类型为int
    int* *q = &p;//q存放p的地址,且p的类型为int* 由于p自身存放的是i的地址因此类型为int*
    //其实通过上述例子我们可以通过定义及初始化得到当前保存变量的类型是什么
    //但是我们仍然需要注意p本身的类型为int*
    //如何判断一个指针是几级指针?---直接看定义中有几个*
    //通俗的理解 定义过程中一旦使用&则指针变量必须在存储数据的原类型上多一层*
    //指针变量类型一定要与所指向变量的类型一致,因为在*操作过程中,编译器严格按照类型大小在内存中取出对应的数据
    
  • 直接访问与间接访问:访问任意一个变量(包括指针变量)其实都有两种方法 引用上述例子做说明
    • 直接访问:用变量名i进行访问
    • 间接访问:
      • 一级间接访问:*p
      • 二级间接访问:* (*q)
  • 空指针与野指针:
    • 空指针:NULL—初始化—错误操作百分百产生段错误
    • 野指针:当前指针指向空间不确定—可能产生段错误
  • 空类型:void *p = NULL—p可以接受任何类型的指针,也可以将值传递给任何指针 适用于不知道类型的情况
  • 指针运算:
    • & * 关系运算 ++ –
  • 指针与数组:我们这里说的指针本质上是指针变量
    • 指针与一维数组:
      int a[3] = {1,2,3};
      int *p = a;
      //a[i]: a[i] = *(a+i) = *(p+i) = p[i]
      //&a[i]: &a[i] = a+i = p+i = &p[i]
      //读取数组的大小---sizeof(a)/sizeof(*a)
      //注意上述的i都是移动了int型的大小而不是一个字节的大小
      
      • 上例子中a和p的区别在哪里呢?— 本质上讲a是地址常量而p是指针变量—a++(False)p++(True)
      • p++和p+1是否相同呢? — 不相同,p++对p值进行了改变而p+1对p自身值并没有变化
      • 匿名数组与指针—int *p = (int [3]){1,2,3};
    • 指针与二维数组:
      • 二维数组的指针需要分清楚行和列,譬如我们定义int a[2][3]中,a和a+1之间的地址将不会再相差一个int大小,此时会相差三个,通俗的理解便是,a此时是一个行指针,每次+1都会跳转一行
      • 我们是否可以使用int *p = a;呢?— 答案显然是否定的,因为,我们需要明确,p和p+1之间的地址仅仅相差一个int类型的大小,其更偏向于一个列指针。如何使得p能指向有效的类型呢,其实深度理解二维数组后,我们可以理解这样一个关系,对于多级指针而言 *其实就是代表着降级,因此,int *p = *a就是完全成立的,在理解上可以理解为p此时指向了a所在行的第一列。那么根据线性存储性及刚刚所说的指向问题,我们是否可以利用指针p达到一层循环遍历整个二维数组呢?
        int a[2][3] = {1,2,3,4,5,6};
          int i;
          int* p = *a;
          for(i = 0; i < 6; i++){
                printf("number = %d\n", *(*a+i));
          }
        
      • 对于二维数组我们更要理解其本质含义,其基地址为a,有行指针的功能,*a所存储的并不是我们数组中的任何数值,而是a数组中第1行第1列所在位置的地址。如下例所示
        printf("The addr of a[1][1] = %p\nThe value of a[1][1] = %d\n", *(a+1)+1, *(*(a+1)+1));
        //输出结果
        //The addr of a[1][1] = 0x16dca33e0
        //The value of a[1][1] = 5
        
    • 指针与字符数组:
      char* str1 = "hello_world";//指针指向了一个字符串常量
      char str2[] = "hello world";//字符串进入了字符数组
      printf("%d %d\n", sizeof(str1), strlen(str1));
      //8---取决于你使用的环境是多少个字节64位环境指针长度就是8字节 11
      printf("%d %d\n", sizeof(str2), strlen(str2));
      //12---存在\0因此多一位 11---不包含\0的原本长度 
      strcpy(str1, "hello");//非法,试图改变str1指向的字符串常量
      strcpy(str2, "hello");//合理,因为数组本来就是一种构造的变量
      str1 = "hello"//合理,将指针指向了新的字符串常量
      str2 = "hello"//非法,企图直接修改地址值
      
  • 指针数组与数组指针:对于二维数组我们明显看到的操作的不便捷性,因此,我们可以使用如下概念
    • 数组指针:本质上是一个指针,指针指向数组,其实就是行指针
      • 定义:[存储类型] 数据类型 ( * 指针名 )[下标] = 值 //下标要与二维数组的第二维一致
      • eg:int (*p)[3] — 理解: *p指向int[3]这样一个数组此时p+1跳跃了一个数组int[3]此时上面例子中的a相关操作可以全部被p代替。
    • 指针数组:本质上是一个数组,存放的数据类型都是指针
      • 定义:[存储类型] 数据类型* 数组名[长度]
      • eg:int* arr[3];
      • char* name[5] = {“hello”,“world”,“hello”,“world”}
  • const与指针:
    • const–把某些内容常量化const float pi = 3.14//不希望pi不改变同时也能语法检测–但是通过指针依然可以改
    • const int i 和 int const i 其实是等价的
    • 指针常量:当前指向的目标变量不可以变化,但是指针本身可以变化
      • const int *p 和 int const *p
      • const int *p = &i;
    • 常量指针:指针自身永远不能变化,但是指针指向的值可以变化
      • int *const p
      • int *const p = &i;
    • const int *const p = &i;—既不能改p也不能改 *p
    • 指针常量和常量指针要如何辨别—碰见const读常量碰见*读指针
    • 具体什么可变什么不可变—省略其他修饰 const p则必然指针不能变,const *p则必然 *p中的内容不能变
    • 但是需要注意的是,const只是限制了当前变量不能变化,其他方式访问该变量依然可以进行改变
  • 多级指针:
    • 理解二级指针存储的是什么,定义的类型是什么,二级指针存储的就是一级指针的地址
    • 二级指针的本质是,需要对原始的指针指向进行修改操作,改变当前指针所指向的数据,这就用到二级指针,比如在main函数中定义一个指针,现在在调用函数中我们需要修改这个指针的指向,那么我们的函数参数就是二级指针。更通俗的理解指针的用法就是—我希望直接在调用函数中改变这个变量,那么我不得不用指针进行修改,但如果这个变量本身就是指针,那么我们不得不通过指针的指针即二级指针进行相关的修改。
    • 二级指针的使用—如果是要改变指针指向则需要用*p进行,若只是遍历寻找相关数据则依然使用p->

你可能感兴趣的:(c语言,学习)