最近,前同事发问:“一个函数怎么返回二维数组指针,外层应用怎么接收这个指针才能以arr[][]这种形式去访问数组元素。”之前也没处理过类似的案例,误以为二维数组名与二级指针可以等价转换,实操后发现自己大错特错。
先下结论:二维数组名不等价于二级指针!!!
1. 动态分配的二维数组
该数组的数组名是二级指针,以整型为例,其类型是int**,指向于一级指针int*。arr[]的地址步进大小是int*的大小。(该二维数组内存不一定连续)以32位系统为例,如下图。
当int**定义的变量arr获取到动态分配的二维数组的数组名后,可以用arr[][]形式访问二维数组元素。
2. 静态定义的二维数组
该数组的数组名是数组指针,以整型为例,其类型是int(*)[],指向一个整型的一维数组首地址,arr[]的一个地址步进是该一维整型数组的长度。(该二维数组内存连续)
当int**定义的变量arr获取到静态定义的二维数组的数组名后,此时不能以arr[][]的形式访问。在32位系统中,假设地址0x8872de70和0x8872de74中存储的值都是2,a[0]此时是一级指针,就会指向0x00020002这个未知地址。然后访问a[0][0]时可能就会出现隐藏的未知错误或者死机,类似于下图64位系统的状态(二级指针已经获取到二维数组首地址,但是最终一级指针指向的地址错误)。
当然,二级指针也可以用来访问静态定义的二维数组元素,找到了数组首地址后,需要通过地址偏移去寻址的方法,而不是用arr[][]去索引数组。
3. 如何访问静态定义的二维数组呢?
可以通过定义数组指针int(*arr)[]的方式去获取二维数组名,访问静态定义的二维数组。
此时个人采用两种方法(具体实现见测试例程):
- 通过函数返回二维数组名获取数组指针。
- 通过函数入参获取数组指针。
typedef uint16_t U16;
typedef uint16_t* PU16;
typedef uint16_t** PPU16;
U16 g_array1_ui16[FIRSTNUM][SECONDNUM] = { {3,3},{3,3} };
U16 g_array2_ui16[FIRSTNUM][SECONDNUM] = { {4,4},{4,4} };
//返回数组指针
U16 (*g_Get2DArrayType2_vd(void))[SECONDNUM]
{
return g_array2_ui16;
}
//数组指针入参赋值
static void g_Get2DArrayType1_vd(U16 (**parray)[SECONDNUM])
{
*parray = g_array1_ui16;
}
也可以通过typedef使得定义更加简单易懂。
typedef U16 (*P_ARRAY)[SECONDNUM];
//返回数组指针
//U16 (*g_Get2DArrayType2_vd(void))[SECONDNUM]
//{
// return g_array2_ui16;
//}
P_ARRAY g_Get2DArrayType2_vd(void)
{
return g_array2_ui16;
}
//数组指针入参赋值
//static void g_Get2DArrayType1_vd(U16(**parray)[SECONDNUM])
//{
// *parray = g_array1_ui16;
//}
static void g_Get2DArrayType1_vd(P_ARRAY *parray)
{
*parray = g_array1_ui16;
}
4. 测试例程
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
/* -------------------- 宏定义 ------------------------*/
#define FIRSTNUM (2u)
#define SECONDNUM (2u)
/* -------------------- 类型定义 ------------------------*/
typedef uint16_t U16;
typedef uint16_t* PU16;
typedef uint16_t** PPU16;
typedef U16 (*P_ARRAY)[SECONDNUM];
typedef enum
{
TEST_DEFAULT_ENUM = 0,
TEST_INIT_U16_ARRAYBUF_ENUM, //二维数组初始化赋值
TEST_DYNALLOCATE_U16_PPBUF_ENUM, //为二级指针指向的空间分配内存
TEST_ARRAY_POINTER_TYPE1_ENUM, //数组指针入参赋值
TEST_ARRAY_POINTER_TYPE2_ENUM, //数组指针返回赋值
TEST_MAX_ENUM
}TEST_TYPE;
/* -------------------- 变量定义 ------------------------*/
U16 g_array1_ui16[FIRSTNUM][SECONDNUM] = { {3,3},{3,3} };
U16 g_array2_ui16[FIRSTNUM][SECONDNUM] = { {4,4},{4,4} };
PPU16 g_ppoint_ppui16 = NULL;
/* -------------------- api定义 ------------------------*/
//返回(二级指针强转)二维数组名
PPU16 g_GetStaU16BufPoint_ppui16(void)
{
return (PPU16)g_array1_ui16;
}
//返回二级指针
PPU16 g_GetDynU16BufPoint_ppui16(void)
{
return g_ppoint_ppui16;
}
//返回数组指针
//U16 (*g_Get2DArrayType2_vd(void))[SECONDNUM]
//{
// return g_array2_ui16;
//}
P_ARRAY g_Get2DArrayType2_vd(void)
{
return g_array2_ui16;
}
//数组指针入参赋值
//static void g_Get2DArrayType1_vd(U16 (**parray)[SECONDNUM])
//{
// *parray = g_array1_ui16;
//}
static void g_Get2DArrayType1_vd(P_ARRAY *parray)
{
*parray = g_array1_ui16;
}
//为二级指针和一级指针指向的空间动态分配内存
static void g_DynAllocatePPBuf_vd(void)
{
g_ppoint_ppui16 = (PPU16)malloc(FIRSTNUM * sizeof(PU16));
for (uint8_t i = 0; i < FIRSTNUM; i++)
{
g_ppoint_ppui16[i] = (PU16)malloc(SECONDNUM * sizeof(U16));
for (uint8_t j = 0; j < SECONDNUM; j++)
{
g_ppoint_ppui16[i][j] = i * FIRSTNUM + j;
}
}
}
//测试接口
static void g_2LPointTest_vd(TEST_TYPE f_testtype_enum)
{
PPU16 l_ppoint_ppui16 = NULL;
U16 (*l_arrpoint)[SECONDNUM] = { NULL };
switch (f_testtype_enum)
{
case TEST_INIT_U16_ARRAYBUF_ENUM:
{
printf("TEST_INIT_U16_ARRAYBUF\r\n");
l_ppoint_ppui16 = g_GetStaU16BufPoint_ppui16();
}
break;
case TEST_DYNALLOCATE_U16_PPBUF_ENUM:
{
printf("TEST_DYNALLOCATE_U16_PPBUF\r\n");
g_DynAllocatePPBuf_vd();
l_ppoint_ppui16 = g_GetDynU16BufPoint_ppui16();
}
break;
case TEST_ARRAY_POINTER_TYPE1_ENUM:
{
printf("TEST_ARRAY_POINTER_TYPE1\r\n");
g_Get2DArrayType1_vd(&l_arrpoint);
}
break;
case TEST_ARRAY_POINTER_TYPE2_ENUM:
{
printf("TEST_ARRAY_POINTER_TYPE2\r\n");
l_arrpoint = g_Get2DArrayType2_vd();
}
break;
default:break;
}
for (uint8_t i = 0; i < FIRSTNUM; i++)
{
for (uint8_t j = 0; j < SECONDNUM; j++)
{
if (l_ppoint_ppui16 != NULL)
{
printf("l_ppoint_ppui16[%d][%d] = %d\r\n", i, j, l_ppoint_ppui16[i][j]);
}
if (l_arrpoint != NULL)
{
printf("l_arrpoint[%d][%d] = %d\r\n", i, j, l_arrpoint[i][j]);
}
}
}
}
//主函数
int main()
{
g_2LPointTest_vd(TEST_DYNALLOCATE_U16_PPBUF_ENUM); //[OK]动态分配二维数组返回(强转二级指针)数组名----二级指针uint16**a 调用a[][]
//g_2LPointTest_vd(TEST_INIT_U16_ARRAYBUF_ENUM); //[ERR]静态定义二维数组返回数组名----二级指针uint16**a 调用a[][]
g_2LPointTest_vd(TEST_ARRAY_POINTER_TYPE1_ENUM); //[OK]静态定义二维数组返回数组名----数组指针uint16 (*a)[] 调用a[][]
g_2LPointTest_vd(TEST_ARRAY_POINTER_TYPE2_ENUM); //[OK]静态定义二维数组返回数组名----数组指针uint16 (*a)[] 调用a[][]
return 0;
}