二级指针即“指向指针的指针”;

下面的实例代码创建了一个二级指针c

  1. int a = 5;
  2. int* b = &a;
  3. int** c = &b;

你不能这样

  1. int a = 5;
  2. int** c = &a;

这样你会得到一个类型不兼容的警告

  1. c5.c:16:18: warning: initialization from incompatible pointer type [enabled by default]
  2. const int** c = &a;
  3. ^

你也可以像这样创建一个二级指针变量,并且分配一些空间给它

  1. char **Pstr_1 = malloc( sizeof( char* ) * 3 );

这里我分配了3个指针长度给Pstr_1,Pstr_1目前指向一个可以存放3个char*类型变量的空间,也就是说它指向的空间可以存放3个指向char的指针

  1. for (; i < 3; ++i)
  2. {
  3. *( Pstr_1 + i ) = malloc( sizeof( char ) * 3 );
  4. }

你可以这样把每个指针都分配特定的内存区域,现在这3个指针都指向了一个可以存放3个char的内存空间了哦

但是你不能这样做,因为Pstr_1分配的这3个空间只能存放char指针,这是类型问题

  1. for (; i < ARR_LEN; ++i)
  2. {
  3. *( Pstr_1 + i ) = i;
  4. }

关于const修饰符

首先我们来看看指向常量的二级指针

  1. int a = 5;
  2. const int* b = &a;
  3. const int** c = &a;
  4. **c = 6;

上面代码会出现警告,说我们试图更改了一个只读的值

  1. c5.c:17:2: error: assignment of read-only location ¡®**c¡¯
  2. **c = 6;
  3. ^

或许你会认为因为b是指向常量的指针,所以导致了这次的警告

  1. int a = 5;
  2. int* b = &a;
  3. const int** c = &b;
  4. **c = 6;
  1. c5.c:16:18: warning: initialization from incompatible pointer type [enabled by default]
  2. const int** c = &b;
  3. ^

:-),二级指针是不能指向不同类型的指针的哦,一级是const类型,二级也要是const类型,相反二级是const类型,也只能指向是const类型的一级指针

由于这个特性,const类型的二级指针,是修改不了最终指向的值的哦!

  1. int a = 5;
  2. int* b = &a;
  3. int** c = &b;
  4. **c = 6;
  5. printf("%d\n", a);

这样就可以完美运行了,**c指向了a,并且修改了a的值

高能时刻

  1. int a = 5;
  2. const int* b = &a;
  3. const int** c = &b;
  4. int d = 6;
  5. const int* e = &d;
  6. c = &e;
  7. printf("%d\n", **c);

它输出了6,全程没有任何警告。

是的,尽管是const的二级指针,它也是可以随时改变指针指向的。

最终回想一下“指向常量的指针”这句话,它适用于1级指针,同样适用于2级指针,你根本不用想太多,因为它最终指向的始终是常量。

同理,这个也适合分配空间的二级指针

  1. const char **Pstr_1 = malloc( sizeof( char* ) * 3 );
  2. int i = 0;
  3.  
  4. for (; i < 3; ++i)
  5. {
  6. *( Pstr_1 + i ) = malloc( sizeof( char ) * 3 );
  7. *(*( Pstr_1 + i )) = i;
  8. }

你得到了一个错误,因为Pstr_1指向的指针*( Pstr_1 + i ),里面的内存区域已经不允许修改了

  1. c5.c:45:3: error: assignment of read-only location ¡®**(Pstr_1 + (sizetype)((long unsigned int)i * 8ul))¡¯
  2. *(*( Pstr_1 + i )) = i;
  3. ^

二维数组的指针

我们知道,要在函数中处理数组,一般要给函数传递指针。那么,对于一维int数组a[10],我们可以定义一个int *类型的指针变量p指向该数组。为什么这样定义?(按照我下面的理解方式有利于理解二维数组指针的定义)

首先我们可以把这个一维数组中的10个元素当作10个数组,每个数组都只有一个元素,即指针在每一次移动,都只需指向一个int类型的变量(通过指针对数组进行操作),故定义指向一个int变量的指针。

那么对于二维数组a[3][2],我们要定义一个 int (*p) [2]的指针。首先我们来分析一下这个指针的类型。它也等价于这种形式:int [2] *p。意思是定义一个指向两个int类型变量的指针。当你学了结构体,你就对这种数据类型的定义方式不陌生了。但没有学过结构体,就有点抽象了。我先举个简单的例子:例如我定义一个数组int a[10],其实它也可以表示成另一种形式:int [10] a。意思是定义一个变量a,它是int [10]类型的,即它是一个拥有10个int变量长度的变量,即是数组。那对于二维数组a[3][2],为什么要定义一个指向两个int变量的指针呢?

按照前面对一维数组的分析,我们可以把这个二维数组看作是三个数组,每个数组有两个元素。指向该二维数组的指针在进行移动时,它指向的是一整个数组,即两个int类型,所以需要定义int [2] 类型的指针变量。

由此我们可以总结规律,指向二维数组a[i][j]的指针类型必然是 int [j] *类型。

  1. # include <stdio.h>
  2. int main(void) {
  3. int a[4][3] = {
  4. {1, 2, 12},
  5. {3, 4, 34},
  6. {5, 6, 56},
  7. {7, 8, 78}
  8. };
  9. int (*pArr) [3]; //一定要加上括号,因为[]的优先级高于*
  10. pArr = a;
  11. for (int i = 0; i < 4; i++)
  12. for (int j = 0; j < 3; j++)
  13. printf("%d ", pArr[i][j]);
  14. return 0;
  15. }

有人会说难道不能定义一个int**的指针类型吗?那你可能是被动态数组影响到了。其实我前面讲的都是针对于静态数组。对于静态数组,它在内存中所占的空间是连续的,只需要用到地址,所以只要一颗;而动态数组是先建立一个一维数组,然后分别在一维数组的元素内再开辟一段连续的空间,它就需要地址的地址,所以需要两个*。这很重要很重要噢!!!

更多维度也是一样的

  1. char str_2[2][2][3] = {
  2. {
  3. "ap\0",
  4. "br\0",
  5. },
  6. {
  7. "ca\0",
  8. "dr\0",
  9. }
  10. };
  11.  
  12. char (*Pstr_3) [2][3] = str_2;
  13.  
  14. i = 0;
  15. int j = 0;
  16. for (; i < 2; ++i)
  17. {
  18. j = 0;
  19. for (; j < 2; ++j)
  20. {
  21. printf( "%s\n", *(*( Pstr_3 + i ) + j) );
  22. }
  23. }

[C]二级指针的更多相关文章

  1. 对线程等待函数pthread_join二级指针参数分析

    分析之前先搞明白,这个二级指针其实在函数内部是承接了上个线程的返回值. 看man手册,发现返回值是个普通指针.人家用二级指针来承接,可能准备干大事.这个可以自己搜索一下.原因嘛,二级指针是保存了这个地 ...

  2. 【C】二级指针探秘 & 星号的两种用法(1.与基本类型结合形成另一种类型,比如与int结合形成int* 2.取值操作)

    1)问题:二级指针到底是什么?怎么用的?怎么存放的? #include <stdio.h> #define TEST_ADDR 0x12FF40 void main() { int a = ...

  3. C语言一级指针与二级指针

    指针的概念 指针就是地址, 利用这个地址可以找到指定的数据 指针就是地址, 那么在使用的时候, 常常会简单的说 指针变量为指针 指针变量就是存储地址的变量 int *p1;// 申请了一个变量, 即在 ...

  4. C++二级指针第二种内存模型(二维数组)

    C++二级指针第二种内存模型(二维数组) 二维数组 二维数组本质上是以数组作为数组元素的数组,即“数组的数组”. 定义 类型说明符 数组名[常量表达式][常量表达式] 例如: float a[3][4 ...

  5. C语言 二级指针内存模型混合实战

    //二级指针内存模型混合实战 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #i ...

  6. C语言 二级指针内存模型③

    //二级指针内存模型③ #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #incl ...

  7. C语言 二级指针内存模型②

    //二级指针第二种内存模型 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #incl ...

  8. C语言 二级指针内存模型①

    //二级指针第一种内存模型 #include<stdio.h> #include<stdlib.h> //说明:①:类似于int a[5]={0},数组名a是一维数组a中首元素 ...

  9. Linus:利用二级指针删除单向链表

    Linus大神在slashdot上回答一些编程爱好者的提问,其中一个人问他什么样的代码是他所喜好的,大婶表述了自己一些观点之后,举了一个指针的例子,解释了什么才是core low-level codi ...

  10. 【转】Linus:利用二级指针删除单向链表

    原文作者:陈皓 原文链接:http://coolshell.cn/articles/8990.html 感谢网友full_of_bull投递此文(注:此文最初发表在这个这里,我对原文后半段修改了许多, ...

随机推荐

  1. jquery 控制 video 视频播放和暂停

    $('video').trigger('play'); $('video').trigger('pause'); 参考:https://blog.csdn.net/arvin0/article/det ...

  2. SQL Server进阶(八)查询——开窗函数、四大排名函数、透视数据、逆透视数据

    概述 ROW_NUMBER() OVER(PARTITION BY CustId ORDER BY ID DESC) https://www.jb51.net/article/75533.htm 开窗 ...

  3. adb server version (31) doesn't match this client (40); killing...

    删除360的手机助手即可解决,进程名字360MoblieMgr.exe

  4. Quartz.NET 配置文件详解

    Ø  前言 在之前的 使用 Topshelf 结合 Quartz.NET 创建 Windows 服务 文章中,使用到了 Quartz 的两个配置文件 quartz.config 和 quartz_jo ...

  5. None.js 第一步 开启一个服务 hello world

    引入 http 模块 var http = require('http'); 创建服务器 http.createServer(function (request, response) { // 发送一 ...

  6. pycharm上传代码到github

    一.配置pycharm 点击create API,添加自己的github账号,切记将clone git 那个对勾去掉,不然会报: Successfully created project ‘…’ on ...

  7. C++ 窗口

    DestroyWindow();     //销毁窗口 可重载的事件: PostNcDestroy    窗口销毁后调用

  8. python 的基础 学习 第八天数据类型的补充 ,集合和深浅copy

    1,数据类型的补充: 元组()tuple,如果只有元素,并且没有逗号,此元素是什么数据类型,该表达式就是什么数据类型. tu = ('rwr') print(tu,type(tu)) tu = ('r ...

  9. Centos 02 操作系统 & Linux安装

    操作系统的概念 操作系统是沟通使用者和硬件之间传递信息的工具或程序,是电子计算机系统负责支撑应用程序运行环境以及用户操作环境的基础系统软件 硬件 ==> 系统核心 ==> 命令解释器she ...

  10. Java-Servlet--《12-WEB应用中的普通Java程序如何读取资源文件.mp4》 有疑问

    \第五天-servlet开发和ServletConfig与ServletContext对象\12-WEB应用中的普通Java程序如何读取资源文件.mp4; 多层时,DAO为了得到资源文件中的配置参数: ...