前言:
数组是一段连续的内存空间,数组的空间大小为sizeof(array_type)array_size,数组名可看做指向数组第一个元素的常量指针* ,指针是一种特殊的变量,与整数的运算规则为p+n等价于(unsigned int)p+n*sizeof(*p),指针之间只支持减法运算,参与减法运算的指针类型必须相同,即p1-p2等于((unsigned int)p1 - (unsigned int)p2)/sizeof(type),特别注意的是只有当两个指针指向同一个数组的元素时,指针的相减才有意义,指针也可以进行关系运算(<,<=,>,>=),但是对于比较运算(==,!=)的指针类型必须相同
深入分析
1.数组名不等同于指针
test.c
#include <stdio.h>
int main()
{
extern int a[];
printf("&a = %p\n",&a);
printf("a = %p\n",a);
printf("*a = %d\n",*a);
return 0;
}
ext.c
int a[]={1,2,3,4,5};
结果:
&a = 0x804a014
a = 0x804a014
*a = 1
将extern int a[]改为extern int *a
结果:
&a = 0x804a014
a = 1
段错误 (核心已转储)
通过运行结果可以知道数组名只不过在某些情况下面可以看作指针,但是数组名绝对不是指针,当声明为extern int a[]时,a就标志着数组的起始地址,当声明为extern int *a时&a表示指针a的起始地址在数组的起始地址,但是a里面保存的地址就是数组的首元素1(四个字节),所以1地址是非法访问的空间就会出现段错误,特别说明的是a和&a是有区别的,&a为整个数组的地址,a为数组首元素的地址,区别在于指针运算,a+1等同于(unsigned int)a+sizeof(a),&a+1等同于(unsigned int)(&a)+sizeof(a),(unsigned int)(&a)+sizeof(&a)
2 面试题分析
int main()
{
int a[5] = {1,2,3,4,5};
int *p1 = (int*)(&a+1);
int *p2 = (int*)((int)a+1);
int *p3 = (int*)(a+1);
printf("%d,%d,%d\n",p1[-1],p2[0],p3[1]);
return 0;
}
结果
5,33554432,3
我们可以知道&a+1就是移动一个数组的长度,p1指向a[6],p1[-1]就相当于往前移动一个指针的位置即p1指向a[5],所以第一个打印出来是5,a里面保存的是数组的首地址,转化为int类型后加一就指向了0x804a015,而且Linux是小端系统,所以*p2的值就为0x02000000,十进制为33554432,对于p3来说很显然就为3
数组参数
数组作为函数参数时,编译器将其编译成对应的指针,所以在一般情况下,当定义的函数中有数组参数时,需要
定义另一个参数标识数组的大小
void f(int a[])<->void f(int *a);
void f(int a[5])<->void f(int *a);
test.c
#include <stdio.h>
void func1(char a[5])
{
printf("In func1: sizeof(a) = %d\n", sizeof(a));
*a = 'a';
a = NULL;
}
void func2(char b[])
{
printf("In func2: sizeof(b) = %d\n", sizeof(b));
*b = 'b';
b = NULL;
}
int main()
{
char array[10] = {0};
func1(array);
printf("array[0] = %c\n", array[0]);
func2(array);
printf("array[0] = %c\n", array[0]);
return 0;
}
结果:
In func1: sizeof(a) = 4
array[0] = a
In func2: sizeof(b) = 4
array[0] = b