指针(1)

似梦清欢
• 阅读 669
指针

在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针"。意思是通过它能找到以它为地址的内存单元。 指针(1) 如上图中,内存中每一个内存块都有唯一一个编号(地址)做标识。即通过内存块的地址可以找到内存单元。 ::: tip 指针是一个变量,存放内存单元的地址。(可以认为指针就是地址) 存放在指针中的值都被当成变量处理。 ::: 计算各种指针类型的大小:

int main()
{
    printf("%d\n", sizeof(char*));  //8
    printf("%d\n", sizeof(short*));  //8
    printf("%d\n", sizeof(int*));  //8
    printf("%d\n", sizeof(double*));  //8
    return 0;
}

指针类型算出变量大小为8。


内存的编址

::: tip 一个字节对应一个地址。 1个字节(byte)=8比特(bit)。比特即二进制的0和1。 ::: 对于32位设备,假设有32根地址线,每根地址线在寻址时产生一个电信号正电/负电(数字信号1或0)。32根地址线产生的地址序列就是32个全0到32个全1,即产生2的32次方个地址序列,用来作为内存编号。32个比特位即4个字节,就是32位平台存储一个地址需要的空间。如果是64位平台,产生的64根地址线产生的二进制序列,需要64个比特位的空间,即8个字节。


指针类型的实际意义

1个十六进制位是4个二进制位,2个十六进制位是8个二进制位,8个二进制位是一个字节,即2个十六进制位占一个字节。 ::: tip 1、1个字节 = 8个二进制位 1Byte = 8bit 2、1个十六进制 = 4个二进制位 3、1个字节 = 2个十六进制 :::

int main()
{
    int a = 0x11223344;  //每2个十六进制数占一个字节,共4字节
    int* pa = &a;
    char* pb = &a;
    printf("%p\n", pa);
    printf("%p\n", pb);
    return 0;
}
0000000AFE8FF5B4
0000000AFE8FF5B4

上述代码中,pa和pb都是指针类型,8个字节大小,可以存放a的地址。第五行代码将取出的整型的地址放进char*类型的指针变量pb中,编译器会报警告:从“int *”到“char *”的类型不兼容。但编译器仍能将地址存进pb中。

指针类型的意义1
int main()
{
    int a = 0x11223344;
    int* pa = &a;
    *pa = 0;
    return 0;
}

指针(1)

int main()
{
    int a = 0x11223344;
    char* pb = &a;
    *pb = 0;
    return 0;
}

指针(1) 如上:在存储数据时什么类型的的指针都可以存储。在对指针进行解引用操作时,整型指针可以对4个字节进行操作,字符型指针只能访问到一个字节。 ::: tip 可以理解为: int* p指向int类型,在解引用时就可以访问int类型大小的字节。 double* p指向double类型,在解引用时就可以访问double类型大小的字节。 ::: ::: warning 指针的类型决定了指针进行解引用操作时能够访问的空间大小。 使用指针时应根据解引用时需要操作的范围选择使用合适的类型。 :::


指针类型的意义2

指针+-整数时,指针的类型决定了+-整数时候跳几个字节。int类型时+1需要跳过一个int即4个字节;char类型时-1需要减去一个char即1个字节。

int main()
{
    int a = 0x11223344;
    int* pa = &a;
    char* pb = &a;
    printf("%p\n", pa);    //000000FCF08FFC84 - pa
    printf("%p\n", pa+1);  //000000FCF08FFC88 - pa+1
    printf("%p\n", pb);    //000000FCF08FFC84 - pb
    printf("%p\n", pb-1);  //000000FCF08FFC83 - pb-1
    return 0;
}

::: tip 指针类型决定指针的步长(字节): int* p:p + 1 --> 4 char* p:p + 1 --> 1 double* p:p + 1 --> 8 :::

int main()
{
    int arr[10] = { 0 };
    int* p = arr;  //首元素的地址
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        *(p + i) = 1;  //内存中每次向右移动一个int的长度并更改值为1
    }
    return 0;
}

如下图,循环中每次向右移动一个int的长度并将该位置更改为1,数组中十个元素全部改为1,循环结束: 指针(1)

int main()
{
    int arr[10] = { 0 };  //10个元素共40字节
    char* p = arr;  //*p拿到数组首元素地址
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        *(p + i) = 1;  //char类型的指针每次向后更改一个字节的值
    }
    return 0;
}

如下图,循环中每次向右移动一个char的长度并将该位置更改为1,循环结束时只能更改10个字节的内容,对于int数组来说,字节的改动只涉及到3(2.5)个数组元素。 指针(1)


野指针

野指针是指针指向的位置不可知(随机的、不正确的、没有明确限制的) 野指针的成因有:指针未初始化、指针越界访问、指针指向的空间释放等。

  • 指针未初始化:如同局部变量int a不初始化默认是随机值,局部指针变量不初始化,指针变量内的地址就是随机值,可能随机指向内存中的某一空间,对指针变量解引用赋值时,操作是非法的,该指针变量就是野指针。
  • 指针越界访问:如下代码中i++到大于10,指针p指向的内存空间超出可以管理的arr数组的范围后,访问非法内存空间,变为野指针。
int main()
{
    int arr[10] = { 0 };
    char* p = arr;
    int i = 0;
    for (i = 0; i < 12; i++)
        p++;
    return 0;
}
  • 指针指向的空间释放:如下代码中在自定义函数中创建变量a并将a地址返回到主函数的指针p中,主函数中的指针p成功接收到a的地址时,自定义函数test中的局部变量销毁,即指针p指向的空间被释放,p变为野指针。
    int* test()
    {
      int a = 10;
      return &a;
    }
    int main()
    {
      int*p = test();
      *p = 20;
      return 0;
    }
    规避野指针
    ::: warning 规避野指针的四个方法:
  • 1.指针初始化*
  • 2.小心指针越界*
  • 3.指针指向空间释放后将指针置NULL*
  • 4.指针使用之前检查有效性* ::: 指针初始化: 创建一个指针后,当没有想好该指针指向的内存空间时,将指针赋值空指针。
    int* p = NULL;  //指向空指针,即指向0,如下定义
    #define NULL ((void *)0)  //把0强制转换为void*类型
    上述代码中的NULL是用来初始化指针的,或者用来给指针赋值。

小心指针越界: 指针的操作范围控制在允许操作范围内。 当程序出现崩溃,可能出现指针越界,需要检查指针。


指针指向空间释放后将指针置NULL: 指针指向空间被释放后,或者不想让指针指向任何空间时,将指针置NULL,该指针就不再指向任何实际空间了。


指针使用之前检查有效性: 当指针不需要使用置为NULL后,指针没有指向任何有效空间,不能使用该指针。当指针中放的是有意义的地址,才可以使用。即使用指针之前需要判断pa是否等于NULL,如果pa == NULL,该指针就不能使用,程序会崩溃。如下图:(nullptr为空指针) 指针(1)

点赞
收藏
评论区
推荐文章

暂无数据

似梦清欢
似梦清欢
Lv1
学海无涯
文章
17
粉丝
17
获赞
1