指针详解1:指针的基础 (详细简略版) 取地址符(&)解引用符号(*)等等

1.内存和地址

1.1.内存

  1. 指针就是用来访问内存的
  2. 怎么访问内存呢? 1byte(字节) = 8bit,
  3. 把内存切成一个个的内存单元,一个单元是一个字节(带图理解)
  4. 每个内存单元就是一个地址,内存单元编号 ==地址 ==指针,指针就是地址

1.2.究竟该如何理解编址

  1. 控制总线:就是你要读取,还是写入(R/W),意思就是传输指令,要做什么事
  2. 地址总线:传输的是地址。去哪里读数据,或者去哪里写数据
  3. 数据总线:进行数据传递

  4. 访问的顺序:是控制总线先发信号,然后地址总线去拿数据或者写数据,接着就要进行数据传输。这样一套组合拳就完成啦!!!

  5. 硬件编址:32位机器有32根地址线总线,表示0或1(电脉冲有无)。假如是64位就有64根地址线

  6. 因为一根地址线有0或1两种意思所以有2^32个地址,就有2^32含义指针变量和地址指针变量和地址

2.指针变量和地址

2.1.取地址操作符(&)

  1. 取的是首地址,第一个字节地址
  2. 指针变量 -- 存放指针的变量 ,只要把东西放到指针变量里面他就是地址 p 的类型是int*
  3. p指针变量里面放着a的地址,a变量里面装着10这个元素
  4. 可以看上方调试图,可以肯定的是p里面就是存储着a的地址,指向的类型是int,所以在内存中0x3EFE54 --->0x3EFE58刚好是4个字节

2.2.指针变量和解引⽤操作符(*)

  1. 通过 *(解引用)p里面的地址,找到里面指向的对象
  2. 确切的说是拿到这块空间,这块空间里装着元素
  3. 拿到了这块地址,可以间接访问里面的元素对他进行修改
  4. a = 0是自己亲自动手 ,p 就是打手,通过p来帮老板干活(间接访问)

2.3.指针变量的大小

  1. 32位(x86)有32根地址线,0或1。要存储这样的地址:32bit位的空间 ==4个字节
  2. 64位(x64)有64根地址线,需要64bit位空间 ==8个字节
  3. 指针变量的大小和类型无关,相同平台下,大小都是相同的

3. 指针变量类型的意义

3.1指针的解引用

  1. 对下列代码进行解释,0x11223344是16进制的数存储到int类型里面,地址也是以16进制存储。
  2. 为了方便观察,调试的时候是以16进制呈现出来给你看的
  3. 指针类型决定了,指针解引用的操作符可以访问几个字节,决定了指针的权限
  4. 下面栗子解释,因为pi指针的类型是char*,访问的权限一个字节,所以对他进行修改时只有一个字节

3.2指针 + -整数

  1. 指针的类型决定了指针向前或者向后走一步有多大(距离)

  2. 一次能操作几个字节。

3.3void*指针

  1. 无具体类型的指针(或者叫泛型指针),这里不就具体讲了,后面再介绍
  2. 可以用来接收任何类型的指针(作用)
  3. 一般 void* 类型的指针是使用在函数参数的部分

  4. 弊端

    1. 不能进行指针+-操作

    2. 不能用 * 解引用操作符

4. const修饰指针

4.1const修饰变量

  1. a 是不是常量呢? 虽然a是不能被修改,但是本质上还是变量,下面栗子就很好的说明了 

4.2const修饰指针变量

  1. 虽然 int * const pa,const在 * 号的右边,这样就不能修改指针本身。但是是可以修改指向地址内的内容
  2. 虽然const int *pb 或者 int const *pb,前面两个一样。因为都是在 * 号的左边,不能可以修改指针本身,可以修改指针指向的内容

5.指针运算,有三种

5.1指针+-整数 

  1. *(p + i) == arr[ i ],先加,再解引用。p拿到的是数组的首元素地址
  2. 可以通过循环指针每次 + i,就能访问到对应空间拿到元素

5.2指针-指针

  1. 直接上栗子
  2. 指针-指针的绝对值,是两个元素之间的个数
  3. 计算的前提条件一定是指向同一块空间

  1. 首先要知道strlen函数,是从第一个元素开始向后访问直到‘ \0 ’之前的字符长度
  2. 当通过函数调用时,先把a首元数存储起来,while里的表达式刚好满足strlen函数的特性,
  3. a传过来是个地址
  4. *a访问到这块空间只要不等于‘\0’,a++通过不断自增,最终会指向 \0的地址
  5. 返回值时,用临时指针变量start存储下来的首地址。a的地址 - start的地址,a  - start就是这个字符串的长度了,画个图给你理解一下吧

5.3指针的关系运算

  1. 指针和指针比较大小,地址和地址比较大小
  2. zarr + sz 刚好是10后面一个地址 ,因为是小于号嘛

6. 野指针

6.1野指针成因

  1. 局部变量指针未初始化,默认为随机值
  2. 指针指向的空间释放
  3. 当指针指向的范围超出数组arr的范围时,p就是野指针
  4. 这个可以去vs里自己试试哈
  5. int main()
     {        
        int *p;//第一种局部变量指针未初始化,默认为随机值
        *p = 20;
    
        //第二种,数组越界访问
        int arr[10] = {0};
        int *p = &arr[0];
        int i = 0;
        for(i=0; i<=11; i++)
        {		
            //当指针指向的范围超出数组arr的范围时,p就是野指针
            *(p++) = i; 
        }
        return 0;
     }
    int* test()
    {
        int n = 100;
        return &n;//当返回地址时,地址已经销毁还给内存了,拿到的是个野指针
    }
    //第三种情况
    int main()
    {
     int*p = test();//接收到的就是野指针了
     printf("%d\n", *p);
     return 0;
    }

6.2如何规避野指针

  1. 6.1野指针成因
    1. 知道指针应该指向哪里,就给一个明确的地址
    2. 不知道指向哪里就初始化为NULL(空指针)
    3. NULL 是C语言中定义的⼀个标识符常量,值是0,0也是地址,这个地址是无法使用的,读写该地址 会报错
    4. 不要返回局部变量地址。需要检测地址的有效型,下面assert断言

7.assert断言

assert.h 头文件定义了宏 assert() ,用于在运行时确保程序符合指定条件,如果不符合,就报错终⽌运行。这个宏常常被称为“断言”

1.基本用法,就拿下面栗子说,当输入大于10会报错有弹窗,控制台还指明了第几行,例子中是130行

7.1assert的好处

  1. assert 出现错误的时候,直接报错,指明什么文件,哪一行
  2. 想vs这种集成环境中,release版本中,会被直接优化掉。
  3. 想关掉assert断言,在#include <assert.h>的前面用#define NDEBUG,当然也可以注释掉

7.2assert的缺点:引入了额外的检查,增加了程序的运行时间

7.3额外补充:

  1. strlen函数的返回值是size_t(无符号整型);
  2. 想一想处理这一块功能需不要assert断言,适当的使用,提高代码韧性,变得强壮些

8. 指针的使用和传址调用

  1. 当实参传递给形参的时候,形参是实参的一份临时拷贝
  2. 对形参的修改不会影响实参的
  3. 下方栗子
  4. 当你用传值调用(swap2)的时候是完成不了的交换两数的
  1. 传址调用应用场景:传址调用可以与主调函数建立联系,可以在函数内部修改主调函数中变量的值
  2. 传值调用:如果只需要主调函数中的变量值,就可以用

总结:学习贵在坚持,你每次不留余力的全力以赴下去,一定要坚信自己可以的,学习路上有大家你起陪你努力,只有难的知识才会有更多的收获,才能与他人拉开差距

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值