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

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

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

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

Satoru Takeuchi
PRO

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

  2. はじめに • 想定聴衆 ◦ 「C言語は聞いたことはあるがどんなものか知らない」という人 ◦ メモリ管理を自分でしなくていいプログラミング言語を使っている人 ▪ ここ十数年で生まれた言語はだいたいそう •

    はなすこと ◦ 想定聴衆に向けてC言語の特徴を紹介 ◦ 今回は変数定義について • 環境 ◦ OS: Ubuntu 20.04/x86_64 ◦ gcc: Ubuntu 9.4.0-1ubuntu1~20.04.1
  3. 変数定義時の振る舞い • ほとんどの人が思い浮かべる挙動 ◦ デフォルトの値に初期化される • C言語の挙動 ◦ 変数を定義するだけでは何が入っているかは未定義 ◦

    別の関数を呼び出したときに定義した別のデータの残骸がのこっているかも
  4. サンプルコード1 1. main()の中で変数iを定義 2. iの値を表示 • 実行結果はどうなる? ◦ 直観的には0を表示 #include

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

    int i; printf("%d\n", i); }
  6. サンプルコード2 1. foo()を呼ぶ 1.1. 変数iを定義して100で初期化 2. bar()を呼ぶ 2.1. 変数iを定義して初期化はしない 2.2.

    iの値を表示 • 実行結果はどうなる? ◦ 現代的な言語なら0になりそう #include <stdio.h> void foo(void) { int i = 100; } void bar(void) { int i; printf("%d\n", i); } int main(void) { foo(); bar(); }
  7. サンプルコード2の実行結果 • 初期化してないbar()内の変数iの値が100 #include <stdio.h> void foo(void) { int i

    = 100; } void bar(void) { int i; printf("%d\n", i); } int main(void) { foo(); bar(); }
  8. 挙動の説明 1. 前提知識となるスタックの説明 2. サンプルコード2の挙動の説明

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

  10. スタックの説明(1/4) #include <stdio.h> void bar() { int i = 100;

    } void foo() { int i = 10; bar(); } void main() { int i = 1; foo(); } メモリ スタック … … いろいろ mainのiの値(1) 0 大きい アドレス 📝 戻りアドレスとか フレームポインタとか
  11. スタックの説明(2/4) #include <stdio.h> 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
  12. スタックの説明(3/4) #include <stdio.h> 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
  13. スタックの説明(3/4) #include <stdio.h> 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
  14. スタックの説明(3/4) #include <stdio.h> void bar() { int i = 100;

    } void foo() { int i = 10; bar(); } void main() { int i = 1; foo(); } メモリ スタック … … いろいろ mainのiの値(1) 0 大きい アドレス ゴミ ゴミ(10) ゴミ ゴミ(100) pop
  15. サンプルコード2の挙動の説明 #include <stdio.h> void foo(void) { int i = 100;

    } void bar(void) { int i; printf("%d\n", i); } int main(void) { foo(); bar(); } メモリ スタック … … いろいろ 0 大きい アドレス
  16. サンプルコード2の挙動の説明 #include <stdio.h> 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
  17. サンプルコード2の挙動の説明 #include <stdio.h> void foo(void) { int i = 100;

    } void bar(void) { int i; printf("%d\n", i); } int main(void) { foo(); bar(); } メモリ スタック … … いろいろ 0 大きい アドレス ゴミ ゴミ(100) pop
  18. サンプルコード2の挙動の説明 #include <stdio.h> 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のゴミが残っている!
  19. まとめ • C言語では変数定義時に初期化しないと、どんな値が入っているかは未定義 ◦ 現代的な言語のように必ず初期化するより高速だが危なっかしい ◦ 変なデータが見えてしまってセキュリティホールになることも • あくまで「未定義」なので、環境によって結果は異なる