C 指针万字详解(二级,内存,数组,函数....)

我是struggle,编程小白一枚,今天让我们来一起学习关于指针的知识吧!😃😃


在这里插入图片描述
在学习c语言的时候,我们都听过这样一句话,指针是c语言的灵魂,那么指针到底是什么呢?指针的特殊之处又在哪里呢?今天就和struggle一起重温/学习指针吧😆!

一、 🚩指针简介

通俗的说,指针变量就是一个变量,只不过是一个值为内存地址的变量。

📍简介内存

这里简单的介绍一下什么是内存地址,每个变量在CPU中都占据一定的内存单元,这个内存单元的编号,就是地址,下面是一个32位的内存示意图:
内存
这里的一个方格就代表着一个字节,一个int 类型的数据,一共占据4个字节,你定义了一个int类型的变量以后,系统就会自动给他分配一个内存空间,如上图中,红色部分就是整形变量a所占据的内存空间,在方格的旁边有一串数字,数字就是内存单元的编号,也就是地址,虽然int类型占据了四个内存空间,但是我们取他首个内存单元的编号,作为地址,也就是 1,当然,我们之前见过一些地址的值,他的值并不是1,2,3,4,5,而是0x…,这是因为,在内存中,地址的值都是16进制的数。
举个例子,我们定义整型变量a和b,来看看他们的地址

int a,b;
printf("0x%p 0x%p ",&a,&b);

在这里插入图片描述

我们可以看到,这里的地址使用十六进制来表示的,而且他们的差值是4,也说明只是取了首个内存单元对应的地址。

📍指针声明

那么,应该如何声明一个指针呢?

int *p = &a;

这里出现了两个我们没有见过的运算符 :* 和&。这里的 * 并非是我们常用的二元乘法运算符,他有着不同的意义:
间接运算符:*

b = *p;//找出p指向的值

这里的*又称间接运算符:

p = &a;
b = *p;
//等价于 b = a;

地址运算符:&

p = &a;

这条语句的意思就是把a的地址赋给p,&a的意思就是变量a的地址。
我们画图理解一下:
在这里插入图片描述
我们先定义了一个整形变量a,然后把10赋给a,再把a的地址赋值给指针变量p,这时候,p的值就是整形变量a的地址,所以我们也称这时候,p指向了a,*p是解引用操作,实际上就是对p中储存地址对应的变量,也就是a进行操作,此时我们进行改变 p的值时,实际上就是直接对a的值进行改变了:
在这里插入图片描述

对于*和&我们还需要知道的一点是,这两个运算符是互逆的
在了解了指针变量的基本概念以后,为了能让大家更明确的了解指针变量的运行方式,我们先来介绍一下二级指针(仅仅是为了加深大家的理解,不会深入讲解):
在这里插入图片描述

如上图所示,现在指针变量p中储存的就是a的地址,现在我们来详细的介绍一下指针变量p,我们常说,指针有两值,一个就是自身储存的地址值,另一个就是指针指向的值,我们来看一下,p的两值都是什么:
p = 0x00eff740; //p储存的地址,也就是a的地址
&p = 0x00eff728; //p的地址
*p = 10 ;//(解引用操作)p指向的值
下面我们将进行二级指针的操作:
在这里插入图片描述
在这里,我们又定义了一个指针变量,只不过这个指针变量储存的是指针变量p的地址,也称为s指向p
在进行了上述操作后,我们再来分析一下,指针变量s的两值以及s的地址是什么:

&s = 0x00ef71c  ;
*s = 0x00eff740  ;
**s = 10;
s  = 0x00eff728  ;

现在我们能看出来了,二级指针变量不过也是指针变量而已,没有离奇的地方,二级指针变量的值也是一个地址而已,二级指针指向的值也就是指针变量p储存的值而已;这里说一下 **s ,他的意思就是解引用 * s 而 *s 是解引用s,所以*s的值就是s中储存的地址对应变量的值,也就是p的值,**s就是a的值。
那我们要是进行 p = &b ;这个操作呢?那就是把p的指向改变了
在这里插入图片描述
此时 *p就等于20了,那 **s的值也就对应变成20了,那么我们如果改变了p的指向,s的指向也就随着发生了改变,我们就可以做很多有意思的东西了,关于二级指针,我们先说这么多,再以后的文章中会有更详细的介绍。

♨️♨️♨️指针变量的类型

我们正常的变量有 int、char、double,当然,指针变量也有他的类型:

在这里插入图片描述
先不说这些类型的指针变量有什么含义,我们先来看看这些指针变量的大小是什么,是 4 1 4 8?,还是一些别的东西?
在这里插入图片描述
我们可以看到,在32位下,所有的指针都是四个字节,在64位下呢?,就会是八个字节,那为什么会这样呢?为什么指针变量的大小会是四个字节呢?
32位和64位有什么区别呢?首先,三十二位的意思是cpu最大寻址空间是2^32(最多能同时处理数据的大小是2 ^32),我们来思考一下,变量的大小对应着数据的储存范围,而指针变量中,储存的是地址,也就是说,如果我们想要表示完整的寻址空间,地址的范围应该是从2^0---2^32,那么指针变量的存储范围就应该是2^0 --- 2^32,换算下来就是四个字节(一个字节等于八位)
原来是寻址空间搞的鬼,32位下有232位的寻址空间,所以指针变量的大小就是四个字节,64位下264位的寻址空间,所以指针变量的大小就是八个字节

我们之前说过,指针有两值,那么对应的,指针也有两个类型,一个是指针的类型(int * ),一个是指针所指向的类型(int)
在这里也简单的说一下我们应该如何判断指针的两型:
例如: int *p;
如果把p去掉,那就是指针的类型; 如果把 *p去掉 那就是指针所指向的类型;
那么,我们现在再来说一下指针变量指向的类型有什么作用, short叫做短整型,用short定义的普通变量叫做短整型变量,那么用 short 定义的指针变量,就叫做短整型指针变量,意义就应该是,指针指向的值是一个整型变量,如果我们没有指向一个短整型变量,会发生什么呢?我们来执行以下操作
在这里插入图片描述

如果指针变量正常的运作了,那么a的值应该会改变成255555,我们来运行一下
在这里插入图片描述

可以看到,运行上述操作后,a被改变成了一个我们不知道的值,这告诉我们,指针指向的变量与其指向的类型应该是严格对应的,稍有不慎,就会导致错误,但是这个错误的原因是什么呢?如果我们对内存有所了解,那么我们可以通过观察内存来解释这个问题
首先我们通过a的地址来查看a在内存中的储存情况(没被改变)
内存
由于变量a的类型是int 有四个字节,对应的就是有八个十六进制位,在内存中的值是 9b 00 00 00 ,由于在内存中变量的值是反着存的,所以a的十进制值就是 9×16+11 = 155;
现在我们再来看看被改变以后的
在这里插入图片描述
a的值变成了43 6e 00 00,对应的十进制数就是 3+16×4+16×16×6+16×16×16×14 = 58947
这个值看起来和我们所给的255555没有关系,但是如果我们把255555转换成十六进制,就可以发现一些规律了
在这里插入图片描述
如果我们按照内存储存的规则,把转换一下那就是 43 e6 03 00 ,我们可以发现,e6 43 被储存了进去,但是03却消失了,这是为什么呢?为什么我们要一直强调指针的指向的类型与其指向的变量类型应该是严格对应的呢?
这是因为: short类型只有两个字节 那么short * 所能改变的也就只有那两个字节而已,所以所有超出两个字节的数据,就被截断了,只剩下了 e6 43.
所以说,指针变量的类型决定着他最多能对多少字节的空间来进行操作,如int * 就是以四个字节为一个整体,多出四个字节,你是没有权限操作的。

📍泛型指针

在有了上面的知识储备以后,我们可以介绍一些特别的东西了。
我们都知道,变量的类型有 int char double…,但是还有一个很特殊的类型:void,空类型。空类型是否能定义变量呢?让我们来试一下。
在这里插入图片描述
非常遗憾的是,不能,那么void 是否能定义一个指针变量呢?这是可以的
而且,他的功能十分强大;
在这里插入图片描述
我们可以看到 void类型的指针可以指向各种类型的变量 而且不需要进行强制转换,这样的指针,我们叫做泛型指针;
为什么说他强大呢?我们思考一下,可以发现,泛型指针有很强的通用性,你给他任何一个类型,他都能装的下去,方便我们写一些通用的函数,但是他也有不足的地方:
只能接收,不能使用。
在这里插入图片描述
虽然我们用泛型指针接受了这个变量,但是我们不能用解引用操作了改变变量的值,这是为什么呢?我们在上文刚刚提到过,指针的类型决定着他能操作多少字节的整体空间,void没有类型,系统怎么知道他要操作多少字节的空间呢?所以,我们在用泛指针解引用操作时,应该先加一步强转,才可以进行操作:
在这里插入图片描述

📍野指针

在我们操作指针的时候很容易出现一种错误,那就是定义了一个指针却没有给他地址,这时候你要是对指针解引用的话是会出现大问题的,程序会崩溃
在这里插入图片描述

在这里插入图片描述
这是因为现在你的指针指向了一个随机的内存空间,但是这个内存空间你并没有去申请,所以你的访问是非法的,这次运气好程序没有崩溃,如果你的指针随机到了一些特殊的地址,程序是会崩溃的,那我们该如何防止这种错误的发生呢,这时候就要引入空指针的概念:

📍空指针

在定义一个指针变量以后,我们可以给他赋值一个NULL:

int* a = NULL;

这时候就说,这个指针是一个空指针,在早些时候,我们说这时候指针指向的值是一个零值,0x000000 但是现在,我们说这个指针不指向任意一个内存空间,他就是空的,当然空指针也不能对他进行解引用操作,但是现在他储存的值是什么,你已经知道了,就可以避免出现一些错误。

♨️常量指针

有时,我们不想让指针的指向改变,或是不想让指针指向的值在被解引用的时候改变,这时我们就会用到常量指针或是指针常量。
常量指针:
在这里插入图片描述
可以看到常量指针的定义有两种方式:

const int*p;
int const *p;

这两种定义方式是相同的,定义了一个常量指针以后,虽然我们的指向还可以改变,但是不能通过解引用来改变变量的值了,但是并不是说变量的值就不可以被改变了,只不过是不能通过解引用的方式改变而已。
指针常量:
指针常量与常量指针不同,指针常量的指向不能被改变,但是指向的值可以通过解引用的方式改变
在这里插入图片描述
当我们想改变指针的指向时,编译器会提醒我们,这是个错误,我们画图来理解一下:
在这里插入图片描述
当然有指针的指向不能改变的,有指向的值不能通过解引用改变的,也就会有都不能改变的,那就是常量指针常量

int a;
const int *const p = &a;//全不可以修改

这就是常量指针常量,两值都不能发生改变。
一会指针常量,一会常量指针,是不是听着头都大了?现在,我们来看一个小技巧,让你轻松掌握他们的区别::看const封锁的谁主要看const在 * 号的左右,在左边封锁的是*p(指针指向的值);在右边封锁的是指针变量自身的值,也就是指针的指向。

习题:

初步介绍了指针的概念和声明以后,我们来做一道小题,看看你有没有真正的理解上述内容

int a = 10;
int * p = &a;
int *p2 = p;

那么经过了以上操作以后,p2的指向是什么呢?指向的是p吗?还是别的什么东西?

答案:指向变量a ,因为把p指向a以后,p中储存的地址就是a的地址,我们再把p储存的地址给了p2,那么p2就和p一样储存的都是a的地址,他们都指向a。
如果还不明确的话,我们画图理解一下:
在这里插入图片描述
这样我们也可以看出,p和p2储存的都是变量a的地址,都是指向变量a的。

如果你答对了这道题,那么恭喜你,你已经对指针的两值有了基本的了解,可以继续往下学习了,如果没有答对也不用灰心,推荐大家多敲代码,然后按照你的代码来绘图,这样你会对指针有更深刻的了解。

二、 🚩指针的运算

在对指针有了基本的了解以后,我们来看看指针能进行什么样的运算,以及运算的规则是什么?

♨️算术运算

在了解了指针的两型后,我们知道,指针都是以整体空间的形式进行操作的,如果是一个整型指针变量,那么给指针+1就是把指针指向了四个字节之后的地址,放在数组中,我们能看的更明确一些:

在这里插入图片描述
我们先给出一个整型指针p 让他指向一个整型数组a[4],这时,指针指向的是数组的第一个元素a[0],如果让p++,那就是指向四个字节以后的地址,在数组中对应的就是第二个元素a[1],再让p++就会指向第三个元素a[2]…
在这里插入图片描述
那让我们在程序中验证一下,我们所推理的是否正确
在这里插入图片描述
我们让指针p先指向数组a的第一个元素a[0],在让p++这时应该指向的是a[1],再让p–,这时指向的应该是a[0],再让p+=3,这时应该指向的是a[0+3] = a[3]
如果我们没有推断错误,应该打印出的值是 1 2 1 4.
在这里插入图片描述
推断正确,通过这个指针的算术运算我们应该加深对指针两型的理解,指针的两型决定着指针是对多少空间进行操作,以及一次会操作多少空间。

♨️指针变量之间的减法

当两个指针指向同一个数组中的元素时,我们可以进行指针变量之间的减法运算,得到的值就是指针指向数组元素下标的差值:
在这里插入图片描述
在这里插入图片描述
或许你会有这样的疑问?指针变量中储存的不是一个地址吗?上一个地址和下一个地址相减,不应该是-4吗?理论上说,确实是这样,但是编译器在处理这条指令时,会根据你指向的类型来进行换算,换算成元素下标的差值而已。

♨️关系运算

当两个指针指向同一个数组时,我们也可以进行关系运算 如>,>=,==,<,<=
编译器会自动比较,哪一个是数组中更前或更后的元素。由于这是判断,所以返回的值就是真或假,也就是1或0。
在这里插入图片描述
在这里插入图片描述

📍小结

在学习了指针的关系运算与算数运算后,我们有几点需要注意:
1.指针变量之间是不能进行加法运算的,这是不合法的。
2.只有相同类型的指针才能进行关系运算,最好是指向同一片地址空间(数组),不指向同一片空间,指针的关系运算是没有意义的。
3.如果没有指向一个数组,我们最好不要对指针进行算术运算,因为这样很有可能会改变其他变量的值或者产生错误导致程序崩溃。

三、 🚩指针与数组

在了解数组与指针的关系之前,我们先来回忆一下数组名是什么?假如我们定义了一个数组,让我们来看看数组名代表的值是什么
在这里插入图片描述
在这里插入图片描述
可以看到,数组名的值是数组首元素的地址,上面也是数组的三值合一
这里解释一下这三个值的意义:
&a[0]是取数组首元素的地址。 那在打印a的时候也没有取地址符号啊,为什么还会打印地址值呢?那是因为,a是数组名,他代表了整片的地址空间,在数组名中,储存了一个值,而这个值呢,正好就是首元素的地址。 再来看最后一种,&a,很明显这是对a取地址,而我们刚刚说过,a代表的是整个空间,那我们的操作就是在对整个空间取地址,整个空间的地址,就由空间中第一个元素的地址来代替,所以也打印了数组首元素的地址。
我们可以看到,既然数组名的值是一个地址,那么我们是否能用指针来指向这一片内存空间,然后进行访问呢?
在这里插入图片描述
在这里插入图片描述
答案是,当然可以。
可以看到,我们通过每次对指针加一,来把数组元素遍历了一遍,并且输出。因为我定义的是一个整型指针变量,所以每次指针加一相当于让让指针指向下一个数组元素(也可以理解为因为int是四个字节,所以我们的指针依一次加了4),那我们再来观察一下数组本身的引用方式
在这里插入图片描述
通过观察我们发现指针对数组元素的调用方式似乎和数组本身数组名+下标的引用方式相似,我们可以大胆推测一下,是不是数组名+下标的操作和指针引用相似呢?或者说,数组其实也是指针的形式?我们实验一下:
我们把数组名当作一个指针,对他进行解引用操作
在这里插入图片描述
我们可以看到,这和刚才指针的调用方式是一摸一样的,并且,运行结果也完全一致。
在这里插入图片描述
其实,电脑在读取数据的时候也是按照指针的方式读取的。所以,我们在使用数组名加下标的方式进行操作的时候,其实最后也翻译成了*(a+i)的形式,a就是首元素的地址,也可以看成某种类型的指针,那对他+i的操作呢?就是根据他的类型,对地址进行操作,如果是int 地址就加4,这样就可以跳到下一个数组元素了,在我们已知这是一片连续空间的前提下,数组方式和指针方式都是允许的。
同过分析数组的调用方式,我们知道了,数组和指针有着十分亲密的联系。

📍数组指针

数组指针其实就是指向数组的指针

int a[5] = {0}; 
int (*p)[5] = &a;

这段话的意思是什么呢?这个指针指向了五个元素,而且这五个元素的类型都是整型,什么样的类型能用一个地址表示五个整型数据呢,那就是数组了。所以说,这句话的意思就是这个指针要指向一个有五个整型元素的数组,值得注意的是,虽然p的值是数组首元素的地址,但是p实际上指向的是整片数组空间,而非首元素,我们来画图理解一下:
在这里插入图片描述
这也说明了,你只能指向一个有五个整型元素的数组,如果不是五个元素,或者元素不是整型,都是不被允许的。

📍指针数组

说完了数组指针的概念,我们来说一下指针数组,指针数组,侧重点是数组,我们解读一下,就是一个储存指针的数组,那么他的代码如下:
在这里插入图片描述
我们可以看到,这和我们平时定义数组的方式其实并没有区别,都是类型+变量名+元素个数,但是数组的变量是指针变量,所以说,给数组赋值也应该赋地址。画图理解如下:
在这里插入图片描述

📍小结

让我们对比一下数组指针和指针数组

int (*p)[5] = &a;
int *p[3] = {&a,&b,&c};

可以看到,他们的区别就在于有没有(),为什么会有这样的区别呢,那我们要知道 *的结合力没有[ ]高,也就是说,定义数组指针的时候,如果我们不加(),p就会被[ ]先结合,而变成一个数组了!所以,在定义数组指针的时候要加小括号。
在这里,我们不对数组与指针更深层次的应用做解释,先了解,在深入的学习以后,我们再来研究。

四、 🚩指针与函数

在了解指针与函数的关系之前,我们先回忆一下,我们当初是如何调用一个函数的?
在这里插入图片描述
当我们定义了一个函数之后,我们就可以通过函数名传参,来进行调用,现在我们定义的是一个void类型的函数,所以直接用用函数名就可以调用了。
在这里插入图片描述
但其实,函数名有另一个名字,那就是函数的入口地址。既然是地址,那么我们能不能通过一个指针来接收这个入口地址呢?

📍函数指针

下面,我们就来说一下函数指针的概念,函数指针就是一个指向函数的指针,具体应该怎么操作呢?
在这里插入图片描述
这时候要串联起我们之前说过的知识点,指针的“两型”,因为指针指向的类型和指针本身的类型应该相同。我们定义了一个int类型的函数,并给他传进两个参数,所以指针也应该是int型有两个参数类型,于是就写成了int (*pfun)(int,int);
了解了函数指针,我们不由得有些疑问,指向了函数他又能做什么呢,我们该如何使用呢?
在这里插入图片描述
结合我们之前学习的指针知识,我们来解读这一行代码

(*pfun)(a,b);

之前我们学过*号是解引用:
例如 int a =1;int *p = &a;
这时候我们让 * p=a,同理,现在就是 * p=fun,因为fun是一个有两个参数的函数,所以我们就要在后面传值进去,就是(a,b) 。这就是通过指针来调用函数。
其实,不解引用,也可以起到相同的作用:

pfun(a,b);

效果与之前解引用的相同,为了更充分的理解,我们来看看函数名,函数名的地址,指针的值,指针指向的值都是什么
在这里插入图片描述
通过监视,我们可以看到 fun与&fun的值相同,因为fun本身表示的就是fun的入口地址,pfun与*pfun的值相同,因为指针的值对应函数的入口地址,指针指向的值对应fun,也是函数的入口地址,所以我们才能通过指针来引用函数。

📍指针函数

指针函数的侧重点是指针,我们知道,除了空函数,其他的函数都有返回值,所以,指针函数就是返回值为地址的函数。
指针函数的声明也和其他函数的声明一样 都是 类型+函数名+参数表
让我们来看一个简单的例子:
在这里插入图片描述
在这里,我们定义了一个整型指针函数,他的返回值是最大值的地址。
然后我们再在主函数中打印这个最大值:
在这里插入图片描述
这就是指针函数,他的类型和返回值的都要是指针。

📍小结

让我们来对比一下指针函数和函数指针的定义/声明

int (*p)(int int);//函数指针
int *p(int a,int b);//指针函数

可以看到,函数指针与指针函数的区别也在于有没有()的问题,有就是指针函数,这是因为,*号的结合力没有()强,如果没有用()圈中,那么p就会先结合后面的(int a,int b)而形成一个指针函数了所以我们在记忆的
时候,就可以从结合性出发,通过比较结合性来判断到底是数组指针还是指针数组,到底是函数指针还是指针函数。

📍函数的值传递与址传递

在学习指针之前,我们对函数的操作都是值传递,比如说我们之前写的交换值函数
在这里插入图片描述
我们期望执行函数以后,a和b的值会发生改变,可事实上,我们把a和b传入函数中,函数中进行的操作并不是直接对a和b进行,而是重新定义了两个变量来接收a和b的值,这也就是我们常说的,形参是实参的拷贝
在这里插入图片描述
可以看到,a和b的值并没有发生我们想要的变化,那么在学了指针以后,我们可以进行怎样的操作呢?
在这里插入图片描述
我们把函数中的参数变成了两个指针,传入的参数变成了a和b的地址,这样有什么作用呢?我们来画图理解一下:
在这里插入图片描述

我们可以看到指针变量a指向整型变量a,指针变量b指向整型变量b,这样我们在解引用的时候就相当于直接在对主函数中的a和b进行操作了,效果如下
在这里插入图片描述
这就是函数的址传递,通过传递地址的方式,函数也可以对主函数中的数据产生影响了。

🚩结语

当你学习到这里,今天的内容就到此结束了,当然,这只是指针的冰山一角而已,越深入,越能体会到指针的奥妙,如果你觉得我的文章对你有帮助,不妨点个关注期待一下下一期的内容呀😆😆

  • 16
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
### 回答1: 好的,以下是用C语言编写一个二级指针数组的示例代码: ``` #include <stdio.h> #include <stdlib.h> int main() { int rows = 3, cols = 4; int i, j; // 声明二级指针数组 int **arr = (int **) malloc(rows * sizeof(int *)); for (i = 0; i < rows; i++) { arr[i] = (int *) malloc(cols * sizeof(int)); } // 对二级指针数组进行初始化和输出 for (i = 0; i < rows; i++) { for (j = 0; j < cols; j++) { arr[i][j] = i * j; printf("%d ", arr[i][j]); } printf("\n"); } // 释放二级指针数组内存 for (i = 0; i < rows; i++) { free(arr[i]); } free(arr); return 0; } ``` 该示例代码中,首先声明了一个二级指针数组`arr`,并动态分配了内存。然后使用双重循环对二级指针数组进行了初始化和输出。最后在程序结束前,释放了二级指针数组内存。 ### 回答2: C语言中,二级指针数组可以通过以下方式声明和定义: ```c #include <stdio.h> int main() { int a = 10, b = 20, c = 30; int *arr[] = { &a, &b, &c }; // 声明一个数组数组元素是int类型的指针 for (int i = 0; i < 3; i++) { printf("%d\n", *arr[i]); // 访问并输出指针数组的值 } return 0; } ``` 在上述示例中,我们声明了一个二级指针数组`arr`,它包含了3个指向`int`类型的指针元素。我们可以通过在声明时给出每个元素的地址来初始化该数组。 然后,在for循环中,我们使用`*arr[i]`来访问并输出指针数组中的值。`*arr[i]`表示解引用第`i`个指针元素。 最终的输出结果将是: ``` 10 20 30 ``` 这样,我们就成功地通过C语言实现了一个二级指针数组,并且访问了其值。 ### 回答3: 要用C语言写一个二级指针数组,首先需要理解二级指针的概念。二级指针是指指向指针指针,用来间接地访问指针所指向的数据。 一个二级指针数组可以看作是一个由指针组成的数组,每个元素都是一个指向指针指针。为了声明和操作一个二级指针数组,可以按照以下步骤进行: 1. 首先确定数组的大小,即二级指针的个数,假设为N。 2. 声明一个二级指针数组,可以使用如下语法:`int** arr = (int**)malloc(N * sizeof(int*))`,这里假设每个指针指向的是int类型的数据。 3. 分配内存给每个二级指针,以便指向相应的数据。可以使用类似下面的语法:`arr[i] = (int*)malloc(M * sizeof(int))`,这里假设每个二级指针指向的数组大小为M。 4. 通过二级指针数组可以访问指向的数据,比如可以使用`arr[i][j]`来访问第i个指针指向的第j个元素。 下面是一个简单的示例代码,展示如何使用C语言实现一个二级指针数组: ``` #include <stdio.h> #include <stdlib.h> int main() { int N = 3; int M = 4; int** arr = (int**)malloc(N * sizeof(int*)); for (int i = 0; i < N; i++) { arr[i] = (int*)malloc(M * sizeof(int)); for (int j = 0; j < M; j++) { arr[i][j] = i + j; // 给每个元素赋值 printf("%d ", arr[i][j]); // 输出每个元素 } printf("\n"); } // 释放内存 for (int i = 0; i < N; i++) { free(arr[i]); } free(arr); return 0; } ``` 以上是一个简单的例子,它创建了一个3x4的二级指针数组,然后给每个元素赋值并输出。最后,记得释放内存,以避免内存泄漏。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值