指针第四部分(野指针、空指针、万能指针)---- 2021.2.5

目录

    • 上一讲指针链接:
    • 野指针
    • 空指针
    • 万能指针
    • 结束语

上一讲指针链接:

指针第三部分(“ * ”与“ & ”、指针的步长)---- 2021.1.31

野指针

先请大家看一下下面所给出的一段代码。想想这段代码有没有语法问题,最终能不能成功运行?

int main()
{
     
    int* p;
    *p = 200;
    printf("%d\n", *p);
    system("pause");
    return 0;
}

先对上述代码做一个简单的解析:

首先我们定义了一个指针变量p,然后对该指针变量p所指向的内容赋值200。最终我们对p指向的那一块内存空间的内容打印出来。

但是我们尝试运行下,最终显示有错误。这是为什么呢?运行结果如下:
在这里插入图片描述原因:

  1. *p:代表取指针变量p指向的那一块内存空间的内容。但是如下图所示,程序中并没有明确指向具体的那哪一块的内存空间,所以就会随机将200赋值给随机的一块的内存空间,所以最终会导致系统崩溃。
    指针第四部分(野指针、空指针、万能指针)---- 2021.2.5_第1张图片

  2. 说的通俗点,此时这个指针变量p相当于是一个疯狗,逮谁咬谁,看见哪个内存空间,就会指向那块内存空间,并且将200赋值给那块内存空间。

所以野指针就是没有初始化的指针,指针的指向是随机的,不可以操作野指针
正常操作,应该是定义一个变量,然后将该该变量的地址赋值给指针变量p即可。如下所示。

int a = 0;
int *p;
p = &a;
*p = 200;

通过上述分析,我们应该还知道一点,指针p保存的地址一定是被定义过的(也就是p指向的那块内存空间一定是向系统申请过的)

空指针

同样,先请大家看一下下面所给出的一段代码。想想这段代码有没有语法问题,最终能不能成功运行?

int main()
{
     
    int  a;
    int* p = 0x00000000;
    
    *p = 200;
    printf("%d\n", *p);
    system("pause");
    return 0;
}

先对上述代码做一个简单的解析:

我们定义了一个整型变量a,定义了一个指针变量p,并且为了避免出现刚刚我们提到的野指针的问题,所以我们人为定义了一个地址,为0x0000 0000。然后我们对指针变量p指向的内存空间的内容赋值200。最终,我们通过*p将那块内存空间的内容打印出来。

但是我们尝试运行下,最终显示有错误。这是为什么呢?运行结果如下:
指针第四部分(野指针、空指针、万能指针)---- 2021.2.5_第2张图片
原因:

  1. 如上面代码所示,我们为了避免出现野指针,将0x0000 0000赋值给指针变量p。此时该指针就会变成空指针。其实空指针的另外一种表达方式如下所示:
 int  *p = NULL;
  1. 但是为啥程序运行后会出问题呢?我们需要知道0x0000 0000或者NULL赋值给指针变量p,也意味着指针变量p指向了该地址,也就是0x0000 0000。具体示意图如下图所示。
    指针第四部分(野指针、空指针、万能指针)---- 2021.2.5_第3张图片
  2. 虽然这个表面上看是没问题的,但是p保存了0x0000 0000的地址,这个地址是不可以使用的,因为往往这个地址是用来存放系统启动文件的,是非法的,所以也就造成了上面所示的写入访问权限冲突,自然而然程序也就不会成功运行了。

那么空指针既然会造成这种问题,空指针到底是在什么应用场合使用的呢?

空指针的作用: 如果使用完空指针,一般我们会将该指针赋值为NULL。在下次使用时,判断指针是否为NULL,就知道指针有没有被使用。如果该指针值为NULL,则表示我们可以使用该指针,进而对该指针进行赋值。如果该指针值不为NULL,则表示我们不可以使用该指针,意味着该指针在其他的某个地方已经被赋值。

万能指针

我引出一个问题;
前面文章提到过,指针变量有很多种数据类型,比如int*char*double*等等,那么有没有一种指针变量类型可以接受所有的数据类型?

这个指针变量我们把它叫做万能指针。

为了让大家更好的理解,特意画了一张图供大家观看,如下图所示。

在这里插入图片描述
对上图解释如下:

我们一般定义一个函数时,往往会留出对应的函数接口,但是为了达到兼容性,我们其实并不知道传入该函数的参数是不是符合我们定义的函数接口的数据类型,所以我们可以使用万能指针来解决这个问题。

同样,先请大家看一下下面所给出的一段代码。想想这段代码有没有语法问题,最终能不能成功运行?

int main()
{
     
    int  a = 10;
    short b = 10;
    void* p = (void*)&a;
    void* q = (void*)&b;
    
    printf("%d\n", *p);
    system("pause");
    return 0;
}

先对上述代码做一个简单的解析:

  1. 一般我们定义万能指针是这样定义的,如下所示:
   void *p;//万能指针
  1. 我们定义了一个整型变量a,并且赋值为10。同样我们定义了一个short类型的变量b,也赋值为10。
  2. 定义两个万能指针p、q用来存放a和b的地址。但是由前面几篇文章我们得知,表达式两边需要进行数据类型的匹配,这边再说一遍吧,加深下大家的印象。以a举例。表达式左边p的数据类型为void*类型,但是表达式右边的a数据类型为int类型,但是由前面文章的值,对a进行取地址&操作,此时此刻a的数据类型就变成了int*类型,但是表达式两边的数据类型还是不匹配,所以此时我们就需要对&a整体的数据类型进行强制转换,所以表达式就变成了 void* p = (void*)&a;
  3. 最终我们将指针变量p指向的a那块内存空间的内容打印出来,按照上述逻辑,最终结果应该是10。

但是我们尝试运行下,最终显示有错误。这是为什么呢?运行结果如下:
指针第四部分(野指针、空指针、万能指针)---- 2021.2.5_第4张图片原因:

  1. 如上所示,最终程序都没能通过编译,是因为我们最终获取到的为p指向的那块内存空间的内容,但是由前几篇文章可知,所取得内容与对应的数据类型的步长有关,也可以理解为与数据类型的宽度有关。
  2. 因为我们定义的p为万能指针,也就是void*类型,那么由前面几篇文章可知,最终p数据类型的步长为sizeof(void),但是众所周知,void代表空,这意味着我们通过指针变量p索取对应指向的内存空间的内容时,系统无法识别void这个类型,所以也就会造成编译不通过的现象。
  3. 举个例子,void b; 不可以定义void类型的变量,因为编译器不知道给变量分配多大的空间但是可以定义void *类型,因为由前面几篇文章可知,指针都是4个字节。

那当我们使用到万能指针时,最终我们如何能够正常打印那块内存空间的内容,请跟我一起来分析。

 printf("%d\n", *p);

p变量的数据类型为void*类型,*p代表取对应内存空间的内容,但是结果不允许。那么我们此时此刻就需要进行强制转换了。

那问题来了,对哪个部分进行强制转换呢?

我们都想要最终打印出来的值为1010的数据类型为int类型。由于p变量为指针变量,p前面的“*”只是提取对应空间的内容,所以我们强制转换的部分为p。所以就应该将void*类型强制转换为int*类型。改完后代码如下,我们可以看看结果如何。

int main()
{
     
    int  a = 10;
    short b = 10;
    void* p = (void*)&a;
    void* q = (void*)&b;

    printf("%d\n", *(int*)p);
    system("pause");
    return 0;
}

指针第四部分(野指针、空指针、万能指针)---- 2021.2.5_第5张图片

结束语

如果觉得这篇文章还不错的话,记得点赞 ,支持下!!!

你可能感兴趣的:(C)