一、指针数组
指针数组本质上是一个数组,是一个指针类型的数组。
下面定义了一个int类型的指针数组类型:
typedef int *PtrArray[4];
“ [ ] ”优先级高于 “ * ”,PtrArray先与“ [ ] ”结合,表明这是一个数组,再和外面的“ int * ”结合,表明这个数组中的元素为int类型的指针。也就是PtrArray是一个数组,大小为4,数组里面存的是4个地址。
我们就可以用这个自定义的类型来定义变量,并通过数组访问的方式访问数组中的每一个int类型的指针,得到指针后便可以通过解引用的方式访问地址中存的int类型的数据。
int main(void)
{
int i = 0;
int a[4] = {1, 2 ,3, 4};
PtrArray ptr_array;
for(i = 0; i < 4; i++)
{
ptr_array[i] = &a[i];
printf("*ptr_array[%d] = %d\n", i, *ptr_array[i]);
}
return 0;
}
linux下gcc编译执行结果如下:
二、数组指针
数组指针本质上是一个指针,这个指针指向一个数组类型,可以认为这个指针就是数组的地址,在数值上等于数组首元素的首地址,但意义上却不相同。
下面定义了一个int类型数组指针类型:
typedef int (*ArrayPtr)[];
ArrayPtr是一个指针类型,这个指针类型指向一个数组,意思就是我们可以用这个自定义类型定义一个变量,然后我们可以给这个变量赋值,这个值必须是一个int类型数组的地址,如下:
int main(void)
{
int a[4] = {1, 2 ,3, 4};
ArrayPtr array_ptr = &a;
return 0;
}
此时编译并不报错,但是如果我们给array_ptr赋的值不是&a,而是a,就会报如下的warning,提示类型不匹配。因为&a是整个数组的地址,a是数组首元素的首地址,尽管数值上两者并没有什么区别,但意义上却完全不同。
我们也可以通过数组指针的方式来访问数组中的元素:
#include <stdio.h>
typedef int (*ArrayPtr)[];
int main(void)
{
int i = 0;
int a[4] = {1, 2 ,3, 4};
ArrayPtr array_ptr = &a;
for(i = 0; i < 4; i++)
{
printf("(*array_ptr)[%d] = %d\n", i, (*array_ptr)[i]);
}
return 0;
}
编译运行结果如下:
数组作为参数传递给函数,传递的是数组首元素指针,而不是数组指针。
之前在一些面试题中看到过类似的说法,题中说的是“ 数组作为参数传递给函数,传递的是数组的地址 ”,这句换在我看来是不严谨的,到底是数组的地址还是数组首元素的地址,编译器会告诉我们,可以用以下代码来测试:
1 #include <stdio.h>
2
3 int *p1 = NULL;
4 int (*p2)[] = NULL;
5
6 void Fun1(int *a)
7 {
8 p1 = a;
9 p2 = a; //11:5: warning: assignment from incompatible pointer type
10 }
11
12 int main(void)
13 {
14 int a[4] = {1, 2 ,3, 4};
15 Fun1(a);
16 return 0;
17 }
Linux下gcc编译结果如下:
我们可以看到,在函数定义处编译器就会提示我们类型不匹配。
这里的p1是一个类型的指针,而p2是一个int数组类型的指针,由此可得结论,数组作为参数传递给函数,传递的是数组首元素的地址,而不是整个数组的地址。这也是数组作为函数参数的情况下一般要加一个参数用来指定传入的数组大小的原因,因为数组传参只会传递数组首元素的地址,在函数内部是不可能通过数组首元素的地址来推断出数组整体大小的(尽管数组的地址与数组首元素的地址在数值上是相等的),我们也不可能在传递数组的时候同时指定数组大小,下面是一个在传递数组的时候同时指定数组大小的示例,在测量p的大小时只会得到一个指针的大小,而无法得到数组的大小。
1 #include <stdio.h>
2
3 void Fun1(int *p)
4 {
5 printf("sizeof(p_in_fun1) = %d\n", sizeof(p));
6 }
7
8 void Fun2(int p[5])
9 {
10 printf("sizeof(p_in_fun2) = %d\n", sizeof(p));
11 }
12
13 int main(void)
14 {
15 int a[5] = {1, 2 ,3, 4, 5};
16 Fun1(a);
17 Fun2(a);
18 return 0;
19 }
编译运行结果:
所以一般数组传参时需要加一个指定数组大小的参数,这样不容易产生数组访问越界的问题,在数组访问越界时会出现随机的结果,甚至会产生运行时的段错误(Segment Error:访问了不该访问的内存区域)。
1 #include <stdio.h>
2
3 void Fun1(int *p)
4 {
5 printf("sizeof(p_in_fun1) = %d\n", sizeof(p));
6 printf("*(p_in_fun1+5) = %d\n", *(p+5)); //产生随机数
7 }
8
9 void Fun2(int p[5])
10 {
11 printf("sizeof(p_in_fun2) = %d\n", sizeof(p));
12 }
13
14 int main(void)
15 {
16 int a[5] = {1, 2 ,3, 4, 5};
17 Fun1(a);
18 Fun2(a);
19 return 0;
20 }
产生了随机数:
但是在传递数组指针时,却可以同时指定数组大小,也可以在函数内部得到数组的大小,测试代码如下:
#include <stdio.h>
void Fun1(int *p)
{
printf("sizeof(p_in_fun1) = %d\n", sizeof(p));
}
void Fun2(int p[5])
{
printf("sizeof(p_in_fun2) = %d\n", sizeof(p));
}
void Fun3(int (*p)[5])
{
printf("sizeof(p_in_fun3) = %d\n", sizeof(*p));
}
int main(void)
{
int a[5] = {1, 2 ,3, 4, 5};
Fun1(a);
Fun2(a);
Fun3(&a);
return 0;
}
编译运行结果:
总结:
数组在作为参数传递给函数时,一般要同时传递数组大小,因为传递的仅仅是数组首元素的地址,通过数组首元素的地址是无法得出数组大小的,传递数组大小可以避免数组访问越界。
新人才疏学浅,有错的地方敬请指正!!
本文来自博客园,作者:夏末终年,转载请注明出处:https://www.cnblogs.com/xiamozhongnian/p/15857475.html