C中指针和数组引发的探索二
在上一篇文章中分析了指针与数组的区别,包括编译器内存分配概况:http://www.cnblogs.com/guoyuanwei/archive/2012/06/05/2535413.html
这篇文章将主要研究下指针和数组间相同点。
在1978年7-8月,The Bell System Technical Journal,57卷,6号,第1991-2019页中提到:“当一个数组名出现在一个表达式中时,它会被转换为一个指向该数组第一个元素的指针”
1、“表达式中的数组名就是指针”如下代码
int a[10], *p, i=2;
p=a; p[i];
上面的代码表示取p[i]的值,可以看到这时指针和数组是一样的用法。实际上对数组的引用如a[i]在总是被编译器编译为*(a+1),这个是C语言的标准规定的,因此编译器的设计者,都遵循
了这个规律,因此在一个表达式中数组名也就成了指针。
2、“C语言把数组下标[]作为指针的偏移量”处理
产生这个现象的根本原因在与硬件机制,指针和偏移量是底层硬件所使用的基本模型
3、 “作为函数参数的数组名等于指针”
在C标准中规定,在函数形参这个特殊情况下,编译器必须把数组形式改写成指向数组第一个元素的指针形式。编译器只向该函数传递数组的地址,而不是整个数组的拷贝,编译器之所以这样做,主要从效率方面考虑,如果拷贝整个数组,无论在时间上还是在内存空间上开销都比较大。因此C语言标准中规定:“所有数组在作为参数传递时,都转换为指向数组起始地址的指针,而其它参数均采用传值调用”,这样也可以简化编译器的设计。
同理函数的返回值也不可能是一个数组,只能是指向数组的指针。这里实际上体现出了一个“传值”与“传址”的区别,这在其它编程语言如C++,C#都有这样的区别。在函数内部使用指针,所能进行的对数组的操作几乎跟传递原本的数组没啥差别。但是如果想用sizeof(实参)来获取数组的长度,得到的结果并不是数组的长度。如下面代码:
void Fun7(char *a);
void Fun8(char a[]);
int main(int argc,char*argv[])
{
char s1[]="abcdef";
char *s2="abcdef";
Fun7(s1);
Fun8(s1);
}
void Fun7(char *a)
{
printf("%d",sizeof(a)) ;
printf("%s",a);
}
void Fun8(char a[])
{
printf("%d",sizeof(a)) ;
printf("%s",a);
}
函数Fun7和Fun8被调用,执行的结果是一样的,但是里面sizeof(a)值并不是数组的大小6,而为4,因为a代表的是一个指针,指针的大小就是4个字节,这也证明了上面的分析。为了进一步的理解这个问题,输入下面的代码,先分析输出的情况:
#include<stdio.h>
void fun1(char *s);
int main(void)
{
char a[]="abcdefg";
fun1(a);
char c=getchar();
}
void fun1(char *s)
{
printf("%d\n",s);
printf("%d\n",&s);
printf("%d\n",&(s[0]));
printf("%d\n",&(s[1]));
}
上面的代码将一个字符数组当作参数传入,
printf("%d\n",s)这条语句应该输出的是指针变量s的值,也就是数组元素的首地址;
printf("%d\n",&s);这条语句应该输出的是编译器给实参s分配的地址,是一个栈区域的地址值;
printf("%d\n",&(s[0]));这条语句应该输出的是数组中第一个元素的地址值;与printf("%d\n",s)结果应该相同。
printf("%d\n",&(s[1]));输出的是数组中第2个元素的地址值。比printf("%d\n",s)输出值大1
运行上面的代码,结果如下:
可以看到与分析的一致。