Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

サンプルコード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(); }

Slide 7

Slide 7 text

サンプルコード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(); }

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

スタックの説明(1/4) #include void bar() { int i = 100; } void foo() { int i = 10; bar(); } void main() { int i = 1; foo(); } メモリ スタック … … いろいろ mainのiの値(1) 0 大きい アドレス 📝 戻りアドレスとか フレームポインタとか

Slide 11

Slide 11 text

スタックの説明(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

Slide 12

Slide 12 text

スタックの説明(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

Slide 13

Slide 13 text

スタックの説明(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

Slide 14

Slide 14 text

スタックの説明(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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

サンプルコード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

Slide 17

Slide 17 text

サンプルコード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

Slide 18

Slide 18 text

サンプルコード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のゴミが残っている!

Slide 19

Slide 19 text

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