Upgrade to Pro — share decks privately, control downloads, hide ads and more …

C言語を知らない人がびっくりしそうなC言語の特徴 ~変数定義編~

C言語を知らない人がびっくりしそうなC言語の特徴 ~変数定義編~

以下動画のテキストです。
https://youtu.be/g7ZcZTkOAl4

Satoru Takeuchi

January 17, 2023
Tweet

More Decks by Satoru Takeuchi

Other Decks in Technology

Transcript

  1. C言語を知らない人がびっくりしそうな
    C言語の特徴
    ~変数定義編~
    Jan. 17th, 2023
    Satoru Takeuchi
    twitter: satoru_takeuchi

    View full-size slide

  2. はじめに
    ● 想定聴衆
    ○ 「C言語は聞いたことはあるがどんなものか知らない」という人
    ○ メモリ管理を自分でしなくていいプログラミング言語を使っている人
    ■ ここ十数年で生まれた言語はだいたいそう
    ● はなすこと
    ○ 想定聴衆に向けてC言語の特徴を紹介
    ○ 今回は変数定義について
    ● 環境
    ○ OS: Ubuntu 20.04/x86_64
    ○ gcc: Ubuntu 9.4.0-1ubuntu1~20.04.1

    View full-size slide

  3. 変数定義時の振る舞い
    ● ほとんどの人が思い浮かべる挙動
    ○ デフォルトの値に初期化される
    ● C言語の挙動
    ○ 変数を定義するだけでは何が入っているかは未定義
    ○ 別の関数を呼び出したときに定義した別のデータの残骸がのこっているかも

    View full-size slide

  4. サンプルコード1
    1. main()の中で変数iを定義
    2. iの値を表示
    ● 実行結果はどうなる?
    ○ 直観的には0を表示
    #include
    int main(void) {
    int i;
    printf("%d\n", i);
    }

    View full-size slide

  5. サンプルコード1の実行結果
    ● 0が表示された
    ○ C言語としてiを0に初期化しているのではなく、「たまたまこうなった」だけ
    #include
    int main(void) {
    int i;
    printf("%d\n", i);
    }

    View full-size slide

  6. サンプルコード2
    1. foo()を呼ぶ
    1.1. 変数iを定義して100で初期化
    2. bar()を呼ぶ
    2.1. 変数iを定義して初期化はしない
    2.2. iの値を表示
    ● 実行結果はどうなる?
    ○ 現代的な言語なら0になりそう
    #include
    void foo(void) {
    int i = 100;
    }
    void bar(void) {
    int i;
    printf("%d\n", i);
    }
    int main(void) {
    foo();
    bar();
    }

    View full-size slide

  7. サンプルコード2の実行結果
    ● 初期化してないbar()内の変数iの値が100
    #include
    void foo(void) {
    int i = 100;
    }
    void bar(void) {
    int i;
    printf("%d\n", i);
    }
    int main(void) {
    foo();
    bar();
    }

    View full-size slide

  8. 挙動の説明
    1. 前提知識となるスタックの説明
    2. サンプルコード2の挙動の説明

    View full-size slide

  9. スタックとは
    ● プログラムの関数呼び出しの流れとローカル変数を管理するしくみ
    ● データ構造はスタック(であるがゆえにスタックという名前がついている
    ● 関数を呼び出すたびにローカル変数を含むスタックフレームというものをpushする
    というイメージ
    ● 一般にメモリアドレスの大きな方向から小さな方向に伸びる

    View full-size slide

  10. スタックの説明(1/4)
    #include
    void bar() {
    int i = 100;
    }
    void foo() {
    int i = 10;
    bar();
    }
    void main() {
    int i = 1;
    foo();
    }
    メモリ
    スタック


    いろいろ
    mainのiの値(1)
    0
    大きい
    アドレス
    📝 戻りアドレスとか
    フレームポインタとか

    View full-size slide

  11. スタックの説明(2/4)
    #include
    void bar() {
    int i = 100;
    }
    void foo() {
    int i = 10;
    bar();
    }
    void main() {
    int i = 1;
    foo();
    }
    メモリ
    スタック


    いろいろ
    mainのiの値(1)
    0
    大きい
    アドレス
    fooのいろいろ
    fooのiの値(10)
    push

    View full-size slide

  12. スタックの説明(3/4)
    #include
    void bar() {
    int i = 100;
    }
    void foo() {
    int i = 10;
    bar();
    }
    void main() {
    int i = 1;
    foo();
    }
    メモリ
    スタック


    いろいろ
    mainのiの値(1)
    0
    大きい
    アドレス
    fooのいろいろ
    fooのiの値(10)
    barのいろいろ
    barのiの値(100)
    push

    View full-size slide

  13. スタックの説明(3/4)
    #include
    void bar() {
    int i = 100;
    }
    void foo() {
    int i = 10;
    bar();
    }
    void main() {
    int i = 1;
    foo();
    }
    メモリ
    スタック


    いろいろ
    mainのiの値(1)
    0
    大きい
    アドレス
    fooのいろいろ
    fooのiの値(10)
    ゴミ
    ゴミ(100)
    pop

    View full-size slide

  14. スタックの説明(3/4)
    #include
    void bar() {
    int i = 100;
    }
    void foo() {
    int i = 10;
    bar();
    }
    void main() {
    int i = 1;
    foo();
    }
    メモリ
    スタック


    いろいろ
    mainのiの値(1)
    0
    大きい
    アドレス
    ゴミ
    ゴミ(10)
    ゴミ
    ゴミ(100)
    pop

    View full-size slide

  15. サンプルコード2の挙動の説明
    #include
    void foo(void) {
    int i = 100;
    }
    void bar(void) {
    int i;
    printf("%d\n", i);
    }
    int main(void) {
    foo();
    bar();
    }
    メモリ
    スタック


    いろいろ
    0
    大きい
    アドレス

    View full-size slide

  16. サンプルコード2の挙動の説明
    #include
    void foo(void) {
    int i = 100;
    }
    void bar(void) {
    int i;
    printf("%d\n", i);
    }
    int main(void) {
    foo();
    bar();
    }
    メモリ
    スタック


    いろいろ
    0
    大きい
    アドレス
    fooのいろいろ
    fooのiの値(100)
    push

    View full-size slide

  17. サンプルコード2の挙動の説明
    #include
    void foo(void) {
    int i = 100;
    }
    void bar(void) {
    int i;
    printf("%d\n", i);
    }
    int main(void) {
    foo();
    bar();
    }
    メモリ
    スタック


    いろいろ
    0
    大きい
    アドレス
    ゴミ
    ゴミ(100)
    pop

    View full-size slide

  18. サンプルコード2の挙動の説明
    #include
    void foo(void) {
    int i = 100;
    }
    void bar(void) {
    int i;
    printf("%d\n", i);
    }
    int main(void) {
    foo();
    bar();
    }
    メモリ
    スタック


    いろいろ
    0
    大きい
    アドレス
    barのいろいろ
    barのi(100)
    push 初期化していないので
    fooのゴミが残っている!

    View full-size slide

  19. まとめ
    ● C言語では変数定義時に初期化しないと、どんな値が入っているかは未定義
    ○ 現代的な言語のように必ず初期化するより高速だが危なっかしい
    ○ 変なデータが見えてしまってセキュリティホールになることも
    ● あくまで「未定義」なので、環境によって結果は異なる

    View full-size slide