游戏开发工具

指针是什么?

指针只是一个名词而已,指针就是地址。

我们平时说指针,也可以指指针变量。


怎么表示?

类型名  指针变量 = 地址;

例如:

int* pa = &a;
//我们这里的指针类型叫做int*,我读做(yin te 星号)。
//pa是指针变量

这段表达式的意思是:定义了一个指针变量pa,里面存放的是a的地址。而这个指针变量的类型为int*。

那下面就有同学疑惑了,那什么是指针变量?


什么是指针变量?

很简单,在之前我们学习了怎么定义整型变量a。

比如定义一个《整型》《变量a》,然后把a初始化为10。

int a = 10;

不过现在变了,我们现在学习了指针。

可以定义一个《int*》《变量pa》,然后把pa初识化为&a。

注意:

int* 是一个类型,叫做指针类型

pa就叫做指针变量

int* pa = &a;


指针类型又是什么?

既然变量有不同的类型,比如整型,浮点型等。

那么指针也有也有不同的类型。

char  *pc = NULL;
int   *pi = NULL;
short *ps = NULL;
long  *pl = NULL;
float *pf = NULL;
double *pd = NULL; //NULL为空指针。

这里可以看到,指针的定义方式是:类型 + * 。

其实:

char* 类型的指针是为了存放 char 类型变量的地址。

short* 类型的指针是为了存放 short 类型变量的地址。

int* 类型的指针是为了存放 int 类型变量的地址。


指针类型存在的意义

那有这么多的指针的类型,指针类型的意义是什么?

我们在这里先说两个重要结论:

1、指针的类型决定了指针向前或者向后走一步(也就是地址+1)有多大(能走多少个字节)

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


比如: char* 的指针+1(也就是地址加一)只能跳过一个字节,而 int* 的指针+1(地址+1)就能跳过四个字节。

因为char类型在内存中占1个字节,int类型在内存中占4个字节。

再比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。

也不能空谈,我们看下面的例子

int main()
{
    int n = 10;//定义整型变量n,初始化为10
    char* pc = (char*)&n;//定义字符类型指针变量pc,初始化为n的地址
    int* pi = &n;//定义整型指针变量pi,初始化为n的地址
    printf("%p\n", &n);
    printf("%p\n", pc);
    printf("%p\n", pc + 1);
    printf("%p\n", pi);
    printf("%p\n", pi + 1);
    return 0;
}

打印结果如下,为16进制的数字。可以看出来字符类型的指针变量pc加1,只能操作一个字节,所以从4+1变到5。而整型变量pi+1跳过了4个字节,变为了8。

1.png

综上所述,这就是指针类型的意义。


野指针是什么?

听到野指针可能有人会疑惑这个名词什么意思啊?

不要着急,野指针很好理解。

对一个狗形容为野的话,可以理解为这个野狗经常占别人的地盘,或者随机出现在任何地方,所以野指针也是这个意思呀。

野指针就是总是占别人内存(地址)的指针、或者随机出现一个地址。


概念:野指针就是指向的内存地址是未知的(随机的,不正确的,没有明确限制的)。

说明:指针变量也是变量,是变量就可以任意赋值。但是,任意数值赋值给指针变量没有意义,因为这样的指针就成了野指针,此指针指向的区域是未知(操作系统不允许操作此指针指向的内存区域)。

注意:野指针不会直接引发错误,操作野指针指向的内存区域才会出问题。

代码示例:

int a = 100;
int *p;
p = a; //把a的值赋值给指针变量p,p为野指针, ok,不会有问题,但没有意义
p = 0x12345678; //给指针变量p赋值,p为野指针, ok,不会有问题,但没有意义
*p = 1000;  //对野指针进行赋值操作就不可以了

把a的值赋值给指针变量p,p为野指针, ok,不会有问题,但没有意义。给指针变量p赋值,p为野指针, ok,不会有问题,但没有意义。


野指针的成因

1. 指针未初始化:指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它所指的空间是随机的。

代码示例:

int main()
{
    int * p;
    *p = 20;
    return 0;
}

(个人理解:指针变量有操作系统随机赋值,未指向一个具体空间,没有落脚点)

2. 指针越界访问:指针指向的范围超出了合理范围,或者调用函数时返回指向栈内存的指针或引用,因为栈内存在函数结束时会被释放。

代码示例:

int main()
{
    int arr[10] = {0};
    int *p = arr;
    for(int i = 0; i <= 11; i++)
    {
        *(P++) = i;//当指针指向的范围超出数组arr的范围,p变成野指针。
    }
    return 0;
}

3 .指针释放后未置空:有时指针在free或delete后未赋值 NULL,便会使人以为是合法的。其实它们只是把指针所指的内存给释放掉,但并没有把指针本身忘记。此时指针指向的就是无效内存。释放后的指针应立即将指针置为NULL,防止产生“野指针”。

代码示例:

int main()
{
    int *p = NULL;
    p = malloc(10 * sizeof(int));
    if (!p)
    {
        return;
    }
    //成功开辟内存,可以操作内存。
    free(p);
    p = NULL;
    return 0;
}

(个人理解:我们前一天住了个宾馆,第二天退房了,虽然我们知道一个该房间的门牌号,但是保洁阿姨已经收拾了房间,我们就不知道房间里具体是什么样的了,所以我们也没法操作了。)


规避野指针

1. 初始化指针

代码示例:

int main()
{
    int *p = NULL;
    int a = 10;
    p = &a;
    *p = 20;
    return 0;
}

2. 避免指针越界

代码示例:

int main()
{
    int arr[10] = {0};
    int *p = arr;
    for(int i = 0; i < 10; i++)
    {
        *(P++) = i;//严格遵守有效范围。
    }
    return 0;
}

3 避免返回局部变量的地址

代码示例:

int * test()
{
    int a = 20;
    return &a;
}
int main()
{
    int *p = NULL;
    p = test();
    printf("%d\n", *p);
    return 0;
}

这与变量的作用域有关,局部变量存在栈区,当被调函数结束后 ,栈区上局部变量的内存空间被释放,若再去访问该空间就不合理了。

4. 开辟的指针释放后置为NULL

当指针p指向的内存空间释放时,没有设置指针p的值为NULL。free只是把内存空间释放了,但是并没有将指针p的值赋为NULL。

代码示例:

int main()
{
    int *p = NULL;
    p = malloc(10 * sizeof(int));
    if (!p)
    {
        return;
    }
    //成功开辟内存,可以操作内存。
    free(p);
    p = NULL;//避免野指针
    return 0;
}

5. 养成良好的编程习惯

好的编程习惯可以避免很多问题,道阻且长,但行则将至!!!