C语言中的 int** 是什么?

柯里棱镜
• 阅读 10804

int*int 说起

int**是什么” 这个问题其实不难。
我们可以递归分析,先看下 int*是什么,嗯?好像还可以继续递归到 int
我们都知道,int是 C 的基础数据类型 整型,而多了个 *int*指向整型变量的指针,那么 int** 是什么就不言自明了,列个表:

C语法释义
int整型
int*指向整型的指针
int**指向指向整型的指针的指针

看到这里,你对 int** 应该有了个初步的认识,但你可能觉得有点绕,没关系,下面我们写一段代码看看:

#include <stdio.h>

int main()
{
    int i = 418;

    int* pi;
    // 根据上面的表格,我们知道 int* 是指向“整型”的指针,
    // 那么 pi 可以保存的是 int 类型的变量 i 的地址:
    pi = &i;

    int** ppi;
    // ppi 可以保存的是 int* 类型的变量 pi 的地址:
    ppi = &pi;

    // 恭喜你,现在你已经知道了怎么定义 int** 类型的变量和给它赋值
    // 我们先写到这里
    return 0;
}

深入思考

假如定义有 int** p(为了方便,我们暂且把 p 认为是 ppi 的别名),那么 p, *p, **p, p + 1, *p + 1, *(p + 1), **p + 1, *(*p + 1), **(p + 1) 分别是什么?

先看指针自身

乍一看有点多,开始有点慌是吧,没关系,我们先看不带加法运算的前三个:p, *p 以及 **p
从上面的代码我们已经知道 p 就是存放 int* 类型变量的地址的变量

    // 从上面暂停下来的地方我们继续

    // 我们都知道,在指针前面加个 * 就是“取得这个指针指向的地址里的值”
    // 因为 pi 存放的是 i 的地址,那么 *pi 就是取得 i 存放的值,类型是 int
    // 同理,*ppi 取得的是 pi 存放的值,类型是 int*
    printf("*pi = %d, *ppi = %p\n", *pi, *ppi);
    // 输出 *pi = 418, *ppi = 0000002D6FF2FD58 (*pi = 后面的值在每台机器上都可能不一样)
    
    // 既然 *ppi 是 int*,那也就是说我们还可以对它再做一次解引用,
    // 拿到 *ppi 这个地址里存放的值,类型是 int
    printf("**ppi = %d\n", **ppi);
    // 输出 **pi = 418

这时,你已经掌握 p*p 以及 **p 分别是什么了

再看指针的加法运算

接下来我们还是先挑最简单的,把不带 * 的拿出来:p + 1,指针 p 做了个加法运算。

那么它加的这个 1 是什么?数字11位?1字节?

都不是,C指针加法运算里的数字操作数的单位是指针的长度,也就是说 p + 1 表示的时候内存中,紧挨着 p 的下一个可用空间的地址:

    printf("ppi = %p, ppi + 1 = %p\n", ppi, ppi + 1);
    // 输出 ppi = 0000008CA96FFB78, ppi + 1 = 0000008CA96FFB80
    // 并且我们可以看到 0000008CA96FFB80 - 0000008CA96FFB78 = 8(16进制)
    // 恰好等于 x64 系统下 1 个指针的大小:8 字节

*(p + 1) 是什么你应该也知道了,就是 p + 1 这个地址(假设是合法的)存放的值,类型是 int*
*p + 1 就是 *p 这个地址再偏移了 1 个指针长度

    printf("*ppi = %p, *ppi + 1 = %p\n", *ppi, *ppi + 1);
    // 输出 *ppi = 0000002D6FF2FD58, *ppi + 1 = 0000002D6FF2FD60

    printf("pi = %p\n", pi);
    // 输出 pi = 0000002D6FF2FD58
    // 可以看到 *ppi == p

好了,还剩下最后三个:**p + 1, *(*p + 1), **(p + 1),先试试结合上面的知识,想一下在我们的例子中这三个分别是什么,想好之后再看下面的答案检验一下自己理解得对不对:

  • **p + 1**p 取得的是 int,值是 418,所以 **p + 1 是 419
  • *(*p + 1)*p + 10000002D6FF2FD60,那么 *(*p + 1) 就是取得这个地址中的值(假设地址都是合法的)
  • **(p + 1):先对 p + 1 这个地址做解引用,得到新的地址 *(p + 1),然后再对新的地址做解引用,得到的是个 int(假设地址都是合法的)

再给你 int*** 你也能回答了

现在再给你 int***,相信你也知道它是什么了:

C语法释义
int***指向指向指向整型的指针的指针的指针
...指向 ... 的指针

拓展阅读

细心的你应该发现了,前面多次对指针做加法运算的时候都有注明假设地址合法,那么这个假设合法的地址到底是什么呢,可以阅读这篇《C指针与数组》(撰写中,敬请期待)

书籍推荐


C语言中的 int** 是什么?

本文首发于本人博客:https://yian.me/blog/what-is/pointer-to-pointer-in-c-programing-language.html

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
虾米大王 虾米大王
2年前
java代码092
code092.jsp通过FindServlet类查询分页数据所有图书信息ID图书名称价格数量作者<%Listlist1(List)request.getAttribute("list");for(code089book:list1)%
虾米大王 虾米大王
2年前
java代码099
code099.jspInserttitlehere$pageScope.user.name
虾米大王 虾米大王
2年前
java代码073
code073.javapackagepack02;importjava.io.IOException;importjava.io.PrintWriter;importjava.util.UUID;importjavax.servlet.ServletException;importjavax.servlet.annotation.Multip
Wesley13 Wesley13
3年前
VSCode配置FiraCode和更纱黑体字体
!(https://oscimg.oschina.net/oscnet/c7bb62d935ceb01d3b7fe176322e84ae00d.png)Fira Code下载到FiraCode字体的GitHub(https://www.oschina.net/action/GoToLink?urlhttps%
Stella981 Stella981
3年前
Linux自动检测网站心跳通知shell脚本
!/bin/bashLIST("http://xxxx.com")NAME("评价系统getwindowList接口")for((i0;i<${LIST@};i))doHTTP_CODEcurlo/dev/nullsw"%{http_code}""${LIST
Wesley13 Wesley13
3年前
oracle的start with connect by prior如何使用
oracle的startwithconnectbyprior是根据条件递归查询"树",分为四种使用情况: 第一种:startwith子节点ID'...'connectbyprior子节点ID父节点IDselectfrommdm_organizationostartwitho.org_code'
Wesley13 Wesley13
3年前
Oracle:Pivot 转多列并包含多个名称
SELECTFROM(SELECTl.DISTRIBUTOR_ID,d.SKU_CODE,d.WH_CODE,d.ORDER_PACKAGES,d.PRICE,d.YEARLY||d.MONTHLYasYM,d
Stella981 Stella981
3年前
PowerDesigner列名、注释内容互换
在用PowerDesigner时,常常在NAME或Comment中写中文在Code中写英文,Name只会显示给我们看,Code会使用在代码中,但Comment中的文字会保存到数据库TABLE的Description中,有时候我们写好了Name再写一次Comment很麻烦,以下两段代码就可以解决这个问题。在PowerDesigner中PowerDesig
柯里棱镜
柯里棱镜
Lv1
先做一个浪漫的人就从爱自已开始。
文章
9
粉丝
0
获赞
0