注:为了方便解释,二维数组都按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;
}
【创作不易,如果觉得有用,请多多转发关注,有什么意见也请大家留言!】