注:博客中内容主要来自《狄泰软件学院》,博客仅当私人笔记使用。
测试环境:Ubuntu 10.10
GCC版本:4.4.5
一、思考
为什么C语言中的数组参数会退化为指针?
二、退化的意义
1) C语言中只会以值拷贝的方式传递参数
2) 当向函数传递数组时:
- 将整个数组拷贝一份传入函数(X错,这样操作效率会大大降低)
- 将数组名看做常量指针传数组首元素地址
C语言以高效为最初设计目标(数组参数退化的目的):
a) 参数传递的时候如果拷贝整个数组执行效率将大大下降
b) 参数位于栈上,太大的数组拷贝将导致栈溢出
三、二维数组参数
1) 二维数组参数同样存在退化的问题
- 二维数组可以看做是一维数组
- 二维数组中的每个元素是一维数组
2) 二维数组参数中第一维的参数可以省略(函数形参使用注意)
- void f(int a[5]) <-->void f(int a[]) <-->void f(int* a) 指针
- void g(int a[3][3]) <-->void g(int a[][3]) <-->void g(int (*a)[3]) 数组指针
四、等价关系
五、被忽视的知识点
1) C语言中无法向一个函数传递任意的多维数组
2) 必须提供除第一维之外的所有维长度
- 第一维之外的维度信息用于完成指针运算
- N维数组的本质是一维数组,元素是N-1维的数组
- 对于多维数组的函数参数只有第一维是可变的
实例分析
传递与访问二维数组
35-1.c
#include <stdio.h>
/*
显示二维数组信息,数组类型int (*)[3]
功能:
用sizeof()计算指针类型大小,为了确认数组是否退化为指针
用sizeof()计算数组大小,一维数组是a,二维数组应该是*a
由于一维数组会退化,丢掉索引值,因此需要一个参数记录丢失的索引值
遍历数组,显示结果
*/
void access(int a[][3], int row) //打印二维数组信息
{
int col = sizeof(*a) / sizeof(int); //计算有多少列:3列
int i = 0;
int j = 0;
//知识点:a所占用的内存空间:3*4,因为a退化为指针类型
printf("sizeof(a) = %d\n", sizeof(a));
//一维数组:a的数组元素所占用的内存空间:12,*a指向了一维数组
printf("sizeof(*a) = %d\n", sizeof(*a));
for(i=0; i<row; i++) //行
{
for(j=0; j<col; j++) //列
{
printf("a[%d][%d] = %d\n", i, j, a[i][j]); //遍历二维数组
}
}
printf("\n");
}
void access_ex(int b[][2][3], int n) //打印三维数组信息
{
int i = 0;
int j = 0;
int k = 0;
/*
1、确认数组退化为指针
2、打印数组大小
*/
//打印4,b退化为指针类型,指针大小都为4
printf("sizeof(b) = %d\n", sizeof(b));
//int (*b)[2][3] = 大小2*3*4=24,退化为指针,指向二维数组
printf("sizeof(*b) = %d\n", sizeof(*b));
//打印数组大小,一维数组是a,二维数组应该*a(二维指针)
//遍历数组
for(i=0; i<n; i++) //一维
{
for(j=0; j<2; j++) //二维
{
for(k=0; k<3; k++) //三维
{
printf("b[%d][%d][%d] = %d\n", i, j, k, b[i][j][k]);
}
}
}
printf("\n");
}
int main()
{
int a[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};
int aa[2][2] = {0};
int b[1][2][3] = {0};
access(a, 3);
access(aa, 2); //传递参数不兼容,显示结果会出现随机值
access_ex(b, 1);
access_ex(aa, 2);
return 0;
}
操作:
1) main函数:
int main()
{
int a[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};
int aa[2][2] = {0};
int b[1][2][3] = {0};
access(a, 3);
//access(aa, 2); //传递参数不兼容,显示结果会出现随机值
//access_ex(b, 1);
//access_ex(aa, 2);
return 0;
}
gcc 35-1.c -o 35-1.out编译正确,打印结果:
sizeof(a) = 4
sizeof(*a) = 12
a[0][0] = 0
a[0][1] = 1
a[0][2] = 2
a[1][0] = 3
a[1][1] = 4
a[1][2] = 5
a[2][0] = 6
a[2][1] = 7
a[2][2] = 8
2) main函数:
int main()
{
int a[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};
int aa[2][2] = {0};
int b[1][2][3] = {0};
access(a, 3);
access(aa, 2); //传递参数不兼容,显示结果会出现随机值
//access_ex(b, 1);
//access_ex(aa, 2);
return 0;
}
gcc 35-1.c -o 35-1.out编译有警告:
35-1.c:32:2: warning: passing argument 1 of ‘access’ from incompatible pointer type [enabled by default]
access(aa, 3);
^
警告:编译函数access到参数1时,指针类型不兼容
35-1.c:3:6: note: expected ‘int (*)[3]’ but argument is of type ‘int (*)[2]’
void access(int a[][3], int row)
^
提示:期望的'int (*)[3]',但是参数是'int (*)[2]'
打印结果:
sizeof(a) = 4
sizeof(*a) = 12
a[0][0] = 0
a[0][1] = 1
a[0][2] = 2
a[1][0] = 3
a[1][1] = 4
a[1][2] = 5
a[2][0] = 6
a[2][1] = 7
a[2][2] = 8
sizeof(a) = 4 //因为函数参数为int a[][3]
sizeof(*a) = 12
a[0][0] = 0
a[0][1] = 0
a[0][2] = 0
a[1][0] = 0
a[1][1] = 0
a[1][2] = 0
3) main函数:
int main()
{
int a[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};
int aa[2][2] = {0};
int b[1][2][3] = {0};
access(a, 3);
//access(aa, 2); //传递参数不兼容,显示结果会出现随机值
access_ex(b, 1);
//access_ex(aa, 2);
return 0;
}
打印结果:
sizeof(a) = 4
sizeof(*a) = 12
a[0][0] = 0
a[0][1] = 1
a[0][2] = 2
a[1][0] = 3
a[1][1] = 4
a[1][2] = 5
a[2][0] = 6
a[2][1] = 7
a[2][2] = 8
sizeof(b) = 4
sizeof(*b) = 24
b[0][0][0] = 0
b[0][0][1] = 0
b[0][0][2] = 0
b[0][1][0] = 0
b[0][1][1] = 0
b[0][1][2] = 0
4) main函数
int main()
{
int a[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};
int aa[2][2] = {0};
int b[1][2][3] = {0};
access(a, 3);
//access(aa, 2); //传递参数不兼容,显示结果会出现随机值
access_ex(b, 1);
access_ex(aa, 2);
return 0;
}
编译有警告:
35-1.c:56:2: warning: passing argument 1 of ‘access_ex’ from incompatible pointer type [enabled by default]
access_ex(aa, 2);
^
警告:编译到函数access_ex第一个参数时,指针类型不匹配
35-1.c:24:6: note: expected ‘int (*)[2][3]’ but argument is of type ‘int (*)[2]’
void access_ex(int b[][2][3], int n)
^
提示:期望'int (*)[2][3]'但是参数是'int (*)[2]'
打印结果:
sizeof(a) = 4
sizeof(*a) = 12
a[0][0] = 0
a[0][1] = 1
a[0][2] = 2
a[1][0] = 3
a[1][1] = 4
a[1][2] = 5
a[2][0] = 6
a[2][1] = 7
a[2][2] = 8
sizeof(b) = 4
sizeof(*b) = 24
b[0][0][0] = 0
b[0][0][1] = 0
b[0][0][2] = 0
b[0][1][0] = 0
b[0][1][1] = 0
b[0][1][2] = 0
sizeof(b) = 4
sizeof(*b) = 24
b[0][0][0] = 0
b[0][0][1] = 0
b[0][0][2] = 0
b[0][1][0] = 0
b[0][1][1] = 1309061
b[0][1][2] = 7498624
b[1][0][0] = 134514331
b[1][0][1] = 2527220
b[1][0][2] = 134514320
b[1][1][0] = 0
b[1][1][1] = -1075465608
b[1][1][2] = 1207527
分析:
虽然能打印出数据,但是出现垃圾数据。
小结:
1) C语言中只会以值拷贝的方式传递参数
2) C语言中的数组参数必然退化为指针
3) 多维数组参数必须提供除第一维之外的所有维长度
4) 对于多维数组的函数参数只有一维是可变的