小试牛刀(指针和数组练习)

练习一
#include <stdio.h>
 int main()
 {
    int a[5] = { 1, 2, 3, 4, 5 };
    int *ptr = (int *)(&a + 1);
    printf( "%d,%d", *(a + 1), *(ptr - 1));
    return 0;
 }

代码分析:
这里写图片描述
一维数组,&数组名取出的是整个数组的地址,&数组名+1指针跳到整个数组的后面,此时该地址的类型为int(*)[5],所以在整形指针ptr的时候需要强转成int *类型
*(a+1)数组名没有单独放在sizeof内部,也没有取地址,此时该数组名降级变成首元素地址,*(a+1)等价于a[1]
(int *)(&a+1)拿到整个数组后面的地址强转成int *,此时该指针访问权限又变成4byte,ptr-1指向数组最后一个元素的地址,解引用拿出这个元素

运行结果:
这里写图片描述

练习二
#include <stdio.h>

struct Test
{
    int Num;
    char *pcName;
    short sDate;
    char cha[2];
    short sBa[4];
}*p;

int main()
{
    p = (struct Test*)0x00000000;
    printf("%p\n", p + 1);
    printf("%p\n", (long)p + 1);
    printf("%p\n", (int*)p + 1);
    return 0;
}

代码分析:
定义了一个结构体Test,该结构体的大小是20byte,p = (struct Test*)0x00000000将一个十六进制的数强制类型转换成指向该结构体指针的类型,因为*p是该结构体的实例化,p是指向该结构体的一个指针的内容,表示整个结构体开辟内存空间的起始地址
p + 1该结构体指针+1,该指针跳过整个结构体的内存大小
(long)p + 1将结构体指针类型的变量强制类型转换成整形,整形+1,所表示的数值+1
(int*)p + 1将结构体指针类型的变量强制类型转换成int *int *类型指针+1,向后偏移4byte

运行结果:
这里写图片描述

练习三
#include <stdio.h>

 int main()
 {
    int a[4] = { 1, 2, 3, 4 };
    int *ptr1 = (int *)(&a + 1);
    int *ptr2 = (int *)((int)a + 1);
    printf( "%x,%x", ptr1[-1], *ptr2);

    return 0;
 }

代码分析:
ptr1[-1]与练习一相同,相当于*(ptr1-1)拿到了数组最后一个元素
这里写图片描述
(int *)((int)a + 1)数组名a在这里降级变为首元素地址,强制类型转化成整形后+1地址向后偏移1byte
整形在内存中以小端存储,+1后又强转成int *,此时ptr2的解引用访问的字节数为4byte,拿到了以小端存储的02 00 00 00

运行结果:
这里写图片描述

练习四(多么痛的领悟)
#include <stdio.h>

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

代码分析:
注意:数组正确的初始化方式是这样

int a[3][2] = {{0,1},{2,3},{4,5}};

而该代码中的初始化方式用到了逗号表达式,逗号表达式的结果取决于最后一个表达式的结果所以该初始化相当于如下代码:

int a[3][2] = {1,3,5};

a[0]是二维数组第一行的数组名,在这里降级为第一行第一个元素的地址,赋给整形数组p,p[0]相当于*(p+0)拿到第一行第一个元素

运行结果:
这里写图片描述

练习五
#include <stdio.h>
typedef int(*q)[4];
 int main()
 {
    int a[5][5];
    int(*p)[4];
    p = (q)a;
    printf("a_ptr=%p,p_ptr=%p\n", &a[4][2], &p[4][2]);
    printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
    return 0;
 }

代码分析:
这里写图片描述
数组名a在此处降级为首元素地址,即第一行的地址,类型为int (*)[5],如果想把a赋给类型为int(*)[4]的p存在类型差异,在vs 2010中虽在编写时有标红提示但是在运行过程中没有报错、结果正常,如需消除标红进行类型强转即可。

&a[4][2]如图所示,拿出了第五行第三个元素的地址
&p[4][2]因为p为数组指针,p每+1跳过一个int [4]大小,p+4跳过了16个整形变量大小即64byte,解引用后拿出了该位置的地址,即为*(p+4),而*(*(p+4)+2)此时*(p+4)相当于int *,表示为在*(p+4)的地址上再往后偏移2个整形大小,解引用后拿出该位置指向的元素,&p[4][2]再还原为该位置的地址

指针-指针=两块地址中间元素的个数

注意:两指针指向同一类型的空间时,指针-指针才有意义
如图可见俩位置间有4个整形空间大小,即16byte
因为是小地址减大地址即小数减大数结果为负,若以%p输出,则将其看成地址,当成无符号数输出

运行结果:
这里写图片描述

练习六
#include <stdio.h>

 int main()
 {
    int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int *ptr1 = (int *)(&aa + 1);
    int *ptr2 = *(aa + 1);
    printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));
    return 0;
 }

代码分析:
第一个值不再赘述,与练习一、三相同
这里写图片描述
*(aa + 1)aa作为一个数组名在这里降级为首元素地址,即第一行的地址,是类型为int (*)[5]的数组指针,aa+1跳过5个整形大小的空间,解引用后拿到如图所示的位置地址,此时*(aa + 1)整体类型为int *
*(ptr2-1)先向前偏移一个整形大小空间,解引用后拿出该位置的元素,即第一行第五个元素

运行结果:
这里写图片描述

练习七

解释下面代码:

1(*( void(*) ()) 0)()
(2)void (*signal(int, void (*)(int)))(int)

代码分析:
(1)
这里写图片描述
(2)
这里写图片描述

练习八
#include <stdio.h>

 int main()
 {
    char *a[] = {"work","at","alibaba"};
    char**pa = a;
    pa++;
    printf("%s\n", *pa);
    return 0;
 }

代码分析:
a是一个指针数组,初始化个数为3,每个都是一个char *的指针,指向一个字符串的起始地址
&a+1此时a的类型为char *(*)[3],+1将会跳过整个char *类型的数组
char**pa = aa作为一个数组名,在这里降级当成首元素地址处理,第一个元素类型为为char *,它的地址类型为char **
pa++跳过一个char *刚好找到数组a中第二个元素,即存放第二个字符串的地址
这里写图片描述
最终到函数打印的时候,pa指向存放第二个字符串的空间位置,解引用拿到”at”的首元素地址

运行结果:
这里写图片描述

练习九
#include <stdio.h>

int main()
{
    char *c[] = {"ENTER","NEW","POINT","FIRST"};
    char**cp[] = {c+3,c+2,c+1,c};
    char***cpp = cp;
    printf("%s\n", **++cpp);
    printf("%s\n", *--*++cpp+3);
    printf("%s\n", *cpp[-2]+3);
    printf("%s\n", cpp[-1][-1]+1);
    return 0;
}

代码分析:
c是指针数组,其中的四个元素都是char *类型
cp也是指针数组,其中的四个元素都是char **类型
cpp是数组指针
这里写图片描述

1printf("%s\n", **++cpp);

这里写图片描述

2printf("%s\n", *--*++cpp+3);

这里写图片描述

3printf("%s\n", *cpp[-2]+3);

这里写图片描述

(4) printf("%s\n", cpp[-1][-1]+1);

这里写图片描述
运行结果:
这里写图片描述

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值