二维数组 二级指针 指针数组 数组指针 的区别和联系

注:为了方便解释,二维数组都按2行3列计算;下面的例子都是亲测有效

一、写法及含义

1、int a[2][3] = { {2,4,6}, {20,40,60} };:定义了一个行为2,列为3的二维数组,所以sizeof(c)为3*2*4=24字节

2、int(*b)[3];:数组指针,指向列有3个的二维数组,因为是指针所以sizeof(b)为4字节(一个指针的大小)

3、int*c[2];:指针数组,是一个数组,里面是两个int*类型的指针,所以sizeof(c)为2*4=8字节

4、int** d;:二级指针,可以指向二维数组,因为是指针所以sizeof(b)为4字节(一个指针的大小)

其中前两个int a[2][3]和int(*b)[3]意思相近,一般都是b = a;把分配好内存的a给b指针,内存存储是连续的,跟内存分配一起理解

int a [2][3] = { {2,4,6}, {20,40,60} };
int(*b)[3];
b=a;
for (int i = 0; i < 2; i++) {
	for(int j = 0; j < 3; j++) {
		printf("b[%d][%d]=%d\r\n", i, j, *(*(b + i) + j));
	}
}

其中后两个int*c[2];和int** d;意思相近,内存存储方式一样(内存可能是非连续的),跟内存分配一起理解

int p[3] = { 2,4,6 };
int q[3] = { 1,3,5 };
int* c[2] = { p, q };
int** d = NULL;
d = c;	// c可能是非连续的
for (int i = 0; i < 2; i++) {
	for(int j = 0; j < 3; j++) {
		printf("d[%d][%d]=%d\r\n", i, j, *(d[i] + j));
	}
}

二、内存分配

1、int a[2][3] = { {2,4,6}, {20,40,60} };:内存已经分配好,而且内存连续;

2、int(*b)[3];:数组指针

// i代表行,j代表列
b = (int(*)[3])malloc(sizeof(int) * 3 * 2);	// 内存分配是连续的
for (int i = 0; i < 2; i++) {
	for (int j = 0; j < 3; j++) {
		b[i][j] = i + j * 2;	// 赋初值
	}
}

3、 int*c[2];:指针数组,里面的指针内存是连续的,但是指针指向的数组,也就是每行之间的内存可能是不连续的

// i代表行,j代表列
for (int i = 0; i < 2; i++) {
	c[i] = (int*)malloc(sizeof(int) * 3);	// 为每个数组里面的指针分配内存,这就可能造成每行指针之间的内存是不连续的
	for (int j = 0; j < 3; j++) {
		c[i][j] = i + j * 2;	// 赋初值
	}
}

4、int** d;:二级指针

// i代表行,j代表列
d = (int**)malloc(sizeof(int*) * 2);
// 下面和指针数组是一样的,也就是在int** d分配了两个指针指向数组的指针之后和指针数组就是一样的了
for (int i = 0; i < 2; i++) {
	d[i] = (int*)malloc(sizeof(int) * 3);
	for (int j = 0; j < 3; j++) {
		d[i][j] = i + j * 2;	// 赋初值
	}
}

三、函数调用的形参和实参

我们需要注意以下几点:

1、内存存储方式不同的不要互为形参和实参,比如上面说的连续存储内存和非连续存储内存,例int a[2][3]不要和int** d互为形参和实参

2、以下是比较常用的实参和形参搭配方式

实参      

所匹配的形参

数组的数组char c[8][10];   

数组指针char (*)[10];

指针数组char *c[10];  

指针的指针char **c;     

指针数组char *c[10];  

不改变char (*c)[10];

指针的指针char **c;  

不改变char **c;

3、函数返回只能是二级指针

4、以下是我自己实验所的,下面在函数里面打印结果如果是对的,那么用相同的方法就可以更改二维数组里面的值

说明:下面例子里面的打印都是打印的第一行第一列的数据

当实参是非连续内存时:

  • int array[][3]和int(*array)[3]作为形参不能使用;
  • int** array和int *array[2]全能访问;
#include <stdio.h>
#include <stdlib.h>
/* 下面没有说明的就是打印正确的 */
int** array_two_1(int** array, int n) {	
	printf("array[1][1]-1=%d\r\n", *((int*)array + 1 * n + 1));  // 错误,内存不连续的不能用这个
	printf("array[1][1]-1=%d\r\n", *(array[1] + 1));// 正确,一般用这个
	printf("array[1][1]-1=%d\r\n", array[1][1]); //正确,这里的a[1][1]和二维数组的a[1]a[1]是不一样的,和*(*(array + 1) + 1))意思相同
	printf("array[1][1]-1=%d\r\n", *(*(array + 1) + 1)); //正确
	return array;
}
int** array_two_2(int array[][3]) {	// 和int**不匹配,无法打印
	printf("array[1][1]-2=%d\r\n", array[1][1]);
	printf("array[1][1]-2=%d\r\n", *(*(array + 1) + 1));
	printf("array[1][1]-2=%d\r\n", *(array[1] + 1));
	printf("array[1][1]-2=%d\r\n", *((int*)array + 1 * 3 + 1));
	return (int**)array;
}
int** array_two_3(int(*array)[3]) {	// 和int**不匹配,以下打印全部错误
	printf("array[1][1]-3=%d\r\n", array[1][1]);	
	printf("array[1][1]-3=%d\r\n", *(*(array + 1) + 1));
	printf("array[1][1]-3=%d\r\n", *(array[1] + 1));
	printf("array[1][1]-3=%d\r\n", *((int*)array + 1 * 3 + 1));
	return (int**)array;
}
int** array_two_4(int* array[2]) {
	printf("array[1][1]-4=%d\r\n", *(array[1]+1));  // 正确
	printf("array[1][1]-4=%d\r\n", *((int*)array + 1 * 3 + 1));// 错误,非连续内存不能用这个
	printf("array[1][1]-4=%d\r\n", array[1][1]); // 正确
	printf("array[1][1]-4=%d\r\n", *(*(array + 1) + 1)); // 正确
	return array;
}

int main() {
    int a[3] = { 1,3,5 };
    int c[3] = { 2,4,6 };
    int* d[2] = { a, c };
    int** g = NULL; // 可能为非连续内存,分配完之后{{0, 2, 4}, {1, 3, 5}}
    g = (int**)malloc(sizeof(int*)*2);
    for (int i = 0; i < 2; i++) {
        g[i] = (int*)malloc(sizeof(int)*3);
        for (int j = 0; j < 3; j++) {
            g[i][j] = i+j*2;
            int t = 9;
        }
    }
    int** res1 = NULL;
    int** res2 = NULL;
    int(*res3)[3] = NULL;	
    int** res4 = NULL;
    
    res1 = array_two_1((int**)g, 3);	// res1 = array_two_1(b, 3); 错误的
    //res2 = array_two_2(g);	// 编译错误用不了
    res3 = (int(*)[3])array_two_3((int(*)[3])g);
    res4 = array_two_4(d);
    return 0;
}

当实参是连续内存时:

  • int** array和int *array[2]只有*((int*)array + 1 * n + 1)能访问;
  • int array[][3]和int (*array)[3]全能访问
#include <stdio.h>
#include <stdlib.h>

int** array_two_1(int** array,int n) {	// 形参没有建立二维数组,所以不能array[1][1]和 *(*(array+1)+1)访问
	printf("array[1][1]-1=%d\r\n", *((int*)array + 1 * n + 1));  // 正确的
	//printf("array[1][1]-1=%d\r\n", *(array[1] + 1));	// 错误
	//printf("array[1][1]-1=%d\r\n", array[1][1]); // 错误的 
	//printf("array[1][1]-1=%d\r\n", *(*(array + 1) + 1)); // 错误的
	//*((int*)array + 1 * n + 1) = 30;	// 正确
	return array;
}
int** array_two_2(int array[][3]) {	// 符合理论
	printf("array[1][1]-2=%d\r\n", array[1][1]);	// 正确,一般写法
	printf("array[1][1]-2=%d\r\n", *(*(array+1)+1)); // 正确
	printf("array[1][1]-2=%d\r\n", *(array[1] + 1)); // 正确
	printf("array[1][1]-2=%d\r\n", *((int*)array + 1 * 3 + 1)); // 正确
	//array[1][1] = 35;	// 正确
	//*(*(array + 1) + 1) = 35;	// 正确
	return (int**)array;
}
int** array_two_3(int (*array)[3]) {	// 符合理论
	printf("array[1][1]-3=%d\r\n", array[1][1]); // 正确
	printf("array[1][1]-3=%d\r\n", *(*(array + 1) + 1));	// 正确,一般写法
	printf("array[1][1]-3=%d\r\n", *(array[1] + 1));  // 正确
	printf("array[1][1]-3=%d\r\n", *((int*)array + 1 * 3 + 1));  // 正确
	//array[1][1] = 36;		// 正确
	return (int**)array;
}

int** array_two_4(int *array[2]) {	// 和int**是一样的,只有*((int*)array + 1 * 3 + 1)能用
	//printf("array[1][1]-3=%d\r\n", array[1][1]);	// 错误
	//printf("array[1][1]-3=%d\r\n", *(*(array + 1) + 1));	// 错误
	//printf("array[1][1]-3=%d\r\n", *(array[1] + 1));	// 错误
	printf("array[1][1]-3=%d\r\n", *((int*)array + 1 * 3 + 1));	// 正确
	//*((int*)array + 1 * 3 + 1) = 37;	// 正确
	return array;
}

int main()
{
	int b[2][3] = { {2,4,6}, {20,40,60} };
	int **res1 = NULL;
	int **res2 = NULL;	//int res2[2][3]; 错误
	int(*res3)[3] = NULL;
	int** res4 = NULL;

	res1 = array_two_1((int**)b, 3);// res1 = array_two_1(b, 3); 错误的
	res2 = array_two_2(b);
	res3 = (int(*)[3])array_two_3(b);
	res4 = array_two_4((int**)b);
	return 0;
}

四、打印方式

说明:这里都按打印第一行第一列说明,行列都是从0开始

注意:前三个适用于连续内存和非连续内存的打印,第四个只适用于连续内存打印

1、a[1][1]:一般用于int a[2][3],这个是按照下标索引读取的

2、*(*(b + 1) + 1):一般用于数组指针int(*b)[3];

  • b为指向列有3个的二维数组的指针;
  • b+1指针就指向了第一行首地址的地址,*(b+1)指向了第一行的首地址;
  • *(b+1)+1指向了第一行第一列的地址;*(*(b+1)+1):取到了里面的内容;

3、*(c[1] + 1):一般用于指针数组,如果int** d也指向了指针数组,那么也用于int** d打印;

  • c[1]为第一行的首地址;
  • c[1]+1:指向了第一行第一列的地址;
  • *(c[1]+1)取到了里面的内容;

4、*((int*)d + 1 * 3 + 1):

  • (int*)d:指向int类型的指针,这里加1就是往前走了4个字节;
  • 后面加上1 * 3 + 1,就到了第一行第一列的地址;
  • 再*就取到了第一行第一列里面的内容;
  • 注意这个不适用于非连续的内存如int*c[2]和int** d

附录一、打印的测试程序

int main()
{
	int a[3] = { 2,4,6 };
	int c[3] = { 1,3,5 };
	int* d[2] = { a, c };
	int b[2][3] = { {2,4,6}, {20,40,60} };
	int** e = NULL;
	int(*h)[3];
	int(*k)[3];	// 连续内存,分配完之后{{0, 2, 4}, {1, 3, 5}}
	k = (int(*)[3])malloc(sizeof(int) * 3 * 2);
	for (int i = 0; i < 2; i++) {
		for (int j = 0; j < 3; j++) {
			k[i][j] = i + j * 2;
		}
	}
	int** g = NULL;	// 可能为非连续内存,分配完之后{{0, 2, 4}, {1, 3, 5}}
	g = (int**)malloc(sizeof(int*) * 2);
	for (int i = 0; i < 2; i++) {
		g[i] = (int*)malloc(sizeof(int) * 3);
		for (int j = 0; j < 3; j++) {
			g[i][j] = i + j * 2;
		}
	}
	e = d;	// d可能是非连续的
	h = b;	// b是连续的
	/* 下面除了不正确的,其他打印的都是正确的,打印正确都是3*/
	printf("k = %d\r\n", k[1][1]);	
	printf("k = %d\r\n", *(*(k + 1) + 1));	// 一般这么访问
	printf("k = %d\r\n", *(k[1] + 1));
	printf("k = %d\r\n", *((int*)k + 1 * 3 + 1));

	printf("g = %d\r\n", g[1][1]);	
	printf("g = %d\r\n", *(*(g + 1) + 1));
	printf("g = %d\r\n", *(g[1] + 1));	// 一般这么访问
	printf("g = %d\r\n", *((int*)g + 1 * 3 + 1));	// 不正确,因为g是非连续的

	printf("b = %d\r\n", b[1][1]);	// 一般这么访问
	printf("b = %d\r\n", *(*(b + 1) + 1));
	printf("b = %d\r\n", *(b[1] + 1));
	printf("b = %d\r\n", *((int*)b + 1 * 3 + 1));

	printf("h = %d\r\n", h[1][1]);
	printf("h = %d\r\n", *(*(h + 1) + 1));	// 一般这么访问
	printf("h = %d\r\n", *(h[1] + 1));
	printf("h = %d\r\n", *((int*)h + 1 * 3 + 1));

	printf("e = %d\r\n", e[1][1]);
	printf("e = %d\r\n", *(*(e + 1) + 1));	
	printf("e = %d\r\n", *(e[1] + 1));	// 一般这么访问
	printf("e = %d\r\n", *((int*)e + 1 * 3 + 1));	// 不正确,因为e是非连续的,打印结果为1,应该为3

	printf("d = %d\r\n", d[1][1]);
	printf("d = %d\r\n", *(*(d + 1) + 1));
	printf("d = %d\r\n", *(d[1] + 1));	// 一般这么访问
	printf("d = %d\r\n", *((int*)d + 1 * 3 + 1));   // 不正确,因为d是非连续的,打印结果为1,应该为3

	return 0;
}

【创作不易,如果觉得有用,请多多转发关注,有什么意见也请大家留言!】

 

  • 23
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值