可以在 64 位机器上使用 4 位、8 位、16 位或 32 位大小的指针吗?

可以在 64 位机器上使用 4 位、8 位、16 位或 32 位大小的指针吗?

我只是粗略地计算了一下无符号 64 位整数的最大大小,即:

18,446,744,073,709,551,615
q5 q4  t   b   m   t   h

看着AWS 的硬件规格在他们最大的机器上,它达到3,904GB

3,904,000,000,000,000,000 bytes
5 q4  t   b   m   t   h

对我来说这意味着指针存储为 64 位整数。我对内存和指针的思考还很陌生,但我只是想澄清一下。

但我还是有点困惑。指针是一种“编程语言构造”。因此从技术上讲,即使在 64 位机器上,如果你只使用少于约40亿整数(最大整数大小为 32 位),那么我想知道为什么你不能只让指针为 32 位。这样指针就是 32 位,直到空间用完,然后你可以开始使用 64 位指针。然后它会给你更多的空间来容纳更多的对象。

但还是很困惑。指针保存的是内存中的地址。它说“地址”是 64 位。因此,如果我们要让 32 位指针指向 64 位内存中的 32 位块,我不确定这会是什么样子或意味着什么。这似乎意味着你必须这样做偏移量(尽管我不太理解这一点)。

想知道是否有人可以用 C、汇编或 JavaScript 演示如何将 32 位指针存储在 64 位地址空间中。如果 C 可以自动处理,那么汇编又是如何做到的。


我想知道如何使用像上面那样的大内存,但存储 32 位指针,直到达到最大值,然后使用 64 位指针,但不确定具体是什么样子。我将尝试绘制一个图表来解释我的想法。

  | The bars and . are like a ruler and mark the bit positions.
  - Each row under a number (1, 2, 3, ...) means a memory address.
  ⬚ Means no data in memory address.
  ⬒ Means data of type 1 in memory address.
  ■ Means data of type 2 in memory address.
  ● Means a bit of integer pointer is plugged into memory address slot.
  ◌ Means no bit of integer pointer is plugged into memory address slot.
                                                                                                                                 |
                                                                 |                                                               |
                                 |                               |                               |                               |
                 |               |               |               |               |               |               |               |
         |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |
   . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |

1. Empty 64-bit memory.
   ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌
   ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚
   ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ...
   ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ⬚ ...
   ...
   ...

2. 64-bit memory filled with 32-bit pieces of data (type 1 ⬒, type 2 ■).
   ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌
   ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
   ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ...
   ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ...
   ...
   ...

3. 64-bit memory filled with 64-bit pieces of data.
   ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌
   ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
   ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ...
   ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ...
   ...
   ...

4. 64-bit memory filled with 4-bit pieces of data.
   ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌
   ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒
   ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ...
   ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ...
   ...
   ...

5. 64-bit memory filled with 32-bit pieces of data, with second 32-bits accessed by a 32-bit pointer.
   ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ●
   ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ 
   ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ...
   ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ... 
   ...
   ...

6. 64-bit memory filled with 64-bit pieces of data, with second 64-bits accessed by a 64-bit pointer.
   ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌
   ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
   ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ...
   ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ...
   ...
   ...

7. 64-bit memory filled with 4-bit pieces of data, with second piece of data accessed by a pointer.
   ◌ ◌ ◌ ◌ ● ● ● ● ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌
   ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒
   ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ...
   ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ⬒ ⬒ ...
   ...
   ...

8. 64-bit memory filled with 8-bit pieces of data, with second piece of data accessed by a pointer.
   ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ● ● ● ● ● ● ● ● ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌
   ■ ■ ■ ■ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ 
   ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ◌ ...
   ■ ■ ■ ■ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ■ ■ ■ ■ ■ ■ ■ ■ ⬒ ⬒ ⬒ ⬒ ⬒ ⬒ ...
   ...
   ...

我想象整数就像锁(即内存地址)的钥匙。空的钥匙孔看起来就像 (1) 中一排 64 个 ◌。64 位地址的完整钥匙孔看起来就像 (6) 中一排 64 个 ●。如果我给 64 位内存地址空间一个32 位钥匙,它看起来就像 (5)。所以它不会完全填满 64 位长(64-◌ 长)的钥匙孔,它只会填充(在这种情况下)它的后半部分。所以它似乎与地址不匹配。但我试图指向后半部分的 32 位数据!为了匹配地址,似乎您必须在完整的 64 位行中填充钥匙孔,如 (6)。我想知道我的理解是否在这里搞错了,请让我知道我哪里错了。

如果不清楚的话,图表中第一个数字 1-4 表示位于内存中的数据(1 表示空内存)。第二个数字 5-8 表示我们试图使用权使用指针的数据(连续的黑色圆圈●是指向内存地址锁的指针/钥匙)。

最后,我还有最后一个问题。我想知道你是否可以更进一步,将数据存储在更小的块中。例如存储 4 位数据,如 (7)。这只是为了更详细地演示指针/地址系统的工作原理。我不知道你是否可以有一个4位指针指向 4 位内存块。由于对齐要求,这似乎意味着您最终每次至少会获取 8 位。但没关系。我只是想确保是否可以使用 n 位指针访问 64 位内存空间中的 n 位数据。

如果是这样,那么看起来会怎么样呢,无论是用 C 语言还是汇编语言,或者 JavaScript 都可以工作。

我想知道这一点是为了知道您应该如何在 64 位内存中存储数据,以及在“内存地址为 64 位”的情况下,您可以使用指针做什么。也就是说,如果我可以做到memory.get(a32BitPointer)并让它从 32 位对齐的内存槽返回 32 位数据。(或者等效地,4、8、16 等位数据或大小指针)。

答案1

指针指向 包含绝对地址。

如果你需要在使用指针之前添加一个值,那么你得到的就是抵消,不是真正的指针。

在 C 语言中,空指针可以是函数指针,例如,您可以通过它调用函数。如果 CPU 处于 64 位模式,则需要全部 64 位才能实现该功能。

如果您的 CPU 支持 64 条地址线(物理上可能更少),那么它的地址空间为 2^64,范围0x1 0000 0000 0000 00000x0000 0000 0000 00000xFFFF FFFF FFFF FFFF

如果您希望您的指针可以被 CPU 指令使用,而不需要额外的 CPU 指令来找出您的真正含义(本机 CPU 代码可以直接处理指针),那么它必须与 CPU 的地址空间一样宽。

偏移量较慢,因为 CPU 必须添加才能获得所需的地址,尽管 CPU 也有执行此操作的本机指令。

我并不是 x86-64 ISA 的超级专家,但可能存在将 32 位值视为 64 位值的 CPU 指令,其中前 32 位假定为 0。CPU 仍然必须在内部将实际值“扩展”为 64 位。

在 x86 和 x86-64 中,你当然可以使用 8、16、32 和 64 位偏移量(没有 x86/x86 CPU 指令只适用于 4 位值)

答案2

首先,3904GB内存仅需要42 位地址。它仅由3 904 000 000 000字节组成,而不是您计算的内容。可以使用 PowerShell 快速检查

PS C:\> [math]::Log(3904GB, 2) # GB base 2, or GiB
41.9307373375629
PS C:\> [math]::Log(3904e9, 2) # GB base 10
41.8280901915491

因此从技术上讲,即使在 64 位机器上,如果您仅使用少于约 40 亿个整数(32 位最大整数大小),那么我想知道为什么不能只将指针设置为 32 位。这样,指针就是 32 位,直到空间用完,然后您就可以开始使用 64 位指针。然后它会为您提供更多空间来容纳更多对象。

x32 ABI64 位 x86 ABI使用 32 位指针。进程只有 32 位地址空间,这意味着它们不能使用超过 4GB 的内存(对于大多数用户应用程序来说没有问题),但它们将能够利用更大更宽的寄存器空间。全局内存空间仍然是 64 位,因为它在硬件中是固定的,因此 32 位指针将用作进程基址的偏移量,而不是直接指针。实现方式如下

void* getRealPointer(uintptr_t base_addr, uint32_t ptr)
{
    // value stored in pointer is the offset/distance from base
    return (void*)(base_addr + ptr);
}

这种技术在许多其他 64 位 RISC 架构中也很常见,例如 Sparc(为什么 sparc64 架构上的 Linux 在用户空间中使用 32 位指针,而在内核空间中使用 64 位指针?)、MIPS 或 PowerPC,因为在过渡到 64 位时,它们并没有像 x86 和 ARM 那样增加寄存器的数量,这意味着 32 位进程可能比 64 位进程更快,除非它需要大量的 64 位数学运算或超过 2/3/4GB 的 RAM

在 64 位处理器(例如 G5)上,Debian PPC 使用 64 位内核和 32 位用户空间。这是因为 64 位 PowerPC 处理器没有像 Intel 64/AMD64 架构那样的“32 位模式”。因此,不需要 64 位数学函数的 64 位 PowerPC 程序的运行速度会比 32 位程序慢一些,因为 64 位指针和长整数消耗的内存是 32 位的两倍,填充 CPU 缓存的速度更快,因此需要更频繁地访问内存。

PowerPC 上的 Linux

尽管如此,你不能只使用 32 位指针until you run out of space then start using 64-bit pointers,这没有意义。类型总是有固定的大小。如果你只为指针的 32 位保留空间,那么当你需要使用 64 位指针时会发生什么?你会把高位部分存储在哪里?


因此,如果我们要让 32 位指针指向 64 位内存中的 32 位块,我不确定这会是什么样子或意味着什么

这就是所谓的字寻址存储器。现在每个值不再指向每个字节,而是指向不同的单词

我们可以更容易地想象记忆是由一系列线性的细胞这些 ID 由唯一 ID 标识。这些 ID 就是我们通常所说的“地址”,存储在指针中。在现代系统上,单元大小通常为 1 字节(即字节寻址内存)。然而许多老系统,如 Unisys 或 PDP,都使用字寻址内存一个单元包含一个字(在这些架构中为 36 位长)。因此在这些系统中char*会比int*你需要更多的位来存储你想要寻址的字节的位置更大

我不太明白您的图表,但人们很少需要这样寻址每个位,因为这显然会减少我们可以寻址的总内存。虽然公平地说,确实存在一些具有位寻址内存的架构,主要是嵌入式系统。当您为 CPU 提供 32 位地址时,看起来您想要 64 位值的低 32 位,但事实并非如此。要寻址每一半,您需要一个以上的有效位,而不是像那样需要一半的位数。原理很简单:如果我们使用更大的单元大小,那么在相同数量的内存上,需要的单元更少,这意味着需要更少的 ID 位;反之亦然。在硬件级别,单元大小通常是固定的。

以下是内存中前 16 个字节的示例

╔══════╤══════╤══════╤══════╤══════╤══════╤══════╤══════╤══════╤══════╤══════╤══════╤══════╤══════╤══════╤══════╗
║ 0000 │ 0001 │ 0010 │ 0011 │ 0100 │ 0101 │ 0110 │ 0111 │ 1000 │ 1001 │ 1010 │ 1011 │ 1100 │ 1101 │ 1110 │ 1111 ║
╠══════╪══════╪══════╪══════╪══════╪══════╪══════╪══════╪══════╪══════╪══════╪══════╪══════╪══════╪══════╪══════╣
║ b0   │ b1   │ b2   │ b3   │ b4   │ b5   │ b6   │ b7   │ b8   │ b9   │ b10  │ b11  │ b12  │ b13  │ b14  │ b15  ║
╟──────┴──────┼──────┴──────┼──────┴──────┼──────┴──────┼──────┴──────┼──────┴──────┼──────┴──────┼──────┴──────╢
║ w0 000      │ w1 001      │ w2 010      │ w3 011      │ w4 100      │ w5 101      │ w6 110      │ w7 111      ║
╟─────────────┴─────────────┼─────────────┴─────────────┼─────────────┴─────────────┼─────────────┴─────────────╢
║ dw0 00                    │ dw1 01                    │ dw2 10                    │ dw3 11                    ║
╟───────────────────────────┴───────────────────────────┼───────────────────────────┴───────────────────────────╢
║ o0                                                    │ o1                                                    ║
╚═══════════════════════════════════════════════════════╧═══════════════════════════════════════════════════════╝

您还可以查看这个答案

如果我们寻址每个 2 字节字,则第 N 个字的字节地址为 N*2。与任何其他块大小相同,其中实际偏移量可以计算为offset*sizeof(chunk)。因此,4 字节对齐地址中的 2 个低位和 8 字节对齐地址中的 3 个低位始终为零。如果您不使用字寻址指针,那么这些低位可用于存储数据这被称为标记指针

64 位 JVM 使用此技术压缩 哎呀. 请参阅JVM 压缩 Oops 背后的技巧Java中的对象总是与8字节对齐,因此它们可以用32位地址寻址8*4 = 32GB的内存。

Java 堆中的托管指针指向与 8 字节地址边界对齐的对象。压缩 oop 将托管指针(在 JVM 软件中的许多地方,但不是所有地方)表示为距离 64 位 Java 堆基地址的 32 位对象偏移量。由于它们是对象偏移量而不是字节偏移量,因此它们可用于寻址最多 40 亿个对象(不是字节),或最多约 32 GB 的堆大小。要使用它们,必须将它们按 8 倍缩放并添加到 Java 堆基地址以找到它们引用的对象。使用压缩 oop 的对象大小与 ILP32 模式下的对象大小相当。

https://docs.oracle.com/javase/7/docs/technotes/guides/vm/performance-enhancements-7.html#compressedOop

最近,谷歌也将同样的技术应用于其 V8 渲染引擎,并使用64 位 V8 中的 32 位指针,减少内存占用约 35%

大多数 RISC 架构上的分支和加载/存储指令也将字地址存储在立即数部分,因为没有必要浪费宝贵的空间来保存那些始终为零的位。例如MIPS 分支和跳转指令:日本航空、J、BEQ、BLEZ、BGTZ...

相关内容