一个字符串数组,例如[String;3],是存储在堆栈还是堆上?

6

Rust 书籍中指出:

另一个使栈快速的属性是所有在栈上的数据都必须占用已知的固定大小。

它还表示 String 存储在堆上,因为其大小不确定且可以被修改。

那么类似包含 String 的数组这样的“复合”数据结构存储在哪里? 数组的大小是固定的,但是数组的组成部分的大小可以改变。

let array: [String; 3] = ["A","B","C"];

这样的“组合”数据类型存储规则是什么?
2个回答

26

两者都是。


一个术语的概念:讨论类型的内存布局时,不应该谈论堆栈 vs 堆,而应该谈论内联 vs 离线1

  • 内联表示数据就在这里,
  • 离线表示数据可通过指针访问(无论指针指向何处)。

一个简单的例子,整数是内联存储的:

//  i32
+---+
| 3 |
+---+

一个典型的struct Point { x: i32, y: i32 }也是内联存储的:

//  Point
+---+---+
| x | y |
+---+---+

一个 String,通常表示为 struct String { data: *mut u8, len: usize, cap: usize } ,它既在线上存储,也离线存储:

//   String
+-------+-------+-------+
| data  |  len  |  cap  |
+-------+-------+-------+
    |
     \
      +-------------+
      |Hello, World!|
      +-------------+

内联部分占用了3个指针的存储空间,而离线部分是一个堆分配的记录,它包含字符串"Hello, World!"的内容。

然而,内联并不总是意味着。一个Box<Point>

//  Box<Point>
+-------+
| data  |
+-------+
    |
     \
      +---+---+
      | x | y |
      +---+---+

将其Point(内联存储其数据成员)存储在堆上!

同样地,“离线”并不总是意味着“堆”:

fn main() {
    let i = 3;
    let r = &i;
}

在这里,r 是一个指向 i 的引用(指针),而 i 存储在栈上!

1 是的,我是凭空想象的,更好的术语将不胜感激。


所以,回到问题:

它还说String存储在堆上,因为其大小未知且可变。

这只是一种近似,如上所述,String 的一些数据内联存储(指针、长度和容量),而一些存储在堆上(字符串内容)。

Where are "composite" data structures such as arrays containing String stored? The array is fixed in size however the components of the array can change in size.

let array: [String; 3] = ["A","B","C"];

它同时存储在堆栈和堆上:

//  [String; 3]
+-------+-------+-------+-------+-------+-------+-------+-------+-------+
| data  |  len  |  cap  | data  |  len  |  cap  | data  |  len  |  cap  |
+-------+-------+-------+-------+-------+-------+-------+-------+-------+
    |                       |                       |    
     \                       \                       \
      +-+                     +-+                     +-+
      |A|                     |B|                     |C|
      +-+                     +-+                     +-+

这是 9 个指针大小的内联数据(在此处于栈上),并且在堆上进行了 3 次单独分配。

这种“复合”数据类型存储的规则是什么?

数据成员总是内联的,指针和引用可能指向离线数据,这些数据可能在堆、栈上等等...


一个后续问题是,String数组的“data”组件是存储在一起的还是在内存中的不同位置?也就是说,它是连续的还是不连续的? - Greg
1
@Greg:我不太清楚你在说什么,但看起来你是指那3个字符串的内容(由data指向)。如果是这样的话,它们存储在3个单独的堆分配中,它们是否相邻取决于分配器;肯定没有保证,这就是为什么我将它们分开绘制的原因。 - Matthieu M.
谢谢,那正是我所指的。 - Greg

6

String只是对Vec<u8>进行了包装,因此适用于所有Vecs

Vec在堆栈上具有固定大小:它的长度、容量和指向存储实际内容的堆的指针。

因此,在堆栈上有三个Strings数组意味着这些字符串的“元数据”位于堆栈上(长度、容量和指向数据的指针)。

然而,这些字符串的实际数据存储在堆上,因为它们的长度是可变的,正如你正确地识别出来的那样。


1
此外,这里有一个链接到 Vec 类型保证,描述了它的分配规则。 - ljedrz
我认为这个答案比被选中的那个更加简洁。 - zl9394

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接