📑

Rustにおける構造体のデータ格納場所分析

に公開

概要

このページで「String構造体もスタックに入りますが、ヒープに入るデータの参照アドレスが一つ入ります。」とあった。
分析することで確認する。

GDBを用いて分析する。
筆者はWindows11においてWSLを用いてUbuntu24.04を使用している。
2025年11月1日現在、筆者の環境ではGDBは以下のコマンドでインストールできる。

sudo apt install gdb

分析

本稿では、前述のページにあるサンプルコードに対して分析を行う。リンク切れをしたときに備えて、対象にするソースコードを示しておく。
以下の文章では、ソースコードといったら、以下のRustコードのことを指すものとする。

struct SeaCreature {
    animal_type: String,
    name: String,
    arms: i32,
    legs: i32,
    weapon: String,
}

fn main() {
    // SeaCreatureのデータはスタックに入ります。
    let ferris = SeaCreature {
        // String構造体もスタックに入りますが、
        // ヒープに入るデータの参照アドレスが一つ入ります。
        animal_type: String::from("crab"),
        name: String::from("Ferris"),
        arms: 2,
        legs: 4,
        weapon: String::from("claw"),
    };

    let sarah = SeaCreature {
        animal_type: String::from("octopus"),
        name: String::from("Sarah"),
        arms: 8,
        legs: 0,
        weapon: String::from("none"),
    };
    
    println!(
        "{} is a {}. They have {} arms, {} legs, and a {} weapon",
        ferris.name, ferris.animal_type, ferris.arms, ferris.legs, ferris.weapon
    );
    println!(
        "{} is a {}. They have {} arms, and {} legs. They have no weapon..",
        sarah.name, sarah.animal_type, sarah.arms, sarah.legs
    );
}

構造体のインスタンスとなる変数の場所

ソースコードにおける、main関数の中で定義されているferrissarahSeaCreature構造体のインスタンスである。
これはメモリ上どこにあるのだろうか?

GDBを起動する。

構造体がインスタンス化された後の行にブレイクポイントを配置する。

プログラムを実行する

変数ferrisと変数sarahがどこにあるのかを調査する。

変数ferrisは0x7fffffffd500、変数sarahは0x7fffffffd5a0にある。

gdbのinfo proc mappingsを実行すると、どこからどこまでが何の領域なのかが見れる。

ここで変数ferrisとsarahがどのメモリ領域にあるのか見てみる。

このマップには以下の様に記述されている。

0x7ffffffdd000     0x7ffffffff000    0x22000        0x0  rw-p   [stack]

つまり、0x7ffffffdd000から0x7ffffffff000がスタック領域のメモリアドレスであるという意味になる。

String構造体の場所

SeaCreature構造体の中にある各フィールドは、以下の通り確かにスタック領域を指している。

String構造体に入る実データの場所

String構造体の中は以下の通りである。
前の章より、変数ferrisにおけるSeaCreature構造体の各フィールドはすべてスタック領域にあった。
問題の「String構造体もスタックに入りますが、ヒープに入るデータの参照アドレスが一つ入ります。」という文章に関して。
前の章の結果から「String構造体もスタックに入りますが」は正しいといえる。
次は「ヒープに入るデータの参照アドレスが一つ入ります。」を検証する。
変数ferrisの中身を見る。

そうすると、String構造体ではないarmsやlegsなどのプリミティブ型は直にデータを確認することが可能だが、String構造体には"crab"や"Ferris"などが確認できない。
一方でString構造体を格納しているフィールドにはpointerという部分が確認できる。
nameフィールドを例にとると、pointerとなっている部分は、0x5555555b2d20になっている。

メモリのマップを再度見てみると、0x5555555b2000から0x5555555d3000がヒープ領域になっている。

0x5555555b2000 < 0x5555555b2d20 < 0x5555555d3000 であるから。nameフィールドに格納されているpointer部分の0x5555555b2d20というアドレスはヒープ領域にある。

本当にあるか見てみよう。

x/s [アドレス]は指定した[アドレス]にあるデータをstring(文字列)として解釈して表示せよ」という命令。
ここでは、構造体SeaCreatureのインスタンスである変数ferrisのnameフィールドの中身は"Ferris"となっているはずであり、実行結果も確かに"Ferris"となっていた。
他のフィールドも同様である。

結論

構造体はスタック上に確かに存在しヒープ領域を指す「参照アドレス」を確かに持っている。
そのヒープ領域には実データが格納されていた。

Discussion