在使用malloc()函数申请动态数组,重新对多维数组与多重指针之间的关系的思考:数组名就是指针,可以用多重指针和数组的方式访问;顺便对嵌套循环效率的一些解释。

发布时间:2021-10-25 03:14:46

一.先从用malloc()函数申请多维动态函数说起。

C语言中用普通的方式定义的数组其大小不能改变。如通过int a[N];定义大小为N的整型数组,其后N的改变不会再改变原来数组大小。但我们可以通过malloc()或calloc()等动态存储分配函数申请分配一块空间,将其返回的所分配单元的起始地址赋给指针,我们就可以利用得到的指针进行和数组一样的操作。因为我们知道普通的数组名其实就是数组的起始地址,相当于一个指针常量。


1.定义一个大小为n的整型动态数组,如下:


int *a;

int n;

n = 10; //此处设为10

a =(int *)malloc(sizeof(int)*n); /*动态分配存放10个整型的内存,将所分配空间起始地址转换为

整型地址赋给指针a*/

a[5] = 100; /*引用同一般数组,但不应超出所分配内存的大小

a[5]相当于*(a+5), 即从首地址向后跳过5个(整型)内存*/

free(a); //释放a所指的内存区

在知道地址和变量类型的情况下,a[i]等同于*(a+i),即从首地址开始跳过i个已知类型的数据元素。这两个内存函数原型为void *malloc(usigned size);和void *calloc(unsigned n, unsign size);它们包含在中。两者区别有二:一是前者分配size个字节大小的连续空间,后者分配n个size字节的连续空间。二是后者分配的内存初始化为0;而前者没有;如果想改变一个已分配地址的内存区的大小可以用函数void *realloc(void *p, unsign size)。这几个函数返回void 指针类型,赋值给其他类型指针时,需经过类型转换,系统一般在编译时也进行隐式转换。


2.定义一个三维动态数组大小为n1,n2,n3


想得到多维的的动态数组,可以嵌套使用前面的方法,如将得到的动态数组的元素a[i]再定义为动态数组。即定义指向指针的指针,由外向内逐级分配空间。


int ***a; //定义指针的指针的指针

int n1, n2, n3;

int i, j, k;

scanf("%d%d%d",&n1,&n2,&n3);

a=(int ***)malloc(n1*sizeof(int **)); /*a有n1个元素,每个元素仍为数组(其实是指针的指针)

a为数组首地址,a[1],a[2],...,a[n1]为其子数组元素首地址,以此类推直至得到元素*/
for(i=0; i
{
a[i]=(int **)malloc(n2*sizeof(int *)); //a[i]有n2个元素,每个元素为一维数组的首地址(其实是指针)
for(j=0; j {
a[i][j]=(int *)malloc(n3*sizeof(int)); //a[i][j]为一维数组,大小为n3
for(k=0; k {
a[i][j][k] = i*n2*n3+j*n3+k; //元素a[i][j][k]的引用同一般数组
}
}

}

.......;

//使用后要释放存储空间,注意由内向外逐级释放

for(i=0; i{
for(j=0; j free(a[i]);
}
free(a);

多维数组分配存储空间从外往内,释放空间时要由从内往外。函数分配的内存空间,即使没有指针指向,它仍然存在,不自动回收,所以不能通过这定指针为空来释放空间。


3.从上述多维动态数组的分配了解指针与数组的关系


从外向内:下图以一个P[2][2][2]的三维动态数组举例:

上图为申请动态数组的内存结构图,从图中可以看出来,内存并不连续。


int a[3][4];
a[0][0]=4;
a[2][3]=4;
int *p=a;
//int (*p)[4]=a;
//p=a;


printf("%d%d",*(*(a+2)+3),*p);

上面这个程序可以输出结果:4 4
但是并不能通过二重指针的方式来访问对应的数组,如下打印代码:


printf("%d%d",*(*(a+2)+3),*(p+11));

要想以二重指针的方式以上面第一个格式打印出来的,得通过以下定义:


int a[3][4];
a[0][0]=4;
a[2][3]=4;
//int *p=a;
int (*p)[4]=a; /*这里很像定义了一个指针数组,但并不是,指针数组没有括号,
这里就是一个指针,就是相当于定义了一个指向四个int型长度为一个单位的指针变量p,
这样就会把二维数组按照4段一个单位,把4个单位相应的首地址存储在p中,整个的存储结构和上面的三维动态数组是一样的*/
//p=a;


printf("%d%d%d",*(*(a+2)+3),*(*(p+2)+3)),p[2][3]);

输出结果都为4;后面加一个数组下标对应二维的数量,这里就和定义二维数组初始化一样,必须要知道低维度所有的数量,才能确定这个数组的大小,这里才能知道。动态与静态数组的区别就在于数据地址的连续性。


int a[3][4];
a[0][0]=4;
a[2][3]=4;
//int *p=a;
int (*p)[4]=a;
//p=a;
int c[2] ={3};
int f[2]={1};
int *d[2]={c,f};
for (int i = 0; i < 2; i++)
{
printf("%d
",*d[i]);
}

int r[2]={1};
for (int i = 0; i < 2; i++)
{
printf("%d
",r[i]);
}

printf("%d%d%d",*(*(a+2)+3),*(*(p+2)+3),p[2][3]);

以上代码可以看出指针数组与动态数组的区别。


二.聊聊for循环嵌套的效率问题

因为CPU流水线和因为流水线导致需要分支预判这两个工作机制的原因,会让大部分人认为for循环嵌套总是次数多的放在内部花费的时间少,原因主要是因为,次数多的放在内部,分支预判出错的概率就会少(分支预判就是if,switch这样的有判断的),但事实并非如此,有很多因素会影响花费的时间;如果处理的指令是取内存中的数据并对其赋值等操作,因为内存的取存操作需要CPU先将内存中的数据渠道CPU中的缓存中取,缓存中的数据才是直接和CPU交换的,保证了CPU的处理速度与取数据的速度保持一致,最大的利用CPU的性能,这时因为CPU中缓存的容量很小一般有5兆左右,存储的数据都是需要经常或马上会使用的,而当对二维数组存储时,数组中同一个维度的数据会在一种算法的作用下(这个算法来维持内存和缓存中数据的稳定,是缓存中的数据保留的全是需要经常使用的)先被送到缓存中去,这时如果是直接对连续的数组数据进行操作,效率就较高,如果是分层对数组数据操作,CPU在缓存中的命中率就会低很多,不断要去内存中获取数据,效率就会低不少,所以这种情况下就不应内层次数多的时间少了;
参考如下例子:


void test1()
{
long dt = DateTime.Now.Ticks;
for (int i = 0; i < 1000000; i++)
{
for (int j = 0; j < 10; j++)
{
a[i,j] = 0;
}
}
Console.WriteLine("test1:{0}",DateTime.Now.Ticks - dt);
}

void test2()
{
long dt = DateTime.Now.Ticks;
for (int j = 0; j < 10; j++)
{
for (int i = 0; i < 1000000; i++)
{
a[i,j] = 0;
}
}
Console.WriteLine("test2:{0}",DateTime.Now.Ticks - dt);
}

这个就是test1更快。
https://blog.csdn.net/schumyxp/article/details/2398935?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-4.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-4.nonecase


https://zhidao.baidu.com/question/57090974.html

相关文档

  • 月经期间排毒方法
  • 额头大量冒痘什么原因绿豆红枣莲藕能泻火
  • 数学教研组工作计划2005~2006学年度第一学期姚文琴
  • 【z】内存屏障(Memory Barriers)
  • 广西中考语文满分作文
  • ubuntu20.04使用fcitx黑屏,改用ibus输入法
  • IOS-解决UITableView最后一行显示不全的问题
  • 美丽的春光作文3篇
  • 健康饮食的名言
  • 急诊科医生传染病毒是哪一集
  • 小学一年级下册语文期末试卷
  • 大学生学年鉴定个人总结范文精选10篇
  • 氧气的化学性质
  • 银行分部副主任个人竞聘演讲稿
  • 初一的第二周周记优秀范例
  • 加快商贸流通业发展实施方案
  • 分子克隆基础:什么是质粒
  • 又简单又漂亮的五年级数学手抄报图片
  • 手机充电器必须用原配的吗
  • 高中抗击肺炎的作文400字
  • 2014年12月大学英语四级作文题目预测:讲座与讨论
  • 考试感想
  • 打动女神内心的情书作文
  • 幼儿园教师年度个人工作总结精选
  • LinkedList中将对象按照某一属性排序
  • 几种新兴宽带接入技术的介绍和比较
  • 孩子吃食用菌能补脑吗?孩子吃什么食物补脑
  • 函数调用时的三种参数传递方式
  • 娱乐英语新闻:Jean-Jacques Annaud to direct Wolf Totem
  • 关于友情的作文素材
  • 猜你喜欢

  • Spring Cloud项目搭建(二)Eureka注册中心搭建
  • 大学生毕业实习报告模板(汇总篇)
  • 屈服的同义词及造句
  • 邓州市环境空气自动监测系统2016年12月数据统计表
  • 党课1500字总结
  • 液压泵的使用方法
  • 云南省昆明市盘龙区(禄劝县)2017-2018学年八年级上学期期末考试物理试题试卷含答案(WORD版)
  • 委托加工合同通用模板
  • 金华市泰来包装材料有限公司企业信用报告-天眼查
  • 安妮股份:*安证券有限责任公司关于公司使用部分闲置募集资金暂时补充流动资金的专项意见 2010-10-29
  • “铁血尖刀”激战大路坝
  • 高 中 物 理 实 验 复 习
  • 我的同学_三年级作文_24
  • 秋天_六年级作文_26
  • 永嘉稻田“湘云鲫”生态养殖技术
  • The Efficiency of Globular Cluster Formation
  • 掌握这一点,教你读懂女人心
  • 工程总承包项目经理作业指导书90475
  • 生物质致密成型清洁燃料项目投资策划方案范文
  • 皇马巴萨18天连踢四场
  • 旬阳县向阳水电站(企业信用报告)- 天眼查
  • 10个艾草治病中医偏方 秋季香薰艾草减肥效果显著
  • 部编版小学语文三年级下册期末试卷(含答案)【新选】
  • 绍兴巴乔文化用品有限公司年产300万套智能控制器项目环评报告表
  • 甘肃省河道采砂管理的现状与对策
  • 根的判别式与韦达定理
  • 【人教版】六年级下册数学单元四_3第3课时《图形的放大与缩小》教案设计
  • 亲子教育-幼儿园小朋友新年贺词简短版【三篇】 精品
  • 商标专用权质押合同
  • java前端学习笔记(第一天)
  • 雕塑项目可行性研究报告(可编辑)
  • OpenStack查看版本
  • 部编版三年级语文下册口语交际 劝说 公开课课件(17张ppt)
  • 人教版四年级科学下《二 新的生命 动物的卵》公开课课件_0
  • 科目三路考灯光
  • 某小学门卫管理制度汇编32
  • JNI搭建:native方法注册或NDK中的方法对应
  • 初二英语上学期module4unit1外研英语
  • 食用硫酸铝钾项目可行性研究报告编写格式说明(模板套用型word)
  • 关于区科技工作瑟肽旯ぷ髯芙
  • 2014届名师导学高考物理第一轮复*课件:第十三章 *代物理 人教版
  • 浅谈量子密钥分配技术
  • 电脑版