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

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

int a = 5;
int* b = &a;
int** c = &b;

你不能这样

int a = 5;
int** c = &a;

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

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

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

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

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

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

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

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

for (; i < ARR_LEN; ++i)
{
*( Pstr_1 + i ) = i;
}

关于const修饰符

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

int a = 5;
const int* b = &a;
const int** c = &a;
**c = 6;

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

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

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

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

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

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

int a = 5;
int* b = &a;
int** c = &b;
**c = 6;
printf("%d\n", a);

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

高能时刻

int a = 5;
const int* b = &a;
const int** c = &b;
int d = 6;
const int* e = &d;
c = &e;
printf("%d\n", **c);

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

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

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

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

const char **Pstr_1 = malloc( sizeof( char* ) * 3 );
int i = 0; for (; i < 3; ++i)
{
*( Pstr_1 + i ) = malloc( sizeof( char ) * 3 );
*(*( Pstr_1 + i )) = i;
}

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

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

二维数组的指针

我们知道,要在函数中处理数组,一般要给函数传递指针。那么,对于一维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] *类型。

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

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

更多维度也是一样的

char str_2[2][2][3] = {
{
"ap\0",
"br\0",
},
{
"ca\0",
"dr\0",
}
}; char (*Pstr_3) [2][3] = str_2; i = 0;
int j = 0;
for (; i < 2; ++i)
{
j = 0;
for (; j < 2; ++j)
{
printf( "%s\n", *(*( Pstr_3 + i ) + j) );
}
}

[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. tomcat下的Cookie特殊符号问题

    案例:在项目中通过Cookie方式临时存放检索条件,不小心在Cookie值中使用了特殊符号"@",导致在服务器端无法正确解析Cookie值.之所以说"不小心", ...

  2. SpringBoot 2.0集成spring-data-elasticsearch

    1 资料 https://segmentfault.com/a/1190000015568618 https://github.com/JeffLi1993/springboot-learning-e ...

  3. 设置 Visual Studio IIS Express 站点局域网访问

    Ø  Visual Stuido 的 IIS Express运行一个网站时,默认地址是这样的:http://localhost:23167/Cache/Three,其中 localhost 表示本机, ...

  4. error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": http://landinghub.visualstudio.com/visual-cpp-build-tools

    解决方案 1. http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted 下载twisted对应版本的whl文件(我的Twisted‑17.5.0‑cp36 ...

  5. Immunity Debugger学习

    1.Immunity Debugger简介 Immunity Debugger软件专门用于加速漏洞利用程序的开发,辅助漏洞挖掘以及恶意软件分析.它具备一个完整的图形用户界面,同时还配备了迄今为止最为强 ...

  6. JavaScript 小工具

    1. 字符串格式化输出 支持形如: Orders of {1} or more {0}' {0},{1}代表第几个参数,包含了完善的异常处理.当给定参数少于格式化串中占位符个数时,未找到的直接留白. ...

  7. 几个js框架

    easyui适合做后端 bootstrap适合前端 layui 其实更偏向与后端开发人员使用,在服务端页面上有非常好的效果.

  8. kettle mysql 乱码

    在数据库连接上添加字符集编码参数characterEncoding, 指定UTF8或GBK

  9. 20165221 JAVA第二周学习心得及体会

    基本数据类型与数组理论学习 根据第二章的网课链接,归纳出以下板块: 知识框架 标识符与关键字 1.标识符 其本质是文件名字 标识符的第一个字符不能为数字,标识符不能为关键字(如inter) 标识符不能 ...

  10. Pftriage:分析和追踪恶意文件,识别特征

    项目地址 PFTriage:https://github.com/idiom/pftriage 参考 Pftriage:如何在恶意软件传播过程中对恶意文件进行分析 https://www.freebu ...