当我访问数组的元素时,在硬件级别会发生什么

What happens at a hardware level when I access an element of an array?

本文关键字:硬件 什么 访问 数组 元素      更新时间:2023-10-16
int arr [] = {69, 1, 12, 10, 20, 113};

当我这样做时会发生什么

int x = a[3];

????

我一直觉得a[3]的意思是:

">从内存地址arr开始。向前走 3 个内存地址。获取在该内存地址处表示的整数。

但是我对哈希表的工作原理感到困惑。因为如果哈希表被实现为一个"桶"数组(就像教授在本讲座中所说的那样:https://www.youtube.com/watch?v=UPo-M8bzRrc(,那么你仍然必须走到你需要的桶;因此,它们的访问效率并不比阵列高。

有人可以为我澄清这一点吗?

把记忆想象成一个大的两列表:

+---------+-------+
| ADDRESS | VALUE |
+---------+-------+
|     ... |   ... |
+---------+-------+
|     100 |    69 |  <-- &arr[0] is 100
+---------+-------+
|     101 |     1 |
+---------+-------+
|     102 |    12 |
+---------+-------+
|     103 |    10 |  <-- &arr[3] is 103
+---------+-------+
|     104 |    20 |
+---------+-------+
|     105 |   113 |
+---------+-------+
|     ... |   ... |
+---------+-------+

我想强调的是,这是一个高度简化的模型,但它应该让你了解正在发生的事情。您的计算机知道您的阵列从地址 100 开始。而且,由于给定数组中的所有元素大小相同,因此您可以通过在起始地址中添加 +3 来轻松访问数组的第三个元素。计算机不需要"行走"到数组的第三个元素,它只需抓取存储在内存中地址 100 + 3 的值。

如果要查看此操作的示例,请编译并运行以下代码:

#include <iostream>
using namespace std;
int main() {
    int a[] = { 1, 2, 3 };
    cout << "Address of a:tt" << &a[0] << endl;
    cout << "Address of a[2]:t" << &a[2] << endl;
    return 0;
}

记下 a 的地址。假设您的计算机使用的是 32 位整数,您应该看到 a[2] 的地址只是 a + 2*4 的地址。它添加 2*4 而不仅仅是 2 的原因是因为每个整数实际上使用 4 个字节的内存(即单个值将跨越 4 个地址(。

int x = a[3];

硬件确实(地址为a(+(3*sizeof(int((

这是一个标准的索引操作,通常可以使用专用硬件在一个步骤中完成此操作。

如果你写这样的东西:

int x = a[3];

然后编译器在编译时知道在哪里寻找变量,因此它可以在编译时设置相对和精确的内存位置。处理器不需要计算变量在内存中的位置。

"从内存地址到达开始,向前走 3 个内存地址。获取 在该内存地址处表示的整数。

所以基本上,这是真的,但这样写只是出于教育目的。在这种情况下,处理器不会这样做。

通过哈希表访问元素时,将根据键计算哈希值。许多键可能会导致相同的哈希值。因此,必须有一个位置存储许多具有相同哈希值的对象,该位置称为存储桶。因为一个存储桶中可能有很多对象,所以必须搜索所有对象才能找到您要查找的值,但这仍然比将所有值存储在数组中(您必须遍历其所有元素(要快得多。

这本质上就是数组访问的工作方式,它非常快。哈希表并不比数组快;事实上,正是因为它们接近与数组一样快的速度,所以它们被认为是非常快的。哈希表的主要优点是可以使用任何可哈希类型作为键,而不仅仅是整数。此外,它们支持稀疏数据,中间没有一堆浪费的数组空间。

它们的访问效率并不比阵列高。

这并没有说太多,因为数组的速度快得令人眼花缭乱。索引数组(即从一个对象到该数组中的随机另一个对象(是 O(1( - 单个加法操作。大多数处理器甚至有专门的指令,用于以各种形式索引到数组和子对象中,这些指令可以做得更好。

处理器不会在途中遍历每个地址 - 它会跳过它们,无论有多少个地址。"与阵列访问一样高效"确实是高度赞扬。