诡计多端的指针

目录

1、指针是什么?

2、指针和指针类型

2.1、指针+-整数

2.2、指针的解引用

3、野指针

3.1、成因

3.2、如何避免

4、指针运算

4.1、指针+-整数

4.2、指针-指针

4.3、指针的关系运算

5、指针和数组

6、二级指针

7、指针数组

8、const修饰指针


1、指针是什么?

指针是什么?

  1. 指针是内存中一个最小单元的编号,也就是地址
  2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量

总结:指针就是地址,口语中说的指针通常指的是指针变量。

在了解指针之前我们先要知道内存是怎样分布的,那么问题就来了,什么是内存?

在内存中是以一个字节为单位来存储的,每个单位都有属于自己的编号,而这些编号也就是我们所说的地址,也就是指针

 指针变量

我们可以通过&(取地址操作符)取出变量的内存起始地址,取出的地址可以存放到一个变量中,这个变量就是指针变量

  • 指针变量是用来存放地址的,地址是唯一标示一个内存单元的。
  • 指针的大小在32位平台是4个字节,在64位平台是8个字节。

2、指针和指针类型

指针的定义方式是: type + *,比如int*、short*、char*……等等,不同类型的变量用不同类型的指针来存放

2.1、指针+-整数

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

2.2、指针的解引用

指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节),对应相同类型的大小。

3、野指针

概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

3.1、成因

  1. 指针未初始化
    int main()
    {
        int* p;//创建指针变量p
        //未初始化,此时指针内的内容是随机值
        //也就是随机的地址,导致随机访问
        printf("%p\n",p);//在此使用
        return 0;
    }
  2. 指针越界访问
    int main()
    {
        int arr[5] = { 0 };
        int i = 0;
        for (i = 0; i <= 5; i++)
        {//这里的数组的下标是从0~4,当i=5时造成越界访问
            //同时如果i<0的时候也会导致越界访问
            arr[i] = 0;
        }
        return 0;
    }
  3. 指针指向的空间释放
    int* test()
    {
        int num = 200;
        return &num;
        //num是一个局部变量,出了test函数
        //会销毁
    }
    int main()
    {
        int* p = test();
        printf("%p\n",p);
        return 0;
    }

3.2、如何避免

  1. 指针初始化
  2. 小心指针越界
  3. 指针指向空间释放,及时置NULL
  4. 避免返回局部变量的地址
  5. 指针使用之前检查有效性

4、指针运算

4.1、指针+-整数

指针可以通过加(向后)减(向前)整数实现指针的偏移,来访问不同的空间,结合解引用操作符来进行对于空间内数据的操作。

4.2、指针-指针

指针减去指针得到的数据的绝对值是一块连续的空间元素的个数(一般用于数组)

用于数组的条件

  1. 数组在空间中连续存放
  2. 指针指向的是数组元素的地址
  3. 解引用操作符可以对数组元素进行操作

4.3、指针的关系运算

指针的关系运算实际上是通过指针的大小,因为指针就是地址,而地址又是一段十六进制的数据,通过地址之间的比较可以来确认一些逻辑:

int main()
{
    int arr[4];
    int* p = NULL;
    
    for (p=arr; p<&arr[4];)
    {
        *p++ = 0;
    }
    return 0;
}

 上面的代码也可以改为:

int main()
{
    int arr[4];
    int* p = NULL;

    for (p = &arr[4]; p >= &arr[0]; p--)
    {
        *p = 0;
    }
    return 0;
}

不过这代码出现了报错,这是一个非标准用法,尽量避免。

实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证它可行。

标准规定:

允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。

5、指针和数组

指针:指针是地址

数组名表示的是数组首元素的地址(2种情况除外)

数组:是一类数据存放的总和

6、二级指针

指针变量也是变量,是变量就有地址,所以存放指针变量的地址的变量,叫做二级指针。

int main()
{
    int a = 10;
    int* p = &a;
    //存放变量a的地址,叫做一级指针
    int** pp = &p;
    //存放一级指针变量的地址,叫做二级指针
    return 0;
}

类似于套娃,通过地址不断的往前找,理论上有三级指针、四级指针……不过应用的场景不太好找。

对于二级指针的运算有:

*pp 通过对二级指针pp中的地址进行解引用,这样找到的是 p ,也就是一级指针,*pp 其实访问的就是 p

int b = 20;
*pp = &b;//等价于p = &b

**pp 先通过 *pp 找到 p ,然后对 p 进行解引用操作: *p ,那找到的是 a

**pp = 30;//等价于a=30

7、指针数组

指针数组是存放指针的一个数组,里面存放的是一系列的指针

int* arr1[5];//整形指针数组
char* arr2[5];//字符型指针数组
……

用法示例:

//一维数组模拟二维数组
int main()
{
    int* arr1[3] = { 1,1,1};
    int* arr2[3] = { 3,3,3};
    int* arr3[3] = { 5,5,5};
    int* arr4[3] = { arr1,arr2,arr3 };
    //创建整形指针数组,并存放值
    int i = 0;
    for (i = 0; i < 3; i++)
    {
        int j = 0;
        for (j = 0; j < 3; j++)
        {
            printf("%d ", *((*arr4+i)+j));
        }
    }
     
    //int i = 0;
    //for (i = 0; i < 3; i++)
    //{
    //    int j = 0;
    //    for (j = 0; j < 3; j++)
    //    {
    //        printf("%d ",arr4[i][j]);//arr4[i][j]相当于是*((*arr4+i)+j)
    //    }
    //}

    return 0;
}

8、const修饰指针

const修饰指针有两种:

  1. 指针的指向不能变
    int* const p

  2. 指针指向的地址里面的内容不可变
    const int* p
    //或者int const* p

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我可是万西西呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值