C函数传递二维数组

其实这个是很早之前写的,一直没有整理,其实是舍友写数据结构实验问的我,我帮他写了个说明。

为什么不能使用指向指针的指针?

  • 如果把二维数组强转指向指针的指针,在解引用时候会直接取当前指向的内存的值。下面结合示例程序解释一下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
using namespace std;
void test(int (*p)[3])
{
cout << p << " " << p + 1 << endl;
cout << *(int**)p << " " << *((int**)p + 1) << endl;
cout << *((int**)p + 2) << " " << *((int**)p + 3) << endl;
}
int main()
{
int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
char *p = (char *)a;
for(unsigned i = 0; i < 24; ++i)
printf("%d", *p++);
cout << endl;
test(a);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
编译器版本
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/local/libexec/gcc/x86_64-pc-linux-gnu/6.1.0/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: ../gcc-6.1.0/configure --enable-checking=release --enable-languages=c,c++ --disable-multilib
Thread model: posix
gcc version 6.1.0 (GCC)
结果
100020003000400050006000
0x7ffc5766f020 0x7ffc5766f02c
0x200000001 0x400000003
0x600000005 0x4007a0
  • 第一行是我把内存中的内容按字节输出了(一个数字是8bit)。由于x86是小端序(存储时地址从低到高),所以内存中应该是
    1
    2
    3
    4
    a[0]
    0x00000001 0x00000002 0x00000003
    a[1]
    0x00000004 0x00000005 0x00000006

恰好每个整数4Bytes

  • 然而在转成指向指针的指针后,看最后两行的输出。由于是指向指针的指针,自然也是指针,解引用时候直接按指针大小(8Bytes),取出来就是
    0x0000000200000001 0x0000000400000003 0x0000000600000005
    由于0不输出,所以最后输出为后两行
    0x200000001 0x400000003 0x600000005
    所以如果这样
    1
    2
    3
    4
    5
    6
    int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
    int **p = (int **)a;
    a[0][0]//相当于取0x200000001指向的值(未定义行为)
    a[0][1]//相当于取0x200000005指向的值(未定义行为)
    (因为此时a[0]相当于指向int的指针,加1相当于地址加4(int大小为4Bytes))
    a[1][0]//相当于取0x400000003指向的值(未定义行为)

那一定要传入多维数组怎么办呢?

  • 用上面示例程序方法void test(int (*p)[3]),给出除第一维外所有维度大小

  • 如下强制类型转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
using namespace std;
void test(int **p, unsigned m, unsigned n)
{
int (*p1)[n] = (int(*)[n])p;
for (unsigned i = 0; i < m; ++i)
for (unsigned j = 0; j < n; ++j)
cout << p1[i][j] << endl;
}
int main()
{
int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
test((int **)a, 2, 3);
}
  • 用指针
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
using namespace std;
void test(int *p, unsigned m, unsigned n)
{
for (unsigned i = 0; i < m; ++i)
for (unsigned j = 0; j < n; ++j)
cout << p[i * n + j] << endl;
}
int main()
{
int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
test((int *)a, 2, 3);
}
  • 用指向二维数组的指针
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
using namespace std;
void test(int (*p)[2][3], unsigned m, unsigned n)
{
auto p1 = *p;
int (*p2)[3] = *p;
for (unsigned i = 0; i < m; ++i)
for (unsigned j = 0; j < n; ++j)
cout << p1[i][j] << endl;
for (unsigned i = 0; i < m; ++i)
for (unsigned j = 0; j < n; ++j)
cout << p2[i][j] << endl;
}
int main()
{
int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
test(&a, 2, 3);
}