【数组与指针的诡异关系】JS 和 C 双视角讲清楚底层结构!

137 阅读3分钟

🧠 引言

你有没有想过:

  • JS 中 arr[i] 背后到底干了什么?
  • 为啥 C 语言里数组名就像个指针?
  • JS 没有指针,但我们真就不需要理解指针了吗?

本篇文章用一种**“双语对照”**(JavaScript vs C)的方式,带你从底层视角看清楚:数组的本质其实就是“地址 + 偏移”,而理解它,会对你掌握更高阶算法(如:内存操作、位运算、动态数组)大有裨益!


🔬 一、数组是啥?从底层说起

数组的本质:

一段连续内存,按固定步长排列的变量集合。

🎯 JavaScript 中的数组

const arr = [10, 20, 30];
console.log(arr[1]); // 输出:20

JS 的数组是对象的“特殊变种”,可以动态扩容,性能与底层实现相关(稀疏 vs 密集数组)。

🎯 C 语言中的数组

int arr[3] = {10, 20, 30};
printf("%d\n", arr[1]); // 输出:20

C 语言中,arr 实质是指向第一个元素的地址。

*(arr + 1) == arr[1]; // 完全等价

🧱 二、JS 模拟数组底层行为

我们可以自己模拟数组的底层机制,帮助理解。

✅ 构建一个“定长数组类”:

class ArrayLike {
  constructor(size) {
    this.buffer = new Array(size);
  }

  set(index, value) {
    if (index >= this.buffer.length) throw new Error('越界访问');
    this.buffer[index] = value;
  }

  get(index) {
    if (index >= this.buffer.length) throw new Error('越界访问');
    return this.buffer[index];
  }
}

const arr = new ArrayLike(3);
arr.set(0, 10);
arr.set(1, 20);
arr.set(2, 30);
console.log(arr.get(1)); // 输出:20

🧠 思考:

  • JS 封装后就像“安全的 C 数组”
  • index 实质就是地址偏移(地址 + index * size

🧪 三、C 中的数组 VS 指针本质

int arr[3] = {10, 20, 30};
int *p = arr;

printf("%d\n", *(p + 1));  // 输出 20
表达式含义
arr首地址(int* 类型)
arr[i]*(arr + i)
p[i]*(p + i)
&arr[i]获取第 i 个元素地址

你以为是数组,实际是“偏移指针+地址”结构!


📚 四、为什么 JS 没有指针,也要懂它?

虽然 JS 没有暴露指针语法,但底层仍依赖:

  • TypedArray(如:Uint8Array) → 内存块操作
  • ArrayBuffer + DataView → 模拟内存读写
  • WebAssembly / WebGPU / 图像处理 → 离不开地址操作

✅ 示例:用 TypedArray 写一个内存块

const buffer = new ArrayBuffer(8);         // 8字节
const view = new Int32Array(buffer);       // 每个占4字节
view[0] = 100;
view[1] = 200;
console.log(view); // 输出:Int32Array(2) [100, 200]

⚠️ 五、易错点提示

场景错误理解正确理解
JS数组是连续内存?不一定(取决于底层优化)对象变种,可能稀疏或优化成稠密
C 的数组是对象?不是,对象和数组完全不同是内存地址的指针偏移结构
arr[i] 本质?表面是索引访问实际是 *(arr + i)

🔄 六、拓展任务

  • 手动实现一个基于 ArrayBuffer 的 Matrix 类
  • 写一个 C 函数,将数组逆序输出(用指针实现)
  • 尝试在 JS 中模拟实现 memcpy() 函数

🧩 总结一句话

前端工程师理解数组背后的“指针模型”,将为你进入更高性能的计算与算法世界打开大门!


下一篇预告:

📘 第4篇:【图解哈希表】从手写 Map 到哈希冲突解决策略!