二维数组与指针、任意行列的二维数组

        数组是最常用到的存储结构,数组是在内存上一段连续的存储空间,数组名的本质就是数组的首地址,所以在函数传参时函数形参可以是一个指针,实参传递时直接传递数组名即可。像这样。

void showArray_1D(int *array, int array_length);
int a[] = {5,2,3,58};
showArray_1D(a, sizeof(a) / sizeof(int));

        在函数里我只需要知道数组的首地址 长度是多少,就可以对其进行一些操作,其地址在内存上也是连续的,每个元素地址相隔sizeof(datatype)个字节。

         比如在栈上一个一维数组地址是这样的:0018FF2C 0018FF30 0018FF34 0018FF38 0018FF3C 0018FF40。我只需要知道这个数组的首地址0018FF2C就可以计算出后面元素地址从而进行操作,数组的中括号实质上是计算地址的方式,如果你传递给函数的地址是0018FF34不是数组第一个元素的地址,那么array[-1]也是可以输出数组的元素的,array[-1]的地址就是0018FF30。

        二维数组的名字可以理解为指向指针的指针,每一个指针指向二维数组每一行的首地址,二维数组在内存中也是连续的,它也是像一位数组那样线性排列的,二维数组的本质就是一维数组,array[i][j] = a[i*col+j];

        那么如何得到一个任意行列row*col的二维数组呢,我在一开始的解决方法是如下,用指向指针的指针,先申请row个指针,表示二维数组每一行的首地址,再对这些个指针分给指向空间。

	array = (int **)calloc(row, sizeof(int *));
	for(i = 0; i < row; i++) {
		array[i]   = (int *)calloc(col, sizeof(int));	
	}

        

虽然可以得到一个任意行列的二维数组,但是!这个二维数组只有每一行的地址是连续的,上图可以看出行与行之间元素地址并不连续,因为进行了row次分配堆空间的操作,而不是一次性分配row*col份空间,多次分配堆空间得到的每段空间地址不一定是连续的。

        正确的打开方式:还是用指向指针的指针,先分配row个指针,二维数组row*col,无非就是一维数组row*col个,申请一段空间长度为row*col,然后再把这段空间交给先前分配row个指针,只需要计算一下每一行的首地址在一位数组中的地址完成赋值即可,例如4*5的二维数组 下图。


计算每一个指针指向的地址,array[i] = data + i * col 不难理解。

int **getArray_2_way2(int row, int col) {
	int *data = NULL;
	int **array = NULL;
	int i;

	data = (int *)calloc(sizeof(int), row * col);
	array = (int **)calloc(sizeof(int *), row);
	for(i = 0; i < row; i++) {
		array[i] = data + i * col;
	}

	return array;
}

        输出验证每个元素的地址都是相互连续的。

        另外一种如果给定了二维数组的列,就可以通过数组指针来完成,数组指针和指针数组非常容易混淆。

int *q[3];   //q是一个具有3个指针元素的数组 指针的数组
int (*p)[4]; //p是指针,指向长度为4一维数组 数组的指针       

         指针数组,“存储指针的数组”,int *q[3],q是一个数组,每一个元素是指针,可以指向任意一个一维数组,这个就很凶悍了,我有三个指针放在一个数组里面,打个比方,第一个指针q[0]我想指向长度为5的数组,第二个指针q[1]我想指向长度为2的数组,第三个指针q[2]我想指向长度为6的数组,就像这样:

int *q[3]; 
int a1[] = {1,2,3,4,5};
int a2[] = {6,7};
int a3[] = {8,9,10,11,12,13};

q[0] = a1;
q[1] = a2;
q[2] = a3;

           这样子有点像java中List<List<int>>的样子。

           数组指针:int (*p)[4], p它是一个指针,指向数组的指针,指向一个长度为4的一维数组,同时p的步长不再是一个int大小而是4个int大小的长度,p+1要扩过4个int元素。可以让其指向一个二维数组,p每加一,相当于在二维数组里面跨过一行。注意此时指针的指类不是int!

int (*p)[4];
int array3[3][4] = {5,2,6,6,2,5,1,5,6};
p = array3;     //这里报警告的话 强转一下

            array[i][j] = p[i][j] = *(p[i]+j) = *(*(p+i) + j)这四者是等价的。

        理解指针和数组一定要站在内存的角度上去思考,数组无非就是内存上的一段存储空间,一旦知道数组的地址 就可以用指针去指向这个首地址 就如同桌面的快捷方式一般。

        通过数组指针完成任意行列的二维数组:这样子得到的二维数组元素地址也是连续的,一次性申请二维数组大小的空间,强转给数组指针即可完成。

int (*array3)[4] = NULL;
data = (int *)calloc(sizeof(int), row*col);
array3 = (int (*)[4])data;

        二维数组的打印:通常情况下,要将二维数组传递到函数里面,要么是二阶指针,要么是确定列数传参。我们可以思考既然二维数组的地址如同一维数组一样是连续的,那就只传递一个二维数组的首地址也就是array[0][0]的地址,不就完成了传参,通过一阶指针就可以去访问二维数组了。如下,双层循环仅仅是为了换行方便。这样子前提必须是数组地址是连续的。

void print_array_2D(int *array, int row, int col) {
	int i;
	int j;

	for(i = 0; i < row; i++) {
		for(j = 0; j < col; j++) {
			printf("%d ", *(array++));
		}
		printf("\n");
	}
}
print_array_2D(&array3[0][0], row, col);
         指针不愧是非常凶悍的东西。

猜你喜欢

转载自blog.csdn.net/yvken_zh/article/details/80167505