void指针和NULL指针&指向指针的指针&指针数组

一、void指针和NULL指针

void指针我们把它称为通用指针,就是可以指向任意类型的数据。也就是说,任何类型的指针都可以赋值给void指针。

void类型指针,不要直接给void指针进行解引用。

对一个NULL指针进行解引用是非法的,会引起段错误。
当你还不清楚将指针初始化为什么地址时,请将它初始化NULL;在对指针解引用时,先检查该指针是否为NULL。
NULL不是NUL 。NUL是ASCII字符表中的第一个字符。
NULL用于指针和对象,表示指向一个不被使用的地址,而’\0’表示字符串的结尾。
在这里插入图片描述
二、指向指针的指针

直接看代码:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int a = 520;
    int *p = &a;
    int **pp = &p;
    printf("a=%d,  *p=%d,  **pp=%d\n",a,*p,**pp);
    printf("&a:%p,  p:%p,  *pp:%p\n",&a,p,*pp);


    printf("Hello world!\n");
    return 0;
}

在这里插入图片描述
printf("&a:%p, p:%p, *pp:%p\n",&a,p,*pp);
代码中:这三个所打印的值都是一样的。
&a:取a的地址。
p:p是指针,指向了a,p中存的就是a的地址,直接以地址的形式打印p就是打印a的地址。
pp:是二重指针,指向p的指针,那么pp中存放的是p的地址,对pp解引用一次得到的就是p的地址。

三、指针数组和指向指针的指针

看程序好好体会下:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int i = 0;
    //这是一个指针数组,它是数组,里边存放了6个指向字符串的指针
    char *CLibrary[] =
    {
        "《C程序设计语言》",
        "《C和指针》",
        "《C primer plus》",
        "《C编程专家》",
        "《C陷阱与缺陷》",
        "《带你学C带你飞》"
    };

    char **lib1 ;//指向指针的指针

    char **lib2[5] ;//指向指针的指针数组。数组中存放的是指向指针的指针。

    lib1 = &CLibrary[5];//我们理解下CLibrary[5],这个变量中存放的是字符串,也就是,是字符串的首地址,也就是指针。
  					    //那么&CLibrary[5]就是指针的指针,跟lib1 的类型一致。很完美
    lib2[0] = &CLibrary[0];//好好体会这句。
    lib2[1] = &CLibrary[1];
    lib2[2] = &CLibrary[2];
    lib2[3] = &CLibrary[3];
    lib2[4] = &CLibrary[4];

    printf("小甲鱼出的C书籍:%s\n",*lib1);//这里我们要打印字符串,那么就是需要地址。其实这里是以指针的方式来访问数组。

    printf("比较NB点的C语言书籍:\n");
    for(i=0; i<5; i++)
    {
        printf("%s \n",*lib2[i]); //同样,我们需要打印字符串,需要的是地址,那么要对lib2[]解引用一次就刚好是地址。
    }
    printf("Hello world!\n");
    return 0;
}

在这里插入图片描述
我们使用指向指针的指针指向数组指针有两个优势:
(1)避免重复分配内存(不使用指针数组去存放)
(2)只需要进行一处修改(都存放在一起,只修改一处就可以)
这样,代码的灵活性和安全性都有了显著的提高。

四、数组指针和二维数组

首先,我们来看下边的程序, 用指针的方式访问以为数组是没有问题的。

#include <stdio.h>
#include <stdlib.h>

int main()
{
  int i;
  int array[] ={ 0,1,2,3,4,5,6,7,8,9 };
  int *p = array;
  for(i=0; i<10; i++)
  {
      printf("%3d",*(p+i));
      printf("\n");
  }

    printf("Hello world!\n");
    return 0;
}

在这里插入图片描述
————————————————————————————————————————

再来看看下边的程序,如果我们要访问二维数组呢?是不是可以直接用双重指针就可以了呢?

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int i,j;
  int array[3][4] =
  {
    {0,1,2,3},
    {4,5,6,7},
    {8,9,10,11},

  };
  int **p= array;
  for(i=0; i<3; i++)
  {
      for(j=0; j<4; j++)
      {
          printf("%3d",*(*(p+i)+j));//这里的跨度是不一样的。
      }
      printf("\n");
  }

    printf("Hello world!\n");
    return 0;
}

上边的程序有没有什么问题呢?先思考一下。
首先是不能成功执行的。原因如下:
用程序来演示:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int i,j;
  int array[3][4] =
  {
    {0,1,2,3},
    {4,5,6,7},
    {8,9,10,11},

  };
  int **p = array;
  /*
  for(i=0; i<3; i++)
  {
      for(j=0; j<4; j++)
      {
          printf("%3d",*(*(p+i)+j));
      }
      printf("\n");
  }
 */
 printf("array的地址:%p ,  p的地址:%p\n",array,p);
 printf("array+1的地址:%p  p+1的地址:%p\n",array+1,p+1);
    printf("Hello world!\n");
    return 0;
}

在这里插入图片描述
我们能看到:
array+1举例array的跨度是:E0-F0 转换成十进制就是16个字节。
p+1距离p的跨度是:E0-E8转换成十进制是8个字节(这个是指针的长度,与硬件系统平台有关系,我的是win10 64位系统codeblock20.1平台,测试指针是8个字节)。

所以:我们这样访问是不对的。会发生段错误,导致程序奔溃。
————————————————————————————————————————
我们用数值指针来进行对二维数组的访问。

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int i,j;
  int array[3][4] =
  {
    {0,1,2,3},//第一行
    {4,5,6,7},//第二行
    {8,9,10,11},//第三行

  };
  int (*p)[4] = array;
  //这里p是一个指向了4个元素的一维数组,所以这个p的跨度刚好是4*4=16个字节。所以p+1刚好就指向了第二行。array是指向二维数组的第一个元素的地址,array+1就是加到第二行了。
  for(i=0; i<3; i++)
  {
      for(j=0; j<4; j++)
      {
          printf("%3d",*(*(p+i)+j));
      }
      printf("\n");
  }

    printf("Hello world!\n");
    return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值