指针练习题

第一题 整形数组

 
数组名是首元素地址
1.sizeof(数组名)-表示整个数组
2.&数组名-数组名表示整个数组

接下来,我们直接看题目


int main()
{
    int a[] = { 1,2,3,4 };
    printf("%zd\n", sizeof(a));//16这里指的是整个数组所占空间大小
    printf("%zd\n", sizeof(a+0));//8 (地址)数组名运算时就表示首元素地址,+o还是首元素地址,表示地址大小4/8
    printf("%zd\n", sizeof(*a));//4 *a就是首元素,数组是int类型
    printf("%zd\n", sizeof(a+1));//8(地址)地址大小不变4或8
    printf("%zd\n", sizeof(a[1]));//4
    printf("%zd\n", sizeof(&a));//8(地址)
    printf("%zd\n", sizeof(*&a));//16
    printf("%zd\n", sizeof(&a+1));//8 &a+1跳过了16个字节
    printf("%zd\n", sizeof(&a[0]));//8第一个元素的地址
    printf("%zd\n", sizeof(&a[0]+1));//8第二个元素的地址
    return 0;
}
      答案  16 8 4 8 4 8 16 8 8 8


第二题
字符数组(1)


int main()
{
    char a[] = { 'a','b','c','d','f' };
    printf("%zd ", sizeof(a));//5
    printf("%zd ", sizeof(a + 0));//8
    printf("%zd ", sizeof(*a));//1
    printf("%zd ", sizeof(a[1]));//1
    printf("%zd ", sizeof(&a));//8
    printf("%zd ", sizeof(&a+1));//8
    printf("%zd ", sizeof(&a[0]+1));//8
    printf("\n");
    //5811888
    //strlen 再次复习一下  <string,h> 参数:按地址依次读取直到读到\o停止。计算字符串长度不包括\0
    printf("%zd\n", strlen(a));//由于数组中没有\0所以所得值一定比5大且为随机值
    printf("%zd\n", strlen(a + 0));//这里a+0指的是首元素地址,也是随机值
    //printf("%zd", strlen(*a));//err这里指的是第一个元素,'a'Acall指是97,也就是从97这个地址开始读取,但这个地址可能被使用了
    //printf("%zd", strlen(a[1]));//这里同理 err
    printf("%zd\n", strlen(&a));//随机值 &a指的是整个地址,但是是从首元素开始,所以也是随机值
    printf("%zd\n", strlen(&a + 1));//随机值-5
    printf("%zd\n", strlen(&a[0] + 1));//随机值-1
    
    return 0;
}


第三题字符数组(2)


int main()
{
    char a[] = "abcdef";//里面其实有一个\0被省略了
        printf("%zd ", sizeof(a));//7
        printf("%zd ", sizeof(a + 0));//8
        printf("%zd ", sizeof(*a));//1
        printf("%zd ", sizeof(a[1]));//1
        printf("%zd ", sizeof(&a));//8
        printf("%zd ", sizeof(&a+1));//8
        printf("%zd ", sizeof(&a[0]+1));//8
      答案  7811888
        printf("\n");
        printf("%d ", strlen(a));//6
        printf("%d ", strlen(a + 0));//6
        //printf("%zd ", strlen(*a));//err
        //printf("%zd ", strlen(a[1]));//err
        printf("%d ", strlen(&a));//6
        printf("%d ", strlen(&a + 1));//随机值
        printf("%d ", strlen(&a[0] + 1));//5
        return 0;
}


第四题字符数组(3)


int main()
{
    char* p = "abcdef";//常字符串
    printf("%zd\n", sizeof(p));//8地址
    printf("%zd\n", sizeof(p+1));//8地址
    printf("%zd\n", sizeof(*p));//1
    printf("%zd\n", sizeof(p[0]));//1  p[0]==*(p+0)
    printf("%zd\n", sizeof(&p));//8地址
    printf("%zd\n", sizeof(&p+1));//8地址
    printf("%zd\n", sizeof(&p[0]+1));//8地址
    //8811888
    printf("%zd\n", strlen(p));//6
    printf("%zd\n", strlen(p + 1));//5
    //printf("%zd\n", strlen(*p));//err
    //printf("%zd\n", strlen(p[0]));//err
    printf("%zd\n", strlen(&p));//随机值
    printf("%zd\n", strlen(&p + 1));//随机值
    printf("%zd\n", strlen(&p[0] + 1));//5
    return 0;
}


第五题二维数组


int main()
{
    int a[3][4] = { 0 };
    printf("%zd ", sizeof(a));//48 a单独存在算的是整个数组的大小
    printf("%zd ", sizeof(a[0][0]));//4 两次解引用得到的是元素,类型为int 大小为4
    printf("%zd ", sizeof(a[0]));//16 一次解引用得到的是第一行首元素地址,单独放在sizeof里,算的是整个数组的大小
    printf("%zd ", sizeof(a[0]+1));//4/8地址 不单独存在,指的是第一行第二列元素的地址
    printf("%zd ", sizeof(*(a[0] + 1)));//4 相比上题多了解引用指的是该元素大小为4
    printf("%zd ", sizeof(a+1));//4/8地址  指的是第二行地址 
    printf("%zd ", sizeof(*(a + 1)));//16 指的是第二行首元素地址,但单独存在算的是整个一行数组的大小
    printf("%zd ", sizeof(&a[0]+1));//4/8地址 相当于a+1 第二行地址
    printf("%zd ", sizeof(*(&a[0] + 1)));//16 和上题相比多了个解引用 相当于第二行首元素地址,也单独存在
    printf("%zd ", sizeof(*a));//16 a本身指的是首元素 *a指的是第一行地址的解引用
    printf("%zd ", sizeof(a[3]));//16 越界了,但是sizeof只算它的大小并不访问,所以不报错
    return 0;
}
总结
1 二维数组可看成一维数组
比如a[3][4]可以看成一个有三个元素的的一维数组
只不过每个元素为一个一维数组的地址,
一次解引用可以得到某一行的一维数组的首元素地址
两次解引用可得到该行一维数组的的某一个元素
本质的理解是这样
2 但是有特殊情况
sizeof(arr)算的是整个数组的大小
&arr取得是整个数组的地址,比如p=&arr   arr他们都是地址但在+-1的情况下 &arr+1跳过的是整个数组的字节大小
但是arr+1加的是该 数组类型字节大小*1
 3 arr[][]={0};
*(arr+0)==arr[0]
(*(arr+0)+0)=arr[0][0]


第六题指针笔试题


开始进入指针笔试题

笔试题1


int main()//                        int* p=&a+1
{                                 // a         &a+1/p
                                  // |         |
    int a[5] = { 1,2,3,4,5 };     // 1 2 3 4 5 0 0 0 0 0 0 //解释一下0为内存中假设存储的值
    int* p = (int*)(&a + 1);      //  a+1->2  p-1->5 答案为2 5
    printf("%d %d", *(a + 1), *(p - 1));//此时p一步为4个字节 a一步也为4个字节
    return 0;
}

struct Test
{
    int Num;
    char* pcname;
    short sdate;
    char cha[2];
    short sba[4];
}*p;
假设p的值为0x0000000000100000 如下表表达式的值分别为多少?
p指针的类型为struct Test大小为32字节 
结构体的内存大小现在还不知道,放一下
当然可以用sizeof计算一下
int main()
{
    p = (struct Test*)0x0000000000100000;//上面的数其实是整数,所以需要强制类型转换
    printf("%p\n", p + 0x1);// 0000000000100020
    printf("%p\n", (unsigned long)p + 0x1);//0000000000100001
    printf("%p\n", (unsigned int*)p + 0x1);//0000000000100004
    printf("\n");
    printf("%zd ", sizeof(struct Test));
    printf("%zd ", sizeof(int));
    printf("%zd ", sizeof(char*));
    printf("%zd ", sizeof(short));
    printf("%zd ", sizeof(char[2]));
    printf("%zd ", sizeof(short[4]));
    return 0;
}
这个指针题目主要是讲了类型转换以及指针类型的作用是决定+-1跳过的字节大小

笔试题2


int main()
{
    int a[4] = { 1,2,3,4 };
    int* p1 = (int*)(&a + 1);
    int* p2 = (int*)((int)a+1);//这里是int强制转换成整数
    printf("%x,%x", p1[-1], *p2);
    return 0;
}
解释一下
p1的计算很简单直接是4
这里得编译器是小端存储
内存中记数也是16进制,两位16进制占一个字节                (这里假设该地址的值)
所以 01|00|00|00(1)|02|00|00|00(2)|03|00|00|00|04|00|00|00| 00 00 00
        |                                                     |
      p2(从0开始)                                            p1(从0开始)
                                 地地址————————————> 高地址
p1[-1]=*(p1-1)等同于p1向右走4个字节然后解引用:解的是 04 00 00 00
但真正在打印出来时,得转化为整形(16进制)00 00 00 00 04==00000000000000000000000000000004==4
*p2为解引用 解的是 00 00 00 02
 但真正在打印出来时,得转化为整形(16进制)02 00 00 00==0000 0010 0000 0000 0000 0000 0000 0000==2^25
但是结果要用16进制打印(有效数字前面的0会被省略)
答案 4,2000000
当然这个代码在vs2022中x64无法运行,但可以在win32中运行(主要是理解它的意思)


笔试题3


int main()
{
    int a[3][2] = { (0,1),(2,3),(4,5) };
    int* p;
    p = a[0];
    prntf("%d", p[0]);
    return 0;
}
解释一下
二维数组的初始化都是用{}的
比如char a[2][3]={{1,2,3},{4,5,6}};
而本题使用的是(),说明是逗号表达式
既 a为       1  3  
             5  0 
             0  0 
所以答案是1 
第九题
int main()
{
    int a[5][5];
    int(*p)[4];//数组指针
    p = a;
    printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
    return 0;
}
解释
其实 p是数组指针,本质上要解两次引用才能得到元素,当把a(二维数组的首元素地址传过去),可把二维当一维使用
 我们假设元素都是0(这里按地址排列)
 00000 00000 00000 00000 00000 (这里一个元素4个字节)
  一    二    三    四     五
&p[4][2]  &a[4][2]
先解引用,再取地址呀
p一步走4*4个字节   而a一步走5*4个字节
p[4]向右走4*4*4个字节 一个元素4字节 走16步
 00000 00000 00000 00      000 00000 (这里一个元素4个字节)
                   |       |
                   p[4]   p[4][2]
p[4][2]由于已经解引用一次了,所以再一次解引用对应的是元素,此时每走一步只走4字节(一个元素)
最后 &p[4][2]取得是第17个元素的地址
同理&a[4][2] 首先是跳过4*5个字节再跳过2个字节所以对应的是第23个元素
&p[4][2]-&a[4][2]=他们之间的元素个数+1=17-23=-4
还有解释-4得转化为地址(也就是转化为16进制)
x64的地址是64个字节,所以需要提升字节位(用lld表示4)
1000000000000000000000000000000000000000000000000000000000000100原
1111111111111111111111111111111111111111111111111111111111111011反
1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1100补
二进制的4位代表16进制的一位
1111->16->f
fffffffffffffffc即为地址
所以答案为:fffffffffffffffc,-4
这个题目比较复杂,多多消化


笔试题4


int main()
{
    int aa[2][5] = { 1,2,3,4,5,6,7,8,9,10};
    int* ptr1 = (int*)(&aa + 1);
    int* ptr2 = (int*)(*(aa + 1));
    printf("%d %d", *(ptr1 - 1), *(ptr2 - 1));
    return 0;
}
解释
我们还是老样子
先把ptr1 ptr2表示出来
1 2 3 4 5 6 7 8 9 10 0 0 0 0 0 0 0 (这里是元素,(一个元素是4个字节),我们假设10个元素后面元素都是0)
          |          |
         ptr2       ptr1
 &aa+1这里是取得是aa数组的地址加1,跳过的是40个字节,所以prt1指向10之后的地址
 aa指的是首元素地址(这里需要把二维数组看成是一个一维数组)所以aa+1是跳过5个元素,指向的是6
 (int*)为强制类型转换,是ptr1和ptr2 +-1时跳过的是4个字节(也就是一个元素)
 所以*(ptr1-1)指的是10 *(ptr2-1)得到的是5
答案是10 5


笔试题5


int main()
{
    char* a[] = { "work","at","alibaba" };
    char** pa = a;
    pa++;
    printf("%s %s %s\n", a[0],*pa,a[2]);
    return 0;
}
解释
char*a[3]={} 其实元素存储的是'w' 'a' 'a'的地址 a是数组,有三个元素,每个元素的类型是char*并且
char*类型指针只能被常字符串赋值    work at alibaba 全都是常字符串
pa取得是a,也就是说取得是首元素地址,该元素存储的是'w'的地址,pa++加的char*类型对应的字节大小 x64是64个字节
其实就是第二个元素的地址
*p对应的是第二个元素,第二个元素是 字常符a的地址
打印出来的是 at
注:这里数组名的类型也是char**,很重要!!!!!!!!
答案是:work at alibaba

总结

这些题目都是笔者之前在学习指针是一下一下敲出来的,如果要学好指针请大家多多动手,这样才能真正理解,谢谢大家阅读,如内容有所不对,请大家多多指教

  • 38
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值