C语言多维数组

文章目录

  • 多维数组
    • 数组名
    • 下标
    • 指向数组的指针
    • 作为函数参数的多维数组
  • 指针数组
  • 小结

多维数组

如果某个数组的维数超过1,它就被称为多维数组,例如,下面这个声明:

int matrix[6][10]

创建了一个包含60个元素的矩阵。但是,它是6行每行10个元素,还是10行每行6个元素?
为了回答这个问题,你需要从一个不同的视点观察多维数组。考虑下列这些维数不断增加的声明:

int a;
int b[10];
int c[6][10];
int d[3][6][10];

a是个简单的整数,接下来的那个声明增加一个维数,所以b就是一个向量,它包含10个整形元素。

c只是在b的基础上再增加一维,所以我们可以把c看做是一个包含6个元素的向量,只不过它的每个元素本身是一个包含10个整形元素的向量。换句话说,c是个一维数组的一维数组。d也是如此,它是一个包含三个元素的数组,每个元素都是包含6个元素的数组,而这6个元素中的每一个都是包含10个整形元素的数组,间接地说,d是一个3排6行10列的整形三维数组。

·

数组名

一维数组名的值是一个指针常量,它指向数组的第一个元素,它的类型是“指向元素类型的指针” 。多维数组也差不多简单,唯一的区别是多维数组第1维的元素实际上是另一个数组。例如,下面这个声明:

int matrix[3][10];

创建了matrix,它可以看做是一个一维数组,包含3个元素,只是每个元素恰好是包含10个整形元素的数组。
matrix这个名字的值是一个指向它第一个元素的指针,所以matrix是一个指向一个包含10个整型元素的数组的指针。

下标

如果要标识一个多维数组的某个元素,必须按照与数组声明时相同的顺序为每一维都提供一个下标,而且每个下标都单独位于一对方括号内。在下面的声明中:

int matrix[3][10];

表达式matrix[1][5]访问下面这个元素
在这里插入图片描述

但是,下标引用实际上只是间接访问表达式的一种伪装形式,即使在多维数组中也是如此,考虑下面这个表达式:

matrix

它的类型是“指向包含10个整型元素数组的指针”,它的值是
在这里插入图片描述
表达式 matrix+1也是一个“指向10个整型元素数组的指针”,但它指向matrix的另一行
在这里插入图片描述
为什么?因为1这个数值根据包含10个整型元素的数组的长度进行调制,所以它指向matrix下一行,如果对其执行间接访问,就如下图随箭头选择中间的这个子数组
在这里插入图片描述
所以表达式*(matrix+1)事实上标识了一个10个整型元素的子数组。数组名的值是个常量指针,它指向数组的第一个元素,在这个表达式中也是如此。它的类型是“指向整型的指针”,我们现在可以在下一维的上下文环境中显示它的值。
在这里插入图片描述
现在请拿稳你的帽子,猜猜下面这个表达式的结果是什么?

*(*(matrix+1)+5)

它所访问的正是那个整型元素。如果它作为右值使用,你取得存储于那个位置的值,如果它作为左值使用,这个位置将存储一个新值。
这个看上去吓人的表达式实际上正是我们的老朋友–下标,我们可以把表达式*(matrix+1)改写成matrix[1],把这个下标表达式带入原先的表达式,我们将得到:

*(matrix[1]+1);

这个表达式完全合法的,matrix[1]选定一个子数组,所以它的类型是一个指向整型的指针,我们对这个指针加上5,然后执行间接访问操作。
但是,我们可以再次用下标代替间接访问,所以这个表达式还可以写出:

matrix[1][5]

指向数组的指针

下面这些声明合法吗?

int vector[10],*vp = vector;
int matrix[3][10],*mp = matrix;

第一个声明是合法的。它为一个整型数组分配内存,并把vp声明一个指向整型的指针,并把它初始化为指向vector数组的第一个元素。vector和vp具有相同的类型:指向整型的指针。但是第2个是非法的。它正确地创建了matrix数组,并把mp声明为一个指向整型的指针。但是mp的初始化是不正确的,因为matrix并不是一个指向整型的指针,而是一个指向整型数组的指针。我们应该怎样声明一个指向整型数组的指针呢?

int (*p)[10];

下标引用的优先级高于间接访问,但由于括号的存在,首先执行的还是间接访问。所以p是个指针,但它指向什么呢?
接下来执行的是下标引用,所以p指向某种类型的数组。这个声明表达式中并没有更多的操作符,所以数组的每个元素都是整数。
声明并没有直接告诉你p是什么,但推断它的类型并不困难,当我们对它执行间接访问操作时,我们得到的是个数组,对该数组进行下标引用操作得到的是一个整型值。所以p是一个指向整型数组的指针

在声明中加上初始化后是下面这个样子:

int (*p)[10] = matrix;

它使p指向matrix的第一行。

作为函数参数的多维数组

作为函数参数的多维数组名的传递方式和一位数组名相同,实际传递的是个指向数组第一个元素的指针。但是,两者之间的区别在于,多维数组的每个元素本身是另外一个数组,编译器需要知道它的维数,以便为函数形参的下标表达式进行求值。这里有两个例子,说明了它们之间的区别:

int vector[10];
... 
func1(vector);

参数vector得我类型是指向整型的指针,所以func1的原型可以是下面两种的任何一种:

void funcl(int *vec);
void func1(int vec[]);

作用于vec上面的指针运算把整型的长度作为它的调整因子。
现在让我们来观察一个矩阵:

int matrix[3][10];
...
func2(matrix);

这里matrix的类型是指向包含10个整型元素数组的指针。func2的原型应该是怎样的呢?你可以使用下面两种形式中的任何一种:

void func2(int (*p)[10];
void func2(int mat[][10]);

在这个函数中,mat的第一个下标根据包含10个元素的整型数组的长度进行调整,接着第2个下标根据整型的长度进行调整,这和原先的matrix数组一样。

在编写一维数组形参的函数原型时,你既可以把它写成数组的形式,也可以把它写成指针的形式。但是,对于多维数组,只有第1维可以进行如此选择。尤其是,把func2写成下面这样的原型是不正确的:

void func2(int**mat);

这个例子把mat声明为一个指向整型指针的指针,它和指向数组的指针并不是一回事。

指针数组

除了类型之外,指针变量和其他变量很相似,正如你可以创建整型数组一样,你也可以声明指针数组。这里有一个例子:

int *api[10];

为了弄清楚这个复杂的声明,我们假设它是一个表达式,并对它进行求值。

下标引用的优先级高于间接访问,所以在这个表达式中,首先执行下标引用。因此,api是某种类型的数组,元素个数为10。在取得一个数组元素之后,随机执行的是间接访问操作,这个表达式不再有其他操作,所以它的结果是一个整型值。
那么api到底是什么东西?对数组的某个元素执行间接访问操作后,我们得到一个整型值,所以api肯定是个数组,它的元素类型是指向整型的指针。

小结

一维数组的数组名指向第一个元素,类型是指向元素类型的指针。
二维数组的数组名是也指向它第一个元素,类型是指向数组的指针。
指针的指针是指向某种类型指针的指针,它和指向数组的指针并不是一回事。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/379546.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

fwrite函数的用法示例_C语言中的fwrite()函数(带有示例)

fwrite函数的用法示例C中的fwrite()函数 (fwrite() function in C) Prototype: 原型: size_t fwrite(void *buffer, size_t length, size_t count, FILE *filename);Parameters: 参数: void *buffer, size_t length, size_t count, FILE *filenameRetu…

伙伴算法、slab机制、内存管理函数

文章目录1 伙伴算法页框操作alloc_pages()2 slabslab机制要解决的问题使用高速缓存3 内存管理函数kmallockzallocvmallocvzalloc区别参考文章内核使用struct page结构体描述每个物理页,也叫页框。内核在很多情况下,需要申请连续的页框,而且数…

Javaweb---监听器

1.什么是监听器 监听器就是监听某个对象的状态变化的组件。 事件源:被监听的对象 ----- 三个域对象 request session servletContext 监听器:监听事件源对象 事件源对象的状态的变化都会触发监听器 ---- 62 注册监听器:将监听器与事件源进行…

Linux中的Ramdisk和Initrd

Ramdisk简介先简单介绍一下ramdisk,Ramdisk是虚拟于RAM中的盘(Disk)。对于用户来说,能把RAM disk和通常的硬盘分区(如/dev/hda1)同等对待来使用,例如:redice # mkfs.ext2 /dev/ram0mke2fs 1.38 (30-Jun-200…

slab下kmalloc内核函数实现

文章目录kmalloc的整体实现获取高速缓存高速缓存获取index总结https://blog.csdn.net/qq_41683305/article/details/124554490,在这篇文章中,我们介绍了伙伴算法、slab机制和常见的内存管理函数,接下来,我们看看kmalloc内核函数的…

标题:三羊献瑞

标题:观察下面的加法算式: 其中,相同的汉字代表相同的数字,不同的汉字代表不同的数字。 请你填写“三羊献瑞”所代表的4位数字(答案唯一),不要填写任何多余内容。 思路分析: 首先…

进程虚拟地址管理

文章目录1 地址分布实际使用中的内存区域2 进程的虚拟地址描述用户空间mmap线程之间共享内存地址的实现机制1 地址分布 现在采用虚拟内存的操作系统通常都使用平坦地址空间,平坦地址空间是指地址空间范围是一个独立的连续空间(比如,地址从0扩…

标题:加法变乘法

标题:我们都知道:123 … 49 1225 现在要求你把其中两个不相邻的加号变成乘号,使得结果为2015 比如: 123…10*1112…27*2829…49 2015 就是符合要求的答案。 请你寻找另外一个可能的答案,并把位置靠前的那个乘号左…

【翻译】eXpressAppFramework QuickStart 业务模型设计(四)—— 实现自定义业务类...

这一讲,你将学到如何从头开始实现业务类。为此,将要实现Department和Position业务类。这些类将被应用到之前实现的Contact类中。你将学到引用对象自动生成用户界面的基本要素。 在此之前,我建议你去阅读一下 【翻译】eXpressAppFramework Qui…

内存重映射

文章目录1 kmap2 映射内核内存到用户空间使用remap_pfn_range使用io_remap_pfn_rangemmap文件操作建立VMA和实际物理地址的映射mmap 之前分配 一次性映射mmap 之前分配 Page FaultPage Fault 中分配 映射内核内存有时需要重新映射,无论是从内核到用户空间还是从内…

math.sqrt 有问题_JavaScript中带有示例的Math.sqrt()方法

math.sqrt 有问题JavaScript | Math.sqrt()方法 (JavaScript | Math.sqrt() Method) The Math.sqrt() method is inbuilt in JavaScript to find the square root of a number. In this tutorial, we will learn about the sqrt() method with examples. JavaScript中内置了Mat…

ISAPI Rewrite 实现简单url重写、二级域名重写

实现步骤: 第一步:下载ISAPI_Rewrite.rar,将Rewrite文件夹和httpd.ini直接放在项目根目录下面。 第二步:IIS配置,筛选Rewrite文件夹里面的Rewrite.dll文件,如图: 第三步:在httpd.ini…

用户登录

用户登录 代码namespace 用户登录 {public partial class Form1 : Form{public Form1(){InitializeComponent();}bool b1, b2, b3, b4, b5, b6;private void button1_Click(object sender, EventArgs e){try{if (b1 && b2 && b3 && b4 && b5 &…

进程上下文和中断上下文

文章目录进程的preempt_count变量thread_infopreempt_counthardirq相关softirq相关上下文原文链接: https://zhuanlan.zhihu.com/p/88883239进程的preempt_count变量 thread_info 在内核中,上下文的设置和判断接口可以参考 include/linux/preempt.h 文…

标题:凑算式

标题:凑算式 这个算式中AI代表19的数字,不同的字母代表不同的数字。 比如: 68/3952/714 就是一种解法, 53/1972/486 是另一种解法。 这个算式一共有多少种解法? 注意:你提交应该是个整数,不要…

Linux内存地址管理

文章目录系统内存布局内核地址的低端和高端内存概念低端内存高端内存地址转换和MMULinux中的四级分页模型虚拟地址字段页表处理将虚拟地址转换物理地址Linux系统中的每个内存地址都是虚拟的,它们不直接指向任何物理内存地址。每当访问内存位置时,可以执行…

录制caf 转 mp3

编译需要使用的 lame库http://www.cocoachina.com/bbs/read.php?tid108237参考的文章http://blog.csdn.net/ysy441088327/article/details/7392842说起来,我一直在找一个音频转换成mp3的方法。一年前,我成功编译出了一个lame for armv7的库。苦于不会使…

開發記要 詭異的變量

告別繁體文盲,從寫blog開始 Variable命名很重要,有多重要,看看.net和java的加密就知道, 都是把variable改到一塌糊塗,你想看看都沒門. 但是這幾天看遺留系統的代碼,真是大開眼界。 我一直以為別人寫a,b,c,d這些單字節variable已經很過分。直到我看到以下這幾個&#xff0…

排序算法---快速排序、堆排序、冒泡排序

排序算法1 快速排序代码实现stdlib库快排2 堆排序堆排序的基本思想如何构造一个大顶堆排序3 冒泡排序1 快速排序 文章原地址:https://blog.csdn.net/morewindows/article/details/6684558 快速排序的平均时间复杂度是0(NlogN),它采用了一种分治的策略&a…

项目总结:华南师范大学校园开发教育android客户端总结

忽略之前小打小闹,这个项目算是我的第一个项目--SCNU的网络公选课的android版本的客户端。项目是从5月中旬开始的,中间经历了几个星期的复习考试时间,到现在可以说是完工了吧(或许还有写细节要修改)。这个项目带给我蛮…