[C]二级指针
二级指针即“指向指针的指针”;
下面的实例代码创建了一个二级指针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]二级指针的更多相关文章
- 对线程等待函数pthread_join二级指针参数分析
分析之前先搞明白,这个二级指针其实在函数内部是承接了上个线程的返回值. 看man手册,发现返回值是个普通指针.人家用二级指针来承接,可能准备干大事.这个可以自己搜索一下.原因嘛,二级指针是保存了这个地 ...
- 【C】二级指针探秘 & 星号的两种用法(1.与基本类型结合形成另一种类型,比如与int结合形成int* 2.取值操作)
1)问题:二级指针到底是什么?怎么用的?怎么存放的? #include <stdio.h> #define TEST_ADDR 0x12FF40 void main() { int a = ...
- C语言一级指针与二级指针
指针的概念 指针就是地址, 利用这个地址可以找到指定的数据 指针就是地址, 那么在使用的时候, 常常会简单的说 指针变量为指针 指针变量就是存储地址的变量 int *p1;// 申请了一个变量, 即在 ...
- C++二级指针第二种内存模型(二维数组)
C++二级指针第二种内存模型(二维数组) 二维数组 二维数组本质上是以数组作为数组元素的数组,即“数组的数组”. 定义 类型说明符 数组名[常量表达式][常量表达式] 例如: float a[3][4 ...
- C语言 二级指针内存模型混合实战
//二级指针内存模型混合实战 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #i ...
- C语言 二级指针内存模型③
//二级指针内存模型③ #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #incl ...
- C语言 二级指针内存模型②
//二级指针第二种内存模型 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #incl ...
- C语言 二级指针内存模型①
//二级指针第一种内存模型 #include<stdio.h> #include<stdlib.h> //说明:①:类似于int a[5]={0},数组名a是一维数组a中首元素 ...
- Linus:利用二级指针删除单向链表
Linus大神在slashdot上回答一些编程爱好者的提问,其中一个人问他什么样的代码是他所喜好的,大婶表述了自己一些观点之后,举了一个指针的例子,解释了什么才是core low-level codi ...
- 【转】Linus:利用二级指针删除单向链表
原文作者:陈皓 原文链接:http://coolshell.cn/articles/8990.html 感谢网友full_of_bull投递此文(注:此文最初发表在这个这里,我对原文后半段修改了许多, ...
随机推荐
- 常见排序算法之python实现
冒泡排序 简介 冒泡排序(英语:Bubble Sort)是一种简单的排序算法.它重复地遍历要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.遍历数列的工作是重复地进行直到没有再需要交 ...
- Android studio在新窗口中打开新项目
- 无法定位程序输入点 InitializeCriticalSectionEx、GetTickCount64
(1)方法一:在vc项目中把对应的方法名改为 InitializeCriticalSection.GetTickCount. (2)方法二:添加如下定义#define WINVER ...
- spring注解第05课 FactoryBean
1.工厂bean调用 @Configuration public class MainConfig2 {/** * 使用Spring提供的 FactoryBean(工厂Bean); * 1).默认获取 ...
- None.js 第五步 Buffer(缓冲区)
// 存数据,取数据; buf = Buffer.alloc(26); for (var i = 0; i < 26; i++) { buf[i] = i + 97; } console.log ...
- 四、文件IO——内核数据结构和原子操作
4.1 缓存 buff 说明 一般设置缓存 buff 的大小是由一定的规律的,就是根据磁盘块的大小来定. Linux下输入命令: df -k 查看磁盘 可以用命令查看下 /dev/sda1 磁盘的 ...
- Commons Lang 介绍
https://commons.apache.org/proper/commons-lang/ https://commons.apache.org/proper/commons-lang/javad ...
- java乱码解决方法
String name = request.getParameter("name"); 乱码解决:String name = new String(request.getParam ...
- BFS与DFS算法解析
1)前言 和树的遍历类似,图的遍历也是从图中某点出发,然后按照某种方法对图种所有顶点进行访问,且仅访问一次. 但是图的遍历相对树的遍历更为复杂,因为图中任意顶点都能与其他顶点相邻,所以在图的遍历中必须 ...
- 【CentOS】JDK的安装
FTP文件上传方式(推荐) # 解压 tar zxvf jdk-9_linux-x64_bin.tar.gz # 修改profile文件 sudo vi /etc/profile # 在文件结尾添加如 ...