当前位置:   article > 正文

【C语言进阶】指针常见笔试题详解_c语言指针笔试题

c语言指针笔试题

前言

在我们学习完指针的有关知识之后,我们为了对指针有一个更为深入的理解,我们今天来学习几道指针笔试题,来看看指针是如何考察的。

预备知识:

数组名一般情况下为

数组首元素的地址

两个例外,此时数组名代表整个数组:

sizeof(数组名)

&(数组名)

第一题

  1. int main()
  2. {
  3.    int a[5] = { 1, 2, 3, 4, 5 };
  4.    int *ptr = (int *)(&a + 1);
  5.    printf( "%d,%d", *(a + 1), *(ptr - 1));
  6.    return 0;
  7. }
  8. //程序的结果是什么?

首先初始化了一个整型数组a,数组有五个元素,a为数组名,随后,我们发现&a,此时a为数组名且直接跟在&后边,所以此时代表整个数组的地址,整个数组地址加1,跨越整个数组,此时&a+1的类型为int (*) [5],为一个数组指针,强制类型转换为(int *),赋给ptr指针变量

最后进行打印,先打印*(a+1),a为数组名,且不属于两个例外,说明在此处代表数组首元素的地址,整型指针进行加1,跨越一个整型元素,解引用,此时就是a[1],为数组第二个元素,ptr指向数组的末尾,且此时为int*,所以进行减1,向前跨越一个整型元素,解引用,即为a[4],为数组的第五个元素。

所以结果就是2和5。

我们来看结果:

第二题 

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

首先我们看到定义了一个结构体指针p,类型为struct Test*,而Test类型的变量大小为20个字节,这里我们先不多讲,后面会对结构体内存对齐有详细的讲解。

p+x1 :p是结构体指针,也就是一个地址,地址为0x100000,加上0x1,即就是跨越一个结构体,加上20个字节,由于为16进制所以得到0x100014,用%p打印,打印四个字节,打印结果为0x00100014。

(unsigned long)p + 0x1 :p为一个指针,存储结构体Test的地址,为0x100000,此时强制类型转换成一个无符号的长整型,整型进行加1,即就是直接加1,同时用%p打印,打印结果为0x00100001。

(unsigned int*)p + 0x1 : p为0x100000,为结构体指针,强制类型转换成整型指针,整型指针加1,即就是跨越一个整型元素,即加上4个字节,打印结果为0x00100004。

最后我们来看结果验证一下:

第三题

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

首先初始化一个整型数组a,数组有四个元素。

 ptr1: a为数组的数组名,由于是&a,所以属于两个例外中的一个,此时a代表整个数组,&a代表整个数组的地址,该地址加1,跨越整个数组,此时数据类型为int (*) [4],强转为int* 赋给ptr1。

ptr2 : a此时不是特殊情况,即a为数组首元素的地址,类型为int*,强转为int型,然后加1,再次强转为int*,此时的地址就是向后跨越了一个字节。

ptr1[-1]: ptr为一个整型指针,ptr[-1]相当*(ptr-1),ptr指向数组最后一个元素的后边,减1向前跨越一个整型元素,所以打印的结果是4。

*ptr2:ptr2指向首元素地址的后边的一个字节,由于在vs中是小端存储,所以数据的低位字节位于低地址处,高位字节位于高地址处。

我们通过一张图来解释:

所以此时得到的值为0x20000000,%x十六进制打印为20000000。

最后我们来看看结果:

第四题

  1. #include <stdio.h>
  2. int main()
  3. {
  4.    int a[3][2] = { (0, 1), (2, 3), (4, 5) };
  5.    int *p;
  6.    p = a[0];
  7.    printf( "%d", p[0]);
  8. return 0;
  9. }

首先我们来定义一个二维数组,行为3,列为2,这个题给我们埋下了一个坑,二维数组初始化括号内部是逗号表达式,而不是常规的初始化,逗号表达式从左向右依次计算,值为最右边表达式的值,所以这个数组的初始化为不完全初识化,只初始化了三个元素。

 定义一个指针变量p,p指向a[0],这里的a[0]也是一个数组名,他就是二维数组第一行的数组名,此时,不是两种特殊情况,这个就是数组首元素的地址,即就是第一行第一个元素的地址,p[0]相当于*(p+0),代表二维数组的首元素故打印结果为1。

我们来验证一下结果:



 第五题

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

 定义一个五行五列的二维数组a,同时定义一个有四个元素的数组指针p,此时将a赋给p。

我们通过一个图来讲解:

由于数组指针,每个数组只有四个元素,而二维数组的列为5,所以p[4][2]的地址小于a[4][2]的地址,所以%d打印的结果就是-4,而用%p打印,打印一个地址,不能打印出一个负数,此时就要考虑数据在内存中的存储。

-4的原码:1000 0000 0000 0000 0000 0000 0000 0100

-4的反码:1111  1111  1111  1111  1111  1111  1111  1011

-4的补码:1111  1111  1111  1111  1111  1111  1111  1100

此时用%p打印,所以认为这个数是无符号的,将-4的补码直接转换为十六进制打印

所以打印结果为:

ff ff ff fc

我们来验证一下结果:

第六题:

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

定义一个二行五列的二维数组aa。

ptr1:对&aa,aa为二维数组的数组名,&aa是特殊情况,代表整个数组,&aa代表整个数组的地址,对&aa加1,跨越整个数组,然后强转为int*。

ptr2:aa为一般情况,为二维数组的数组名为二维数组第一行的地址,对第一行的地址加1,跨越一行,指向数组的第二行,对该指针解引用,得到aa[1],即就是第二行的数组名,也是第二行第一个元素的地址,此时类型就是int* ,强转并没有什么意义。

* ( ptr1 - 1 ):ptr1指向整个数组的末尾,此时ptr1为int* 类型,所以减1向前跨越一个整型元素,再解引用,此时的结果应该是10。
*(ptr2 - 1):ptr2指向数组第二行第一个元素,也为int* 类型,所以减1向前跨越一个整型元素,再解引用,此时的结果是5。
我们来验证一下:

 

第七题

  1. #include <stdio.h>
  2. int main()
  3. {
  4. char *a[] = {"work","at","alibaba"};
  5. char**pa = a;
  6. pa++;
  7. printf("%s\n", *pa);
  8. return 0;
  9. }

定义一个char*类型的指针数组,数组中每个元素为char*类型,再定义一个二级指针指向这个数组的首元素。

pa++,pa的类型为char**,说明pa指向的元素类型为char*,pa加1,跨域一个char*的元素,pa本来指向work的首元素,pa++之后指向at。

同样我们来验证一下:

 第八题

  1. int main()
  2. {
  3. char *c[] = {"ENTER","NEW","POINT","FIRST"};
  4. char**cp[] = {c+3,c+2,c+1,c};
  5. char***cpp = cp;
  6. printf("%s\n", **++cpp);
  7. printf("%s\n", *--*++cpp+3);
  8. printf("%s\n", *cpp[-2]+3);
  9. printf("%s\n", cpp[-1][-1]+1);
  10. return 0;
  11. }

首先定义一个指针数组,数组中每一个元素都是字符串首字符的地址,然后定义一个二级指针数组,再定义一个三级指针变量指向二级指针数组的首元素,即就是二级指针数组首元素的地址。

我们来通过图片来理解一下他们的关系:

 注意:

c是一个char*类型的指针数组,数组中的元素并不是字符串,而是字符串首字母的地址。

**++ cpp:cpp指向cp[0],前置++后,cpp指向cp[1],解引用后为c+2,再次解引用,得到POINT首字母P的地址,输出POINT
*--*++ cpp + 3 前置++后,cpp指向cp[2],解引用后为c+1,前置--后,cp[2]被改为c,指向c[0]的地址,解引用得到ENTER的首字母E的地址,类型为char*,加3之后,跨越三个char类型的元素,得到E的地址,输出ER
* cpp [ - 2 ] + 3 cpp[-2]相当于*(cpp-2),得到c+3,类型为char**,再次解引用得到FIRST首元素F的地址,为char*类型,加3跨越3个char类型的元素,所以得到S的地址,所以输出ST
cpp [ - 1 ][ - 1 ] + 1:cpp[-1][-1] 相当于*(*(cpp-1)-1),所以cpp-1指向c+2,解引用后得到c+2,c+2-1得到c+1,指向NEW的首元素,得到N的地址,此时为char*类型,加1后得到E的地址,所以输出EW

我们来验证一下:

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/164709
推荐阅读
相关标签
  

闽ICP备14008679号