IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言日记 25(2) 补充:二维数组与指针理解具体过程详解 -> 正文阅读

[C++知识库]C语言日记 25(2) 补充:二维数组与指针理解具体过程详解

一维数组名:首地址;指针常量

我们可以让指针指向一维数组名,然后再做相关操作

下面我们假设有一个二维数组,我们试图通过指针来对他进行操作:

#include<iostream>
using namespace std;
void main()
{
	int i, j;
	int A[3][3] = { {0,1,2},{3,4,5},{6,7,8} };
	cout<< A <<endl;
	cout << *A << endl;
	cout << **A << endl;
}

结果:

?

我们可以看到,这里:

A和(*A)输出的都是一个地址(而这里的这个地址显然就是第0行第0列的地址)

只有(**A)输出的结果是一个数值;是首个元素的值本身


A:二维数组名

{

书P83:数组名是数组首(个)元素的内存首地址

书P94:数组名就是指向数组首地址(第一个元素地址)的指针常量

}

所以,这里A代表整个二维数组的首(个)元素(A[0])的内存首地址;

或者说A作为指针指向A[0](整个二维数组的第0行)的首(个)元素的地址;

{

A[0]是(一个)由【A[0]([0])、A[0]([1])、A[0]([2])】这几个数组元素所组成(构成)的一个一维数组

而A[0],就是【A[0]([0])、A[0]([1])、A[0]([2])】这些(所有的)元素所组成的一个一维数组的总和,以A[0]作为这(个)整个的名称(名字)

我们可以不恰当地说:(不严谨)

A[0]这个一维数组可以看作二维数组A[3][3]的一个子数组

}

而“A[0]的内存首地址”,即“一维数组的内存首(个元素的)地址”;

自然就是X系统给A[0]这个一维数组的首元素A[0][0]所安排的内存地址了。


*A:二维数组的一个一级指针

{

C语言日记 25 指针与数组_宇 -Yu的博客-CSDN博客

“a[i]”改为“*(a+i)”,运行情况相同

当然,把数组名作指针名,他们仍然会有区别:

如数组会自动分配空间,而指针不能;数组名不能赋值,但指针可以

}

*A作为一个一级指针,其实在本质上等价于A:(A[0]等价于(*A))

指向A[0](整个二维数组的第0行)的首(个)元素的地址;


而(**A)在这里的这个输出语句里面,并不代表二级指针

相当于*(*A),即:

求A[0](整个二维数组的第0行)的首(个)元素的值;

那么对于二维数组

我们能否像对付一维数组那样(书P92),定义一个指针指向数组的首元素然后一个一个输出呢?

显然可以:

#include<iostream>
using namespace std;
void main()
{
	int i, j;
	int A[3][3] = { {0,1,2},{3,4,5},{6,7,8} };
	int* p;
	p = *A;
	for (i = 1; i <= 3; i++)
		for(j = 1; j <= 3; j++)
	{
		cout << *p << "   ";
		p++;
	}
}

结果:

但是我们在这里要知道:

我们在实际使用来输出的工具仍是一个一级指针,说白了我们输出的方式和我们之前已经学过的方法没有任何区别,我们对此没有任何长进;而且,这里我们不能直接使用数组名A:

#include<iostream>
using namespace std;
int main()
{
	int i, j;
	int A[3][5] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
	int *p;
	p = A;
	for (i = 0; i < 3; i++)
		for (j = 0; j < 5; j++)
			cout <<1 << "   ";
}

结果:

因此虽然以及指针就能(以二维数组构造的思维模式)输出整个数组,在这里我们还是应该把思维关注的重点放到二级指针之上。

那么我们是不是可以像书上说的那样,把二维数组名理解为一个指针的指针呢?

如果可以的话,即:

我们可以定义一个int **p(指针的指针),然后就可以将数组名赋给p,然后对二维数组进行相关操作;

like:

#include<iostream>
using namespace std;
int main()
{
	int i, j;
	int A[3][5] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
	int **p;
	p = A;
	for (i = 0; i < 3; i++)
		for (j = 0; j < 5; j++)
			cout <<1 << "   ";
}

?可是结果:

?也就是说:

书上说的所谓的“二维数组名为一个指针的指针”这个说法其实是完全错误的!!!

“无法从“int [3][5]”转换为“int **”已经完全表示了二维数组名不是指针的指针;

而从C语言日记 25 指针与数组_宇 -Yu的博客-CSDN博客的例6-4(2)里面我们也看到了,

正确使用的打开方式为:

#include<iostream>
using namespace std;
int main()
{
	int a[3][5] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
	int i, j;
	int(*p)[5];
	p = a;
	for (i = 0; i < 3; i++)
		for (j = 0; j < 5; j++)
			cout << p [i][j] << "   ";
}

或者说是:

#include<iostream>
using namespace std;
int main()
{
	int a[3][5] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
	int i, j;
	int (*p)[5];
	p = a;
	for (i = 0; i < 3; i++)
		for (j = 0; j < 5; j++)
			cout << *(*(p + i) + j) << "   ";
}

即:

先定义一个数组指针int (*p)[3],然后将数组名A赋给p,进行相关操作

即二维数组名是一个数组指针,p+1指向的是下一个数组(即下一行的地址)

{

数组指针:

指向数组地址的指针,本质上就是个指针;

指针数组:

数组元素为指针的数组,其本质为数组。(例如 int *p[3],定义了p[0],p[1],p[2]三个指针)

}

?二维数组指针

具体例如:

int a[3][5] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
int(*p)[5];
p = a;

注释:

int(*p)[5]:

有点A[0]([0])的意思(味道),被赋址后指向的,就是数组名所代表的:

整个二维数组的首(个)元素(A[0])的内存首地址A[0]([0]);

所以我们可以说:

整个【(*p)[5]】(作为一个整体),被赋址后,就是一个指针

而拿他没有被赋址的时候单拎出来看,由于他被赋址后,就是一个指针,那么:

【(*p)[5]】自然就是一个指针变量;

也就是说拿【(*p)[5]】封装好作为整体来看,他和(*p)本质上也没有什么区别


具体拆解【(*p)[5]】来看:(拿他和二维数组A[x]([y])对照比较)

(*p)部分就相当于A[x]部分(行地址),[5]部分就相当于[y]部分(列地址);

(*p)[5]表示的,是(一个)由【(*p)[0]、(*p)[1]、(*p)[2]、(*p)[3]、(*p)[4]】这几个数组元素所组成(构成)的一个一维数组;

同时这整个数组名,作为一个指针指向整个(这个)数组的首元素的地址(数组名不是本来就指向首地址嘛)

或者说,我们这里可以不严谨的不拘泥于符号,不拘泥于认为给我们规定好的死规矩:

p[5]本身就是一个一维数组(这个总是很正常,没什么奇怪的了吧)

如同a[i]==&a[i][0],*(p+i)==&a[i][0];

但是对于定义一个二维数组,我们可以让他“当p变为(p+1)的时候,让指针指向整个二维数组的第二行”推展至i,即:

p+i==&a[i];*(p+i)==a[i];

而拉出p本身看,它本身其实就代表着一个一维数组,“[5]"表示一维数组包含5个元素

*(p+2)+3表示a[2][3]地址(2加在p位置里面,3加在一维数组的5个位置里面;【从0加起】)

*(*(p+2)+3))表示a[2][3]的值。

这里很有意思且一石二鸟的地方是:

一来(向外)“(*p)[5]”作为整个*(p+i)系列的首个元素,本身就对应了数组名找的是二维数组的首元素

同时(向内)数组名(*p)[5],本身就被看做(*p)[5]这个一维数组的首(个)元素(p[0])的地址(指针(指向这个地址))

为什么**p不能用

二维数组名即数组首地址,不是指针的指针。

表面上:

行地址即一维数组指针,而数组名指向行搜索即指针的指针。

但是如果A[3][3],int**p=A;

执行p++时,编译器如何知道长度(列宽:一行有多少列)?

所以,A[3][3]的地址类型,不是简单的“指针的指针”,而是行指针的指针。

而行宽是由定义的数组列数和元素类型所决定的;

例如:int类型就是4*3=12个字节。(数据对齐)

所以A的地址类型应该是int ()[3],而不是int **。

所以应该:int (*p)[3]=A;

其含义为:

p是一个指向(含3个int类型元素的一维数组or行的)指针

其实我们完全可以将一种指针类型强制转为任何其他类型;

那为什么还要区分指针类型呢?

为了实现数据对齐,准确定位。

?附:

数组指针和指针数组的区别 - jack_Meng - 博客园

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-10-22 20:56:54  更:2022-10-22 20:58:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 6:00:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码