C语言学习记录——삼십이 指针数组题(2)

目录

二维数组

指针


二维数组

int main()
{
    int a[3][4] = { 0 };
    printf("%d\n", sizeof(a));   48
    sizeof(a[0][0]);    4
    sizeof(a[0]);   16
    sizeof(a[0] + 1);   4
    sizeof(*(a[0] + 1));     4 
    sizeof(a + 1);    4
    sizeof(*(a + 1));    16
    sizeof(&a[0] + 1);      4
    sizeof(*(&a[0] + 1));    16
    sizeof(*a);   16
    sizeof(a[3]);   16
    return 0; 
}

11、数组名放到sizeof内部,也就是计算总大小。3*4*4=48

22、a[0][0],单个元素的大小,就是4

33、a[0]单独放在sizeof括号里,计算这一行的大小,4*4 = 16

44、a[0] + 1,并不是像33一样,而是放在了表达式里了,所以a[0] 表示的是首元素,所以最后结果就是4。

55、a[0] + 1就是第一行第二个元素,加上*  得到它的地址,所以就是4/8

66、a就是这个数组的数组名,不是sizeof (数组名),也不是& (数组名),所以就是首元素地址。而把二维数组看成一维数组时,首元素就是它的第一行,a就是第一行地址,+1,也就是第二行地址,所以就是4

77、拿出第二行,所以是16

88、a[0]就是第一行,取地址它,然后+1,也就是第二行地址

99、拿出第二行,16

10、*a,就是首元素地址,也就是第一行,所以16

11、数组第四行,16。虽然这个数组没有第四行,但是sizeof内部表达式是只根据类型来计算的,所以都可以算出来。

指针

int main()
{
    int a[5] = { 1, 2, 3, 4, 5 };
    int* ptr = (int*)(&a + 1);
    printf("%d,  %d\n", *(a + 1), *(ptr - 1));
    return 0;
}

&a就是整个数组地址,+1,指向数组后的那个地址,存这个地址是数组指针,所以要强制类型转换。*(ptr - 1) 那么也就指向了5。而*(a + 1) 很简单,就是指向2了。

struct Test
{
    int Num;
    char* pcName;
    short sDate;
    char cha[2];
    short sBa[4];
}*p;
//假设p的值为0x100000。如下表达式的值分别是多少?
//已知,结构体Test类型的变量大小是20个字节。
int main()
{
    p = (struct Test*)0x100000;
    printf("%p\n", p + 0x1);
    printf("%p\n", (unsigned long)p + 0x1);
    printf("%p\n", (unsigned int*)p + 0x1);
    return 0;
}

p的值是0x100000,是结构体指针类型,+0x1,其实就是+1,指针加1,要加这个类型的变量大小,所以+20,20放在16进制位中就是14,所以会变成0x100014,%p打印,是16进制位打印,32位的机器,转换成16位就是8个字节,所以就是0x00100014。

结构体的计算是这样的,对齐数是一个编译器默认给的数值,一般为4;第一个变量要放在结构体地址的偏移量为0的地址处,也就是从头开始加,int为4,现在第一个数字就是4,4要+后面的数字,char*,是个指针,4个字节,但不能盲目地直接加,从第二个开始,后面的成员需要对比一下对齐数,取该成员大小和对齐数的较小值,对齐到这个较小值的整数倍位置,现在是4和4的比较,所以直接选4;此时已经到了8,是2的4倍,short是2个字节,比4小,那么它就真实地占2个字节,现在是10;char是1个字节,10是1的10倍,cha是有2个字符的数组,所以cha占2个字节,现在是12;12是2的6倍,所以依然可以接着加,short有4个,所以12 + 2 * 4 = 20

当把p强制类型转换后,转换成了一个十进制long型整数,那么对应的十进制数是1048576,再加1,就是1048576,转回16进制其实就是100001,所以打印出来就是0x00100001。

把p强制转换为整数指针后,再加1,就需要加4,所以最终变为0x00100004。

    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这个数组在内存中的布局是这样的

以小端存储为例

01 00 00 00   02 00 00 00   03 00 00 00   04 00 00 00

 取出数组a的地址+1,ptr1指向了04 00 00 00的后面一个字节。电脑中每个字节 都有地址,所以地址之间相差一个字节,所以+1就是加一个字节。

同样的,a地址转换为int ,不论地址是什么样,+1后再指向数组时,指向的就是首元素01后的那个字节,处于01和00之间。

ptr[-1],之前写过,可变成*(ptr - 1),所以就是4。而*ptr2,从那里开始往后打印,00 00 00 02,打印出来就是20000000。

    int a[3][2] = { (0, 1), (2, 3), (4, 5) };
    int* p;
    p = a[0];
    printf("%d", p[0]);

a[3][2]里放的是逗号表达式,逗号表达式的结果是最后一个表达式,所以初始化进去的只是1,3,5。

也就是

1   3

5   0

0   0

a[0]是第一行的数组名,所以p里面存储着1。p[0]就可以等同于*(p + 0),所以打印出1。

    int a[5][5];
    int(*p)[4];
    p = a;
    printf("%p, %d\n", &p[4][2] - &a[4][2] - &a[4][2]);

对于a的布局

00000   11111   22222   33333    44444

以数字来代替,0就是代表a[0]。*p指向的是一个四个元素的数组。现在让p = a,编译器会报错。a数组名,也就是首元素地址,它的类型是int [5],那么指针类型就是int (*) [5]。而p的类型是

int (*) [4]。类型不一样,不过也可以。

先看&a[4][2]。对应的就是44444中第三个4。

然后&p[4][2]。

p是指向了00000。p[4],之前学过,也就是p + 4。那么从p + 0开始,由于他是地址,一次+4个字节,p + 0不动,p + 1越过4个字节,直到p + 4,指向的就是33333中的第二个3。然后解引用,p + 4拿到的是后面4个3,并且是首元素地址,然后[2]也就是再+2,所以最终指向第4个3。这样两者相减后就是-4。这是%d输出的结果。%p,地址输出,-4最终的补码是11111111111111111111111111111100,拿它做地址后,4个1就是1个f,最后的1100是12,也就是c,所以最后就是FF FF FF FC。

结束。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值