八道超经典指针面试题(三千字详解)

目录

第一题:

第二题: 

第三题:

第四题:

第五题:

第六题:

 第七题:

第八题:


第一题:

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

预测其输出结果。

题解思路:指针题是需要画图的,不画图思路是绕不出来的。下图就是a数组的内存布局。

int * ptr =(int *)(&a + 1);ptr所指如图:

很多同学可能不太明白:数组名为一个数组的首元素地址,但除两种情况:

1.&+数组名——————————————这里就表示是整个数组的地址。

2.sizeof(数组名)——————————   这里表示的是整个数组所占的字节数。

回到题目,(&a+1)中的1的意思就是步距为一个数组,在首元素地址的基础上加上了一整个数组的字节。

接下来看看printf()中的a+1、ptr-1:

那么答案一目了然:分别是第二个元素——2,与覅五个元素——5. 

第二题: 

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

思路与题解:

0x开头的是十六进制数。

在第一个printf()中p+0x1,一个Test结构体占20个字节,且p是struct Test *p类型,p每次加上一个1都要移动对应的20字节(详情请见初级指针的指针与指针类型).又因为是十六进制,20=0x14,所以p+0x1=0x100014.

在第二个printf()中(unsigned long)p+0x1,在这里p先由指针变量被强制类型转换为无符号long类型,因此p+0x1就是普通的整数相加,得到0x100001。然后%p在将p的数据以地址的形式打印出来了。

在第三个printf()中,p被再次转换成指针,但这次是以无符号整型的形式。unsigned int *就限定了每次指针访问的空间为四个字节。所以p+1的结果为0x100004

第三题:

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:由int * ptr1=(int *)(&a+1);画出下图:

ptr1[-1]其实就相当于*(ptr1-1)。因为其实‘[]’与’*‘的功能是一样的。(详情见七千字详解操作符

 ptr2:

但是我们仔细看一下 int *ptr2 = (int *)((int)a + 1);中数组名a被强制转换为int类型的了。所以a+1只能向前移动一个字节。为了更好的观察我把这部分的图化成这个形式。(因为vs编译器是小端存储,左移这里的数据是01000000而不是00000001)

(int)a+1往后移动了一个字节如下图 :

 这就是ptr2所指向的数据。

 综上所述,答案为:0x02 00 00 00,00 00 00 04.

第四题:

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

思路与答案:

这里其实用到了逗号表达式(详情见七千字详解操作符)与二级数组。

其实在a[3][2]中的数据为:

a指向

p=a[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;
}

思路与答案:

 这个就是a[5][5]的布局,其中的数字代表帝即位元素。

a[5][5]:

先分析a[4][2],a[4][2]也等效于*(*(a+4)+2),你看哪种便于理解。如下图:

 p[4][2]:

由int (*p)[4]得出:p为int [4]类型的指针,那么p就指向一个有四个元素的数组。由此可得p+1一次会移动四个元素:

 &p[4][2] - &a[4][2]的值为相差的元素个数,因此&p[4][2] - &a[4][2]=-4

但是这里还需要我们谈一下:

&p[4][2] - &a[4][2]得出的整数结果是-4,-4在内存中存储的是一个二进制的补码,原理如下

因此我们内存中的-16是这样的

原码:1000 0000 0000 0100

反码:1111 1111 1111 1011

补码:1111 1111 1111 1100

因为%p是无符号数,所以 %p直接提取内存中的补码:0xfffffffc

因为%d是输出有符号整数,所以要将补码重新转成原码才输出:-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;
}

思路与题解:

aa数组的内存步距如下: 

aa指向第一行的地址: 

 

&aa去除的是整个数组的地址,所以&aa+1如下图:

 aa+1则会指向第二行的数据:

 

 

所以:

 答案呼之欲出:10,5

 第七题:

这是一道阿里巴巴的笔试题:

#include <stdio.h>
int main()
{
 char *a[] = {"work","at","alibaba"};
 char**pa = a;
 pa++;
 printf("%s\n", *pa);
 return 0;
}

 思路与题解:

 

pa++如下图:

 

因此答案为:“at” 

第八题:

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;
}

思路与题解:

指针间的关系图如下:

**++cpp就等效于*(*(cpp+1)) 得到如图:

顺着指针得出:POINT 

*--*++cpp+3等效于*(*(cpp+1)+1)+3,我们从内到外分析,

由cpp+1得到如图:

 又*(cpp+1)+1得到:

 

*(*(cpp+1)+1)解出来是c[0]的地址,*(*(cpp+1)+1)+3就是这样的。

 

 因此*(*(cpp+1)+1)+3打印出来就是ER。

*cpp[-2]+3: 

*cpp[-2]+3相当于:**(cpp-2)+3得到下图:

 由此可见答案为:ST

 

 

  • 27
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 23
    评论
好的,下面列举几道 C++ 指针面试题供参考。 1. 指针和引用有什么区别? 指针和引用都可以用来访问内存中的变量,但是它们有以下几点区别: - 指针可以被赋值为 `nullptr`,而引用不行。 - 指针可以在运行时指向不同的变量,而引用在声明时就必须绑定到一个变量上,并且不能更改绑定的变量。 - 指针可以进行算术运算和解引用操作,而引用不能。 - 指针可以被重新赋值为指向不同类型的变量,而引用必须与原始变量类型匹配。 2. 指针和数组有什么关系? 指针和数组有着紧密的关系,因为数组名其实就是数组首元素的地址。例如,下面的代码定义了一个数组并初始化: ```c++ int arr[] = {1, 2, 3, 4, 5}; ``` 可以通过下标访问数组元素,也可以通过指针访问数组元素。例如,下面的代码使用指针访问数组元素: ```c++ int* p = arr; for (int i = 0; i < 5; i++) { cout << *(p + i) << " "; } ``` 这里的 `p` 是一个指向 `int` 类型的指针,它指向数组 `arr` 的首元素。在循环中,我们通过指针访问数组元素,其中 `*(p + i)` 表示指针 `p` 加上 `i` 个偏移量后所指向的元素。 3. 如何避免空指针引用? 空指针引用是一种常见的程序错误,可以通过以下几种方式避免: - 在使用指针之前,先将其初始化为 `nullptr`。 - 在使用指针之前,先进行空指针判断。 - 使用智能指针,可以自动管理指针的生命周期,并且可以避免空指针引用。 例如,下面的代码演示了如何进行空指针判断: ```c++ int* p = nullptr; if (p != nullptr) { *p = 10; } ``` 在这个示例代码中,我们先将指针 `p` 初始化为 `nullptr`,然后在使用指针之前,先进行空指针判断。由于 `p` 是空指针,所以不会执行赋值操作,从而避免了空指针引用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一周学八天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值