• 指针


    基础知识

    *星号:在C语言中表示乘号,也可以表示指针符号。

    &取地址符号:加载变量的前面可以取变量地址。

    左值:在赋值运算符左边,表示变量名所代表的内存空间

    右值:在赋值运算符的右边,表示变量名所代表的内存空间中存放的值

    什么是指针?

    答:指针本质上就是一个变量,完整的名字应该是指针变量,简称指针。一个指针变量存储的是一个内存地址(32位系统为4字节)。

    为什么需要指针?

    答:指针的存在是为了实现间接访问。间接访问(间接寻址)是CPU设计时候决定的,所以决定了汇编必须能实现间接寻址,在汇编之上的C语言也必须能实现间接寻址。对于更高级的语言诸如C#、java这类语言帮我们封装了,所以没有指针也可以实现间接寻址。

    指针变量的使用步骤:定义指针变量、关联指针变量、解引用。

    #include<stdio.h>
    
    int main(void)
    
    {
      int a=3;
      int *p;    //定义指针变量
      p = &a;   //关联指针变量
      *p =5    //解引用
    }

    在函数种定义指针变量时,遵循局部变量的一般规律。也就是如果定义时没有初始化p的值,则值是随机的(上次使用后未清0的值)。因此就引出了野指针的问题。

    什么是野指针?

    野指针是指指针所指向的位置是不可知的。在函数内部定义指针变量时,没有对变量初始化就会出现野指针的情况。这时候指针变量p的值是随机的,也就是指针指向的变量是随机的。可能出现如下情况,第一:指向一个不可访问的地址。(程序会触发段错误)

                     第二:指向一个可用的、而且没什么特别意义的空间。(程序不报错,但实际上错了。相当于错误被掩盖了)

                     第三:情况就是指向了一个可用的空间,而且这个空间其实在程序中正在被使用。(导致程序出现离奇错误)

    如何避免野指针?

    举例:

      int *p=NULL;  //第一点:定义指针时,同时初始化为NULL

      P = &a;     //第二点:在指针使用之前,将其赋值绑定给一个可用地址空间(假设之前已定义了一个变量a)

      if(NULL != P)  //第三点:在指针解引用之前,先去判断这个指针是不是NULL

      { 

        *P = 4;    

      }

      P = NULL;   //第四点:指针使用完之后,将其赋值为NULL

    一般的,在中小型程序中,自己水平可以把握的情况下,不必严格参照这个标准;但是在大型程序,或者自己水平感觉不好把握时,建议严格参照这个方法。    

    NULL是什么?

    #ifdef _cplusplus     // 定义这个符号就表示当前是C++环境
    #define NULL 0      // 在C++中NULL就是0
    #else
    #define NULL (void *)0 // 在C中NULL是强制类型转换为void *的0
    #endif

    const关键字与指针

    第一种:const int *p;    //指针变量p指向的是一个int类型的常量
    第二种:int const *p;    //指针变量p指向的是一个int类型的常量
    第三种:int * const p;    //指针变量p本身是一个常量,指向的那个值是个int型变量
    第四种:const int * const p;  //指针变量p是个常量,它指向的是一个int型常量

    数组中几个关键符号(a a[0] &a &a[0])的理解

    (1)a:数组名。a做左值时表示整个数组的所有空间,a不能做左值;a做右值表示数组首元素的首地址<==>&a[0];
    (2)a[0]:数组的首元素,也就是数组的第0个元素。做左值时表示数组第0个元素对应的内存空间(连续4字节);做右值时表示数组第0个元素的值;
    (3)&a:就是数组名a取地址,字面意思来看就应该是数组的地址。&a不能做左值;&a做右值时表示整个数组的首地址。
    (4)&a[0]字面意思就是数组第0个元素的首地址。不能做左值,做右值时表示数组首元素的地址。

    数组与指针

    (1)数组元素使用时不能整体访问,只能单个访问。访问方式有2种:数组形式和指针形式。
    (2)数组格式访问数组元素是:数组名[下标]; (注意下标从0开始)
    (3)指针格式访问数组元素是:*(指针+偏移量); 如果指针是数组首元素地址(a或者&a[0]),那么偏移量就是下标;指针也可以不是首元素地址而是其他哪个元素的地址,这时候偏移量就要考虑叠加了。
    (4)数组下标方式和指针方式均可以访问数组元素,两者的实质其实是一样的。在编译器内部都是用指针方式来访问数组元素的,数组下标方式只是编译器提供给编程者一种壳(语法糖)而已。所以用指针方式来访问数组才是本质的做法。

    举例:

    int a[5] = {1, 2, 3, 4, 5};
    int *p;
    p = a;
    printf("*(p+1) = %d.
    ", *(p+1));

    类型匹配

    char *p;  int a[5];  p = a;    类型匹配,这里的a做右值表示&a[0]是数组首元素首地址
    int *p;   int a[5];   p = &a;  类型不匹配,这里&a做右值,a本身表示整个数组,取地址符&加在前面表示数组的地址

    define与typedef关键字

    #define dpChar char *
    typedef char *tpChar;

    dpChar p1, p2; sizeof(p1) sizeof(p2)
    tpChar p3, p4; sizeof(p3) sizeof(p4)

    变量传参实际上只是把值赋值了一份传进去,也就是传说中的传值调用

    void func1(int a)
    
    {
    printf("a=%d.
    ",a);
    }
    
    int main(void)
    {
      int a=4;
      func1(a);
      return 0;
    }

     函数传参

    (1)数组名作为函数形参传参,实际传递的是整个数组首元素的首地址,因为传参为传值。void func(int a[]){}

    (2)指针作为函数形参传参,和数组作为函数形参的实现方式一样。void func1(int *a){}

    (3)结构体变量作为函数形参,和普通变量传参时表现一样。由于结构体很大我们通常也是传地址的

    一般地函数传参的时候,分为输入性参数和输出型参数,我们通常在输入性参数加上const 关键字,表明他不需要更改。

     (本文内容参考了朱有鹏C语言高级专题)

  • 相关阅读:
    Unity3D 事件
    Unity3D 动画回调方法
    Unity3D优化总结
    Unity3D 第一人称控制器 C#脚本
    TCP/IP与IETF的RFC
    linux内核调优参考
    nginx_tcp_proxy代理酸酸乳
    Gitlab+Jenkins实现自动部署
    inotifywait命令详解及安装
    yum无法安装nginx,报错内容为1:nginx-1.14.2-1.el7_4.ngx.x86_64: [Errno 5] [Errno 2] 没有那个文件或目录
  • 原文地址:https://www.cnblogs.com/jxjl/p/6942074.html
Copyright © 2020-2023  润新知