本文是针对B站鹏哥c语言教程指针练习的个人总结笔记,学习复习用。
练习一
int main()
{
int a[3][4];
printf("%dn",sizeof(a));
printf("%dn",sizeof(a[0]));
printf("%dn",sizeof(a[0]+1));
printf("%dn",sizeof(*(a[0]+1)));
printf("%dn",sizeof(a+1));
printf("%dn",sizeof(*(a+1));
printf("%dn",sizeof(&a[0]+1));
printf("%dn",sizeof(*(&a[0]+1)));
printf("%dn",sizeof(*a));
printf("%dn",sizeof(a[3]));
return 0;
}
一道一道来分析:
printf("%dn",sizeof(a));
首先看第一小题,数组名单独放在sizeof内,计算整个数组的内存大小,该数组共有3*4 = 12个int元素,则结果为48。
一道一道来分析:
printf("%dn",sizeof(a));
首先看第一小题,数组名单独放在sizeof内,计算整个数组的内存大小,该数组共有3*4 = 12个int元素,则结果为48。
首先看第一小题,数组名单独放在sizeof内,计算整个数组的内存大小,该数组共有3*4 = 12个int元素,则结果为48。
printf("%dn",sizeof(a[0]));
printf("%dn",sizeof(a[0]+1));
printf("%dn",sizeof(*(a[0]+1)));
a[0]与a[0]+1区别在哪?
a[0]是原二维数组的行数组,是一个一维数组名,相当于其首元素的地址,是一个int指针类型,a[0] + 1就相当于&a[0][0]+1即&a[0][1]。
a[0]单独置于sizeof内时,计算的就是该一维数组的内存大小(前面提过),即4*4 = 16。但是a[0]+1在sizeof内就是计算一个指针变量的内存大小,即4或8。
注意:指针变量大小和cpu寻址长度相关,32位就是4个字节,64位就是8个字节。
那么*(a[0]+1),不言而喻,就是*(&a[0][1])即a[0][1],其内存大小为4。
printf("%dn",sizeof(a+1));
printf("%dn",sizeof(*(a+1));
a是一个二维数组名,一维数组指针类型,相当于&a[0],就是第一行的地址,则a+1即&a[0]+1,指针的增减运算是与指针类型密切相关的,这里加1就是地址偏移了一个含有四个int元素的一维数组的内存大小对应字节数,讲的不太明白的亚子,看图!!
*(a+1)即*(&a[0]+1)即*(&a[1])即a[1],计算的就是一维数组a[1]内存大小即16。
printf("%dn",sizeof(&a[0]+1));
计算的是&a[1],指针呗,结果就是4或8。
printf("%dn",sizeof(*(&a[0]+1)));
计算的就是a[1],同*(a+1),结果为16。
printf("%dn",sizeof(*a));
*a即*(&a[0])即a[0],4*4 = 16。
printf("%dn",sizeof(a[3]));
欸,不对劲呀,你这不越界访问了吗??你定义的二维数组行下标不也就0,1,2吗?好像是哦......但是
注意3:sizeof不对括号内表达式进行计算,只是用以计算操作数所占内存大小。什么意思呢?在这里,定义了一个二位数组a[3][4],内存空间中开辟了一段大小为48的连续内存 :
&a[i]就是以&a[0]为出发点,向后偏移i个单位,这里的单位的字节数由指针类型所确定,如&a[i]为一维数组指针型,内含4个int,则一单位就是16。a[3]就是在&a[2]+1处向后找了一块16字节的内存确立为一个含4int的一维数组,将其放入sizeof内并没有去访问并操作该数组(实际上不存在),单纯地计算其对应类型(假设存在)所占内存大小而已,结果就是16。
PS:如果是a[-1]呢?结果也是一样的。
小总结:1.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
2.&数组名,这里的数组名表示整个数组,取出的是整个数组的地址,也是一个数组指针类型。
3.除此之外,所有的数组名都表示其首元素的地址。
至此,练习一部分结束
练习二 (更新中...)
int a[4] = {1, 2, 3, 4};
int *ptr1 = (int *)(&a + 1);
int *ptr2 = (int *)((int)a + 1);
printf("%x,%x",ptr1[-1],*ptr2);
&a取的是整个数组的地址,相当于一个一维数组指针类型,加1即为&a[1]。
(int *) 强制将int *p() 转化为int *p,ptr1[-1]相当于在ptr1[0]上向左偏移一个int地址单位,其值就与a[3]相同,输出结果就为4000000。
(int)a + 1不同于a + 1,后者是Int指针型加1,地址增加4字节,而前者是int型加1,地址增加1字节。这里输出是以%x也就是16进制输出的,若是小端存储的话,*ptr2拿出来的就是四个字节的0x00000002对应的数0x02000000,输出结果就为2000000。
大端模式(Big-endian):高位字节排放在内存的低地址端,低位字节排放在内存的高地址端,即正序排列,如存放0x2就为00 00 00 02。
小端模式(Little-endian):低位字节排放在内存的低地址端,高位字节排放在内存的高地址端,即逆序排列,如存放0x2就为02 00 00 00。
int a[3][2] = {(0,1),(2,3),(4,5)};
int*p;
p = a[0];
printf("%d",p[0]);
这里有陷阱,注意在初始化这个二维数组时里面的括号是( )而非{ },( )里面的逗号表达式一算出来就相当于int a[3][2] = {1,3,5}。p = a[0]就是把a[0]这个一维数组的地址放到p处,则p[0]就为a[0][0]即1。



