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

下面的实例代码创建了一个二级指针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. 五.HashTable原理及实现学习总结

    有两个类都提供了一个多种用途的hashTable机制,他们都可以将可以key和value结合起来构成键值对通过put(key,value)方法保存起来,然后通过get(key)方法获取相对应的valu ...

  2. 【python小练】0005

    第 0005 题:你有一个目录,装了很多照片,把它们的尺寸变成都不大于 iPhone5 分辨率的大小. 首先,iphone5的分辨率是1136x640. if条件句判断横(纵)向是否大于对应的ipho ...

  3. 【python小练】0000

    第 0000 题:将你的 QQ 头像(或者微博头像)右上角加上红色的数字,类似于微信未读信息数量那种提示效果. 类似于图中效果 from PIL import Image, ImageDraw, Im ...

  4. 近几年ACM/ICPC区域赛铜牌题

    2013 changsha zoj 3726 3728 3736 3735 2013 chengdu hud 4786 4788 4790 2013 hangzhou hdu 4770 4771 47 ...

  5. 第25月25日 urlsession

    1. private lazy var session: URLSession = { let configuration = URLSessionConfiguration.default conf ...

  6. IIS7配置Gzip压缩

    II7中自带了gzip功能,理论上应该比ii6配置起来应该简单一点,但是容易出的问题比较多.有的II7配置web服务器角色的时候可能没有安装启用动态内容压缩,所以这个钩子是灰色的,需要再次安装. 如图 ...

  7. MFC工作者线程

    //************工作者线程**************1.在头文件中添加UINT ThreadFunc(LPVOID lpParam); 注意应在类的外部 2.添加protected型变量 ...

  8. ionic3安装

    1.安装 1)安装nodejs: 官网下载安装 2)可选安装 cnpm:  //安装了cnpm后,以后用到npm安装的东西都可以把npm替换成cnpm npm install -g cnpm --re ...

  9. STL之permutation/ equal_range/ binary_range学习

    1,is_permutation 函数,判断其中一个序列是不是另外一个序列的全排列. 包括四个参数,前两个是第一个数组需要判断的起始位置和终止位置.后两个是第二个数组需要判断的起始位置和终止位置. # ...

  10. 调整JVM虚拟机的内存大小

    jvm默认只有64M; public static void main(String[] args){ byte b[] = new byte[1024*1024*65];//此时会报内存溢出: } ...