真实面经题目 · 原创解析

给定数组指针,如何访问第 50 个元素?

这道题考察的是数组下标、指针运算和数组指针类型的边界理解。核心回答应先澄清“第50位”通常指第50个元素,因此数组从 0 开始计数时应访问 arr[49],若给的是首元素指针则写成 *(p + 49)。如果题目说的是下标 50,则对应 arr[50],也就是第 51 个元素。

出现于:阿里巴巴 · 测开

60 秒回答模板

可以先说明数组访问的前提:C/C++ 中数组下标从 0 开始。如果题目里的“第50位”按日常表达理解为第 50 个元素,那么应访问下标 49,即 arr[49]。如果给的是指向首元素的指针 int *p,那么等价写法是 *(p + 49),因为 p + 1 不是移动 1 个字节,而是移动一个元素的大小。还要区分“数组名退化为指向首元素的指针”和“数组指针”这两个概念:arr 在表达式中通常会退化为 int *,而 int (*p)[100] 是指向整个含 100 个 int 的数组的指针。若 p 是 int (*p)[100],访问第 50 个元素应写成 (*p)[49] 或 *(*p + 49)。如果面试官强调的是下标 50,则答案改为 arr[50]、*(p + 50) 或 (*p)[50],并说明那是第 51 个元素。

考点 先澄清第50位的含义
主线 首元素指针的访问方式
易错点 把第 50 个元素直接写成 arr[50],没有意识到…

深入解析

01

先澄清第50位的含义

“第50位”有两种可能理解:一种是自然语言里的第 50 个元素,另一种是程序下标为 50 的位置。数组下标从 0 开始,所以第 50 个元素对应下标 49,写作 arr[49]。如果直接说下标 50,则访问 arr[50],它实际上是第 51 个元素。优秀回答要先说明这个歧义,再给出对应写法。

02

首元素指针的访问方式

如果题目说给了数组和数组指针,但实际指的是指向首元素的指针 int *p,那么 p 等价于指向 arr[0]。访问第 50 个元素时应写 *(p + 49),也可以理解为 p[49]。这里的 p + 49 按 int 的元素宽度移动 49 个位置,不是简单移动 49 个字节。

03

数组名退化的关键点

数组名 arr 在大多数表达式里会退化为指向首元素的指针,也就是 int *,因此 arr[49] 与 *(arr + 49) 等价。这个等价关系来自下标运算的定义:a[i] 本质上可以理解为 *(a + i)。但 sizeof(arr) 和取地址 &arr 等场景不会按普通首元素指针处理。

04

数组指针和元素指针的区别

真正的数组指针类型是 int (*p)[100],它指向的是整个长度为 100 的数组,而不是单个 int 元素。此时 p + 1 会跨过一个完整的 100 元素数组,移动的跨度是 100 * sizeof(int)。所以访问第 50 个元素不能写 *(p + 49),而应先解引用整个数组,再访问元素:(*p)[49]。

05

边界与合法性判断

长度为 100 的数组合法下标范围是 0 到 99。如果访问第 50 个元素,arr[49] 一定在范围内;如果访问下标 50,arr[50] 也在范围内,但含义不同。回答时应避免只给一个表达式而不说明语义,因为这类题通常在考察自然序号和程序下标之间的差异。

06

可给出的标准表达

可以组织成一句完整回答:若第50位指第 50 个元素,则数组写 arr[49],首元素指针写 *(p + 49);若 p 是 int (*p)[100] 这种数组指针,则写 (*p)[49];若题目明确说下标 50,则访问 arr[50] 或 *(p + 50)。这样既回答了操作,也覆盖了类型差异。

c

数组下标与指针偏移等价关系

int value_at_50(const int *arr, int len) {
  if (arr == NULL || len < 50) {
    return 0;
  }

  return arr[49];      /* same as *(arr + 49) */
}
  • 第 50 个元素对应下标 49,因为数组下标从 0 开始。
  • arr[i] 本质等价于 *(arr + i),指针偏移会按元素类型大小前进。

易错点

  • 把第 50 个元素直接写成 arr[50],没有意识到数组下标从 0 开始。
  • 把 int *p 和 int (*p)[100] 混为一谈,导致指针步进单位判断错误。
  • 认为 p + 1 只是地址数值加 1 字节,忽略了指针运算会按元素类型大小移动。
  • 只回答一个表达式,不说明“第50位”可能是自然序号或下标语义。
  • 把数组名完全等同于指针变量,忽略 sizeof(arr) 和 &arr 等特殊场景。

面试官追问

如果 p 是 int *p,为什么第50个元素不是 *(p + 50)?

因为第 50 个元素是自然序号,从 1 开始数;数组下标从 0 开始,所以第 1 个元素是 *(p + 0),第 2 个是 *(p + 1),依次类推,第 50 个就是 *(p + 49)。*(p + 50) 对应的是第 51 个元素。

arr[49] 和 *(arr + 49) 完全一样吗?

在普通元素访问表达式中可以认为等价,因为 arr 会退化为指向首元素的指针,arr + 49 指向第 50 个元素,再解引用得到值。但在 sizeof(arr)、&arr 等场景里,arr 不按普通 int * 处理。

int *p 和 int (*p)[100] 最大区别是什么?

int *p 指向单个 int 元素,p + 1 前进一个 int;int (*p)[100] 指向一个包含 100 个 int 的完整数组,p + 1 会跨过整个数组。前者访问第 50 个元素写 *(p + 49),后者写 (*p)[49]。

如果数组长度是100,arr[100] 能访问吗?

不能。长度为 100 的数组合法下标只有 0 到 99,arr[100] 已经越界。即使内存上看起来有地址,也属于未定义行为,测试或开发中都不能依赖这种访问结果。

怎样回答更稳妥?

先说需要确认“第50位”是第 50 个元素还是下标 50。默认按第 50 个元素回答为 arr[49] 或 *(p + 49);如果给的是数组指针 int (*p)[100],则是 (*p)[49]。最后补充下标 50 对应 arr[50]。