以杨辉三角为例,从内存角度简单分析C语言中的动态二维数组

学C语言,一定绕不过指针这一大难关,而指针最让人头疼的就是各种指向关系,一阶的指针还比较容易掌握,但一旦阶数一高,就很容易理不清楚其中的指向关系,现在我将通过杨辉三角为例,我会用四种方法从内存的角度简单分析动态二维数组,若有不足或错误之处,还请指出!

在讲这之前,以一维数组为例,先重新认识一下数组:

int array[5] = {1, 2, 3, 4, 5};

首先数组名称是该数组的首地址常量,即数组名称就是指针,就有&array[0] == array!

那么我们可以推出*array == array[0] == 1;

这里引入一个概念“指类”(这个概念没有在正规场合出现过,只是我为了方便分析而引入的),其表示指针所指向的空间的类型!array是一个int*类型的指针,那么它的指类就是int类型(比较容易的记忆,就是指针的类型去掉一个*就是其指类!);

其实array[0]是一个表象,其本质应该是*array ;

我们的array是局部变量,在系统堆栈中申请了sizeof(int)*5,即20字节大小的空间,用于存放5个整型数!

因为array的值是该数组首元素的地址(即首地址),那么array+1的意思就是给该数组的首地址这个值增加了一个int类型空间字节数,也就是4字节,从而array+1的值就应该是该数组的下一个int类型元素的地址,即&a[1],所以就有array+1 == &a[1];

那么array加几加几,加的实际就是多少个指类空间大小!

那么*array就可以理解成*(array + 0),同理,array[1] ==*(array + 1),array[2] == *(array + 2)......

上式还可以由加法交换律变形得到array[1] == *(1 + array),那么array[1]透过这一本质来看,其也可以变形成1[array];

*这样写编译器不但不会报错,而且连警告都不会有,但不建议这样书写!

*如果对我上文提到的系统堆栈不了解的话,强烈建议看一看下面的这个博客,后文全部涉及到内存!

*后文我不会用array[index]这种方式,而是用*(array + index)这种最本质的方式

C语言中关于形参与实参关系

这里我们先讲一讲系统堆栈和系统堆:

操作系统将内存分成:系统数据,系统功能调用(核心代码)区域;用户代码和数据区域;系统堆栈区;系统堆区;

系统堆栈是由编译器自动分配,用于存放函数的参数值,局部变量的值等;

而系统堆区是由程序员通过malloc/calloc函数自主申请的空间,系统堆的空间要远远大于系统堆栈的空间,但切记,一定要在使用完毕后,通过free函数释放掉所申请的空间。

*C语言(包括C++)不像Java那样有gc(垃圾回收)机制,gc机制大大减少了程序员的工作量,程序员在Java中通过new申请空间时,只需负责申请,gc会帮助善后(实则是Java的JVM),而C/C++需要程序员自主释放,所以java相比C++要容易掌握!

----------------------------------------------------------------------------------------------------------------------------------

杨辉三角:

关于杨辉三角如何计算得到的问题,我就不累赘了(*^_^*)

方法一:

如图,我们给出了一个六层的杨辉三角,通常的话,我们会给一个静态的二维数组用来存放这个杨辉三角:

int yangHui[row][row];

但是这会生成一个row行×row列的空间,而我们实际用到的的空间要比这小,除了最后一层,其他每一层都会有浪费的空间,为了避免这样的情况,我们就应该想到动态的数组,根据当前行数,通过malloc/calloc动态申请每一层的空间。所以,我们可以用如下的方式表示这个二维数组:

int *yangHui[row];

这种定义看起来很奇怪,其实它完全就是一个一维数组,这个一维数组的大小是row,只不过这个一维数组的每一个元素是由int *类型所组成,其本质就是一个一维的指针数组!我们可以把它定义成以下这种形式:

typedef int* type;
type yangHui[row];

这样看的话就比较好理解了,他就是一个类型为type类型的一维数组!而type就是int*,那么,这个一维数组存放的每一个元素就应该是一个int*类型的的值,那么这个值完全就可以是一个int类型的一维数组的首地址!即yangHui数组里面存放的是row个一维数组的首地址!

铺垫工作完成,下来我们就来生成杨辉三角:

*由于杨辉三角往后的数字越来越大,故以下代码都用long类型!先假定要生成的杨辉三角的层数num = 5;

void creatYangHuiOne(int num);

void creatYangHuiOne(int num) {
	long *yangHui[num];
	int row;
	int col;

	for (row = 0; row < num; row++) {
		*(yangHui + row) = (long *) calloc(sizeof(long), row + 1);
		for (col = 0; col <= row; col++) {
			*(*(yangHui + row) + col) = (row == col || col == 0) ? 1 : *(*(yangHui + row - 1) + col - 1) + *(*(yangHui + row - 1) + col);
		}
	}
        // 关于showYangHuiTriangle函数我会在最后给出,只是为了打印好看,不做重点!
	showYangHuiTriangle(yangHui, num);
	for (row = 0; row < num; row++) {
		free(*(yangHui + row));
	}
}

多次循环得到下列关系

方法二:

void creatYangHuiTwo(int num);
void destoryYangHui(long **yangHui, int num);

void creatYangHuiTwo(int num) {
	long **yangHui = NULL;
	int row;
	int col;

	yangHui = (long **) calloc(sizeof(long *), num);

	for (row = 0; row < num; row++) {
		*(yangHui + row) = (long *) calloc(sizeof(long), row + 1);
		for (col = 0; col <= row; col++) {
			*(*(yangHui + row) + col) = (row == col || col == 0) ? 1 : *(*(yangHui + row - 1) + col - 1) + *(*(yangHui + row - 1) + col);
		}
	}
	showYangHuiTriangle(yangHui, num);
	destoryYangHui(yangHui, num);
}

void destoryYangHui(long **yangHui, int num) {
	int row;

	for (row = 0; row < num; row++) {
			free(*(yangHui + row));
		}
	free(yangHui);
}

方法三:

long **creatYangHuiThree(int num);

long **creatYangHuiThree(int num) {
	long **yangHui = NULL;
	int row;
	int col;

	yangHui = (long **) calloc(sizeof(long *), num);

	for (row = 0; row < num; row++) {
		*(yangHui + row) = (long *) calloc(sizeof(long), row + 1);
		for (col = 0; col <= row; col++) {
			*(*(yangHui + row) + col) = (row == col || col == 0) ? 1 : *(*(yangHui + row - 1) + col - 1) + *(*(yangHui + row - 1) + col);
		}
	}

	return yangHui;
}

与方法二基本一样,只不过返回值是long **类型,将creatYnaghHuiThree函数中yangHui的值即在系统堆中申请的空间的首地址addressRow作为返回值返回!该空间不会随着子函数的调用结束而消失,需要在主函数中释放!

方法四:

void creatYangHuiFour(long ***yangHui, int num);

void creatYangHuiFour(long ***yangHui, int num) {
	int row;
	int col;

	*yangHui = (long **) calloc(sizeof(long *), num);

	for (row = 0; row < num; row++) {
		*((*yangHui) + row) = (long *) calloc(sizeof(long), row + 1);
		for (col = 0; col <= row; col++) {
			*(*((*yangHui) + row) + col) = (row == col || col == 0) ? 1 : *(*((*yangHui) + row - 1) + col - 1) + *(*((*yangHui) + row -1 ) + col);
		}
	}
}

当creatYangHuiFour函数调用结束,栈底栈顶指针回落,系统堆栈申请的子函数的局部变量都奔释放,但是主函数的yangHui空间的值通过指针运算已经由NULL变为ddressRow,而这个空间是在系统堆中申请的,不会随着子函数的调用结束而消失,即该空间还未被释放,故需要在主函数中释放!

打印函数及主函数:

void showYangHuiTriangle(long **yangHui, int num);
int getMaxNumberLength(long num);

int getMaxNumberLength(long num) {
	int count = 1;

	while (num/=10) {
		++count;
	}

	return count;
}

void showYangHuiTriangle(long **yangHui, int num) {
	int len = getMaxNumberLength(*(*(yangHui + num -1) + num/2));
	int i;
	int j;
	int row;
	int col;

	for (row = 0; row < num; row++) {
		for (i = 0; i < num - row - 1; i++) {
				for (j = 0; j < len; j++) {
				printf(" ");
				}
		}
		for (col = 0; col < row + 1; col++) {
			printf("%ld", *(*(yangHui + row) + col));
			if (getMaxNumberLength(*(*(yangHui + row) + col)) < len) {
				for (j = 0; j < len - getMaxNumberLength(*(*(yangHui + row) + col)); j++) {
					printf(" ");
				}
			}
			for (j = 0; j < len; j++) {
				printf(" ");
			}
		}
		printf("\n");
	}
}

int main() {
	long **yangHui = NULL;
	int num;

	printf("请输入行数:\n");
	scanf("%d", &num);
	creatYangHuiOne(num);
	creatYangHuiTwo(num);
	//yangHui = creatYangHuiThree(num);
	creatYangHuiFour(&yangHui, num);
	showYangHuiTriangle(yangHui, num);
	destoryYangHui(yangHui, num);

	return 0;
}

输出如图:

感谢您的阅读(*^_^*)

原文地址:https://www.cnblogs.com/a526583280/p/9250478.html

时间: 2024-10-11 09:40:15

以杨辉三角为例,从内存角度简单分析C语言中的动态二维数组的相关文章

vector动态二维数组(容器的容器)占用内存分析

之前在这里写过一篇"C++中的动态二维数组".在C++中没有动态二维(多维)数组.但是根据原理我们可以自己创建. 在看过STL的vector源代码后"<STL源码剖析>---stl_vector.h阅读笔记"后,想到可以用容器的容器来做二维数组. 创建一个2x4的二维数组.想到的办法是:先创建一个容器的容器,外层大小的2(2行),然后里面容器小大为4(4列). int row=2,col=4; vector<vector<int> &g

动态二维数组一个很奇怪的问题,内存泄露还是???

最近遇到的问题,具体如下: #include <iostream> using namespace std; typedef void(*P[10])(bool& flag); #define N 10000 #define M 10000 template<class T> void fun(bool& flag) { int i; static T** p=nullptr; switch (flag) { case false: p = (T**)new T*[

杭州电子科技大学 Online Judge 之 “杨辉三角(ID2032)”解题报告

杭州电子科技大学 OnlineJudge 之 "杨辉三角(ID2032)"解题报告 巧若拙(欢迎转载,但请注明出处:http://blog.csdn.net/qiaoruozhuo) Problem Description 还记得中学时候学过的杨辉三角吗?具体的定义这里不再描述,你可以参考以下的图形: 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 1 5 10 10 5 1 Input 输入数据包含多个测试实例,每个测试实例的输入只包含一个正整数n(1<=n<

采用一维数组输出等腰三角形的杨辉三角

采用一维数组输出等腰三角形的杨辉三角 心得体会: 在网上查询了杨辉三角的方法,刚开始用在程序中直接定义n的方式输出杨辉三角,后来查询到从键盘输入n的方法,在调试程序时有很多错误的地方,如数组未定义等问题. 原文地址:https://www.cnblogs.com/wlyxjj/p/10630796.html

JAVA实现杨辉三角的三种方式

一.前言 既然是实现杨辉三角,就要知道什么是杨辉三角.如下图,就是两种杨辉三角. (1)等边形状的杨辉三角 (2)直角形状的杨辉三角 在知道这两种都是杨辉三角之后,我们就来实现利用java语言打印出杨辉三角. 二.杨辉三角的规律 第n行有n个数字. 每一行的开始和结尾数字都为1. 用二维数组表示就是a[i][0]=1;  a[i][j]=1(当i==j时): 第n+1行的第i个数字等于第n行的i-1个数字加上第n行的i个数字. 用二维数组表示就是 a[i+1][j]=a[i][j-1]+a[i]

js算法集合(二) javascript实现斐波那契数列 (兔子数列) Javascript实现杨辉三角

js算法集合(二)  斐波那契数列.杨辉三角 ★ 上一次我跟大家分享一下做水仙花数的算法的思路,并对其扩展到自幂数的算法,这次,我们来对斐波那契数列和杨辉三角进行研究,来加深对Javascript的理解. 一.Javascript实现斐波那契数列 ①要用Javascript实现斐波那契数列,我们首先要了解什么是斐波那契数列:斐波那契数列(Fibonacci sequence),又称黄金分割数列.因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为

杨辉三角,二项式系数,组合数,斐波那契数列

古人就是厉害,在此%杨辉大佬,这个杨辉三角真的是好厉害啊. 杨辉三角 杨辉三角,是二项式系数在三角形中的一种几何排列.在欧洲,这个表叫做帕斯卡三角形.帕斯卡(1623----1662)是在1654年发现这一规律的,比杨辉要迟393年,比贾宪迟600年. 排列信息: 杨辉三角有多种重要的性质. 概述: 前提:每行端点与结尾的数为1. 每个数等于它上方两数之和. 每行数字左右对称,由1开始逐渐变大. 第n行的数字有n项. 第n行数字和为2n-1. 第n行的m个数可表示为 C(n-1,m-1),即为从

python 杨辉三角

1 1 2 1 1 3 1 2 1 4 1 3 3 1 5 1 4 6 4 1 6 1 5 10 10 5 1 7 1 6 15 20 15 6 1 8 1 7 21 35 35 21 7 1 9 1 8 28 56 70 56 28 8 1 10 1 9 36 84 126 126 84 36 9 1 11 1 10 45 120 210 252 210 120 45 10 1 12 1 11 55 165 330 462 462 330 165 55 11 1 13 1 12 66 220

打印杨辉三角

使用动态内存打印任意阶杨辉三角: #include<stdio.h> #include<stdlib.h> void init_arr(int **p,intline) {                  for (int i= 0; i<line; i++)                 {                      p[i][0] = 1;                 }                  for (int i = 1; i<