一、一维数组
1、一维数组的数组名的值是这个数组第一个元素的地址,它是一个指针常量(只有当数组名在表达式中使用时,编译器才会为它产生一个指针常量,而当数组名作为sizeof操作符或者单目操作符&的操作数时,数组名并不用指针常量来表示)。
int arr[10] = {0};
int *p0 = arr;
int *p1 = arr + 1;
int *p2 = &arr[0];
printf("%ld\n", sizeof(p0));
printf("%ld\n", sizeof(&arr[0]));
printf("%ld\n", sizeof(p2));
printf("%ld\n", sizeof(arr)); //数组名非指针常量
int (*p3)[10] = &arr; //数组名非指针常量
//打印结果:
//8
//8
//8
//40
注意,尽管p0、p2、&arr[0]和arr的值相等(都是数组第一个元素的地址),但前三个printf的打印结果与第四个不同,前三个打印结果都是指针的大小,第四个是数组的大小(所占字节数)。此外sizeof(p3)的值也是指针大小,因为它们都是指针变量。
2、在1中,p0、p2和p3的值是相等的,即arr、&arr[0]和&arr的值都是数组首地址,但是arr+1、&arr[0]+1和&arr+1却不完全相等,arr+1和&arr[0]+1都是数组第二个元素的地址,但&arr+1是数组最后一个元素的地址往后偏移1,即这里等于&arr[10]+1。
3、在1中&arr返回的是数组arr的指针,即数组指针。一维数组的数组指针定义方法为:
类型 (*指针变量名)[SIZE] = &数组名
而下面这种写法是非法的:
int *p3 = &arr;
但一般编译器只会给出警告:warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
且此时p3变成了指向数组第一个元素的指针,跟p0一样了。
4、数组作为函数参数进行传递时,有以下两种情景:
情景1
void func(int *p)
{
//
}
int main()
{
int arr[10] = {0};
func(arr);
return 0;
}
情景2
void func(int a[])
{
//
}
int main()
{
int arr[10] = {0};
func(arr);
return 0;
}
情景1和情景2是等效且合法的,即下面两个函数的原型是相等的:
void func(int *p);
void func(int a[]);
将一个数组传入时,实际传递的仅仅是这个数组的首地址,而不是整个数组。所以在函数中无法通过sizeof关键字来获取数组长度的,sizeof(p)和sizeof(a)返回的都是指针长度,且sizeof(a)在编译时会给出警告:
warning: ‘sizeof’ on array function parameter ‘a’ will return size of ‘int *’ [-Wsizeof-array-argument]
正因如此,形参为数组时,是可以匹配任意长度的实参数组的(因为传递的只是只想数组第一个元素的指针)。此外,数组作为参数进行传递时,通常会将该数组的长度一同传进来。
二、二维数组
1、二维数组的数组名的值也是它第一个元素的地址,但它第一个元素也是一个数组,所以二维数组的数组名是一个指向数组的指针,但在数值上仍然是第一个元素的地址,即下面三个printf的结果是相同的。
int arr[3][5] = {0};
printf("&a[0][0]=%p\n", &a[0][0]);
printf("a[0]=%p\n", a[0]);
printf("a=%p\n", a);
//打印结果
//&arr[0][0]=0x7fff9227b4a0
//arr[0]=0x7fff9227b4a0
//arr=0x7fff9227b4a0
假设arr[3][5]是一个3行5列的矩阵,那么arr[0]+1和&arr[0][0]+1都是第一行第二列的地址,但arr+1是第二行第一列的地址,即偏移量为5*sizeof(int)
将二维数组的数组名赋值给一个指针变量时,写法如下:
int (*p)[5] = arr;
2、二维数组作为函数参数进行传递时和一维数组相似,实际传递的也是指向数组第一个元素的指针,但不同的是,二维或多维数组的每个元素是另外一个数组,编译器需要知道它的维数,以便为函数形参的下标表达式进行求值。下面是二维数组名作为函数参数的使用方式:
void func1(int p[][5])
{
//
}
void func2(int *p[5])
{
//
}
int main()
{
int arr[3][5] = {0};
func1(arr);
func2(arr);
return 0;
}
上面两种函数原型都是合法的。
这里的关键在于编译器必须知道第二个及以后的各维长度才能对各下标进行求值,因此在原型中,必须声明这些维的长度。第一维的长度并不需要,因为在计算下标时用不到它。在编写一维数组的形参的函数原型时,你既可以把它写成数组的形式,也可以把它写成指针的形式。但是对于多维数组,只有第一维可以进行如此选择。尤其是下面这种原型是不正确的:
void func3(int **p);