指针内存大小
一个基本的数据类型(包括结构体等自定义类型)加上“”号就构成了一个指针类型,
**指针大小是一定的,与“”号前面的数据类型无关。“*”号前面的数据类型只是说明指针所指向的内存里存储的数据类型。所以,在32 位系统下,不管什么样的指针类型,其大小都为4byte。** 指针存放所指向内容的首地址。
*int p = NULL 和 p = NULL 有什么区别?
a 和&a 的区别
如上图&aa+1表示aa的下一个数组,即aa[5]的首地址。即aa+20的地址,每个int型为4个byte。
则赋值的ck指针的指向的地址为aa[5]的地址,则(ck-1)则为aa[4]的地址。
&aa + 1: 取数组aa的首地址,该地址的值加上sizeof(aa) 的值,即&aa + 5*sizeof(int),也就是下一个数组的首地址,显然当前指针已经越过了数组的界限。
(ck-1): 因为ck 是指向aa[5],并且ck是int * 类型,所以(ck-1) 是指向aa[4] ,输出589。
定义为指针,声明为数组
文件1
char *p = “abcdefg”;
文件2
extern char p[];
在文件1 中,编译器分配4 个byte 空间,并命名为p。同时p 里保存了字符串常量“abcdefg”的首字符的首地址。这个字符串常量本身保存在内存的静态区,其内容不可更改。在文件2中,编译器认为p 是一个数组,其大小为4 个byte,数组内保存的是char 类型的数据。在文件2 中使用p 的过程如下图:
指针数组和数组指针
指针数组:可以理解为数组,数组里存的是指针。即存储指针的数组。
指针数组就相当于一次声明了多个指针。数组的每一个元素都是一个指针。
int *p[3];
p[0]=(int*)malloc(20);
p[1]=(int*)malloc(30);
p[2]=(int*)malloc(30);
*p[0]=124;
*(p[0]+1)=155;
*p[1]=888;
指针数组的应用
如上图所示,manuel[ ]数组存的是几个字符串的首地址。也可以输出*(manuel[2]+3)表示字符h。
应用2
int a[4]={111,2114,34575,415367};
int main(void)
{
int *ptr2=(int *)(a+1);
int *ptr1=(int *)(&a+1);
printf( "%x\n",*ptr2);
return 0;
}
*ptr2为数组a的第二个元素。
但是
int a[4]={111,2114,34575,415367};
int main(void)
{
int *ptr2=(int *)((int)a+1); //注意注意注意
int *ptr1=(int *)(&a+1);
printf( "%x\n",*ptr2);
return 0;
}
这里的ptr2则是a元素的首地址加1个byte,而不是加一个元素。
如上图ptr2的地址为a数组第一个元素的地址加1个byte,所以*ptr2的值需要涉及到大端小端。
二维数组与数组指针
数组指针
首先它是一个指针,它指向一个数组,简称指向数组的指针。
数组指针就相当于一次声明了一个指针。只不过这个指针指向很特别,是一个数组
首先定义一个二维数组,5行5列,定义一个数组指针,指向的数组长度为5.
p的地址为二维数组的首地址。
int hsb[5][5]={{1,2,3,4,5},
{6,7,8,9,10},
{11,12,13,14,15},
{16,17,18,19,20},
{21,22,23,24,25}
};
int (*p)[5];
p=hsb;
注意数组指针的用法与指针数组不能混淆。(*(p+1))[2] 这里的括号是不能取消的。
((p+1))[i]这里的i可以为0-4,表示第二个数组里面的元素,地址是元素加1 。
((p+i))[2] 这里的表示递增为1个数组,分别表示3、8、13、18、23
(*(p+1))[2] 也可以表示为p[1][2]
***(p+1)[i]*不加括号的时候不能表示数组指针,这时候i加1,则表示递加一个数组的地址。并且该地址为递加数组的首地址。
总结:数组指针的括号是绝对不能省略掉的。
[]优先级大于,故数组指针声明时要加()
实列1:
#include<stdio.h>
#include<stdlib.h>
void main()
{
//数组指针的用法,用处。
int b[16]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
int (*p)[4]; //该语句是定义一个数组指针,指针步长为4个int即16位。
//int *p2=b;
p=b;
int i=0;
while(i<16)
{
printf("%d\t",(*p)[i]);
//printf("%d\t",*p2++);
i++;
}
}
printf("%d\t",(*p)[i]);遍历输出数组b;
数组指针就相当于舞王僵尸一样,带了小弟,n就是小弟的个数,通过下标值能直接访问到具体哪一个小弟,即数组里哪一个数
数组指针下标自增(i++)的时候,指针指向不变。
实列2:
把例1的while()语句替换。
**while(i<4)
{
printf("%d\t",(*p++)[0]);
i++;
}**
输出1 5 9 13,相当于指针一次移动了4个int.16个字节。
实列3:
**while(i<4)
{
printf("%d\t",(*p+i)[0]);
i++;
}**
这时则是增加一个int元素的地址,则输出 1 2 3 4;
**
实列4
:**
数组指针指向二维数组
#include<stdio.h>
#include<stdlib.h>
void main()
{
int a[3][4]={{1,2,3,4},{11,12,13,14},{21,22,23,24}};
int (*p)[4]; //该语句是定义一个数组指针,指针步长为4个int即16位。
p=a;
int i=0;
while(i<3)
{
//printf("%d\t",(*p)[i]);
//数组指针,指向的是一个数组整体,相当于指针也带了下标,当执行i++操作时,下标+1,得到该数组的下一个元素,
//在该例中,指针没有位移,所以依次输出为1 2 3
printf("%d\t",(*p++)[0]);
//整型数组类型的指针,指向的是一个数组整体,当执行*p++操作时,指针位移该数组长度的位数
//在该例中,即指针位移4个int的长度,所以输出是1 11 21
i++;
}
}
&p[4][2] - &a[4][2]的值为多少?
int a[5][5];
int (*p)[4];
p = a;
&a[4][2]=45+2=22个int
&p[4][2]=44+2=18个int
当数组名a 作为右值时,代表的是数组首元素的首地址。这里的a 为二维数组,我们把数组a 看作是包含5 个int 类型元素的一维数组,里面再存储了一个一维数组。如此,则a 在这里代表的是a[0]的首地址。a+1 表示的是一维数组a 的第二个元素。a[4]表示的是一维数组a 的第5 个元素,而这个元素里又存了一个一维数组。所以&a[4][2]表示的是&a[0][0]+45sizeof(int) + 2sizeof(int)。根据定义,p 是指向一个包含4 个元素的数组的指针。也就是说p+1 表示的是指针p 向后移动了一个“包含4 个int 类型元素的数组”。这里1 的单位是p 所指向的空间,即4sizeof(int)。所以,p[4]相对于p[0]来说是向后移动了4 个“包含4 个int 类型元素的数组”,即&p[4]表示的是&p[0]+44sizeof(int)。由于p 被初始化为&a[0],那么&p[4][2]表示的是&a[0][0]+44sizeof(int)+2* sizeof(int)。