数组和指针
数组:同类元素的集合
指针:存放地址的变量
(图解1)
&a和a的区别
&a:整个数组的地址
a:在降级使用时代表了数组首元素的地址,在sizeof和&的时候代表了整个数组
尝试:
char arr[10]={};
int *p=arr;
int *p=&arr;//左右两边的类型不同,编译不通过
int (*p)[10]=&arr;//指向数组的指针
_________________________________________________________________________________________________________________________
int arr[5]={1,2,3,4,5};
sizeof(arr): 5 strlen(arr):5
sizeof(arr+1):4 strlen(&arr):5
sizeof(&arr):4 strlen(&arr+1):随机数
sizeof(&arr+1):4 strlen(arr+1):4
sizeof(*&a):20//数组的地址,解引用之后指向整个数组相当于int (*p)[5]
###&a+1:跳过了整个数组 开始的位置在‘\0’的后面
__________________________________________________________________________________________________________________________
char *name="abcde";
sizeof(name): 4 (指针类型的大小) strlen(name):5
sizeof(name+1):4 strlen(&name):随机值(取的是指针的地址,从指针地址处开始寻找'\0')
sizeof(&name):4(二级指针的大小) strlen(&name+1):随机数
sizeof(&name+1):4 strlen(name+1):4
数组和指针的访问方式
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
printf("%d %d",*(a+1),*(ptr-1));
输出结果:2 5
详解:*ptr指向整个数组之后的下一个元素,将它-1相当于退回4个字节指向数组元素5;
值得注意的是char arr[]="abcde"; char *p=(char *)(&arr +1);再次访问*(p-1)时指向'\0',因为它是一个char型的指针,它只能回退1个字节!
变量的声明:不创建空间,只声明其他部位变量,使用其时引用同一块空间!
eg:char arr[]="abcde"; 在另一文件中:extern char arr[]; printf("%s\n",arr);使用时仍然访问arr地址。
1.现在我们来将数组声明成一个指针
eg:char arr[]="abcde"; 在另一文件中:extern char *arr; printf("%s\n",arr);这样的方式是不被允许的,arr是一个指针变量,编译器在找arr时只可以看到4个字节(指针的大小)。
我们可以对指针arr取地址找到arr首址printf("%s\n",(char *)(&arr));
2.我们现在定义一个指针并将其声明为数组
eg:char *p="abcdef";在另一文件中:extern char p[]; printf("%s\n",p);
此时p存储的是‘a’的地址,所以p[]里存储了‘a’的地址而不是字符串!那么我们取出的%s其实是从a地址的第一个字节开始访问!
假设‘a’的地址:0018ff44 按照小端存储模式它在内存里是这样子的:44 ff 18 00 你取出44ff88遇到0相当于遇见'\0'停下
可以这样取出(char *)(*(int *)p)将其强制类型转换成整形,取出a的完整的地址,在强制类型转换成char*型的指针,此时它就已经相当于是一个指向数组的指针!
###由此可以总结出数组和指针是两种不同的类型!它们也并没有什么联系
参考书籍:《高质量C\C++编程》《C语言深度剖析》
变量的声明可以出现在多个地方,但定义只可以出现一次.
指针数组和数组指针
int q[10] (含有10个整形元素的数组) 开辟40字节空间
int *p[10] (含有10个整形指针的数组) 开辟40字节空间
int (*r)[10] (指向一个大小为10的整形数组的指针)开辟4个字节空间 初始化:int(*r)[10]=&q;
int *(*w)[10](指向大小为10整形指针数组的指针) 开辟4个字节空间 初始化:int*(*w)[10]=&p;
eg: char a[5]={'','','','',''};
char(*p)[4]=&a;//错 除非强制类型转换char(*p)[4]=(char*)&a; 此时p+1向后偏移4个字节
char(*p)[5]=a;//错
eg: struct Test
{
...
}*p;//指向结构体的指针
atruct Test test;//假设p保存0x00000000 假设结构体为20字节
p+0x1 = 0x00000014//指向大小20的结构体的指针+1等于向后偏移20字节
(unsigned long)p+0x1 = 0x00000001//把指针强制类型转换成一个长整形,数字加一就相当于直接加一
(unsigned int *)p+0x1 = 0x0000004//把结构体指针强制类型转换成一个int型指针,偏移4字节
int a[]={1,2,3,4};
int *p1=(int*)(&a+1);
int *p2=(int *)((int)a+1);
printf("%x %x",p1[-1],*p2);
详解:&a+1的位置是数组后面的一个字节空间p1[-1]向前退4 个字节
(int )a+1将数组首址数字化加一就等于让p2从数组首址处向后偏移了一个字节的大小
注意:我们取出的时候应该注意一下你的计算机是大端还是小端的存储方式,两种存储方式取出的数据是不一样的~
(图解2)
利用联合完成大端小端的测试
void check()
{
union UN//所有变量共用同一块空间,空间大小是最大成员的空间,联合成员一次只能使用一个
{
int c;//4字节
char i;//1个字节
}
UN.c=1;
if(UN.i==1)
printf("little!");
else
printf("big!");
}