| | 43 | |
| | 44 | また、 C 言語は変数や関数の宣言や定義にうるさい言語でもあります。定義されていない名前の関数を呼び出そうとしたり、宣言されていない変数を使用しようとするようなプログラムも、やはりコンパイル時にエラーになります。これらの性質は、多くのスクリプト言語が、定義されていない関数の呼び出しは実行時にエラーになったり、宣言されていない変数への代入は許されていたりするのとは対照的です。 |
| | 45 | |
| | 46 | === 型の扱い === |
| | 47 | |
| | 48 | C 言語そのものには、文字列を扱う型が存在しません。基本的に、整数値と実数値しか扱えません。 |
| | 49 | |
| | 50 | しかし、文字列を扱う方法は存在します。 C 言語は配列をサポートしているので、整数値の羅列を配列に納め、それを文字列として扱うことが可能です。 |
| | 51 | |
| | 52 | 初歩のサンプルプログラムとしてよく用いられる Hello World プログラムを以下に示します。このプログラムは、単に画面に "Hello World!" と表示するだけのものです。 |
| | 53 | |
| | 54 | {{{ |
| | 55 | #include <stdio.h> |
| | 56 | |
| | 57 | int main() |
| | 58 | { |
| | 59 | printf("Hello World!\n"); |
| | 60 | return 0; |
| | 61 | } |
| | 62 | }}} |
| | 63 | |
| | 64 | 文字列リテラルがあるので、「文字列扱えるじゃないか! 嘘をつくな!!」とお怒りかも知れませんが、このプログラムは以下のように書き換えることも可能です[[FootNote("ターミナルが ASCII 文字セット (をサブセットにする文字セットすべて) で動作している必要はありますが、そうでない端末を探す方が難しいでしょう…。")]]。 |
| | 65 | |
| | 66 | {{{ |
| | 67 | #include <stdio.h> |
| | 68 | |
| | 69 | int main() |
| | 70 | { |
| | 71 | char text[] = { 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 10, 0 }; |
| | 72 | printf(text); |
| | 73 | return 0; |
| | 74 | } |
| | 75 | }}} |
| | 76 | |
| | 77 | 配列 text は単なる数値の羅列の筈なのに、関数 printf() に渡すとそれが文字列として表示される。これは、コンピュータにおける文字列データが本質的には文字に割り当てられた番号 (文字コード) の羅列に過ぎないことを表しています。 |
| | 78 | |
| | 79 | "char" というのは整数を表す型の一種で、文字コードを扱うのに最適な精度の整数を扱うものです。上記のサンプルのように、行頭に型名を書き、その後ろに変数名を書くことで、使用する変数を'''宣言'''します。 C 言語では、変数はどこかであらかじめ宣言しなければ使うことができません。また、宣言する際に変数の型を決定する必要があり、一度宣言した変数の型を変更することはできません。上記のサンプルの場合、 text は char 型の配列として宣言されたので、 char 型の配列以外の種類のデータを持つことはできないのです。 |
| | 80 | |
| | 81 | なお、 C 言語は配列の他に、データ構造を表現する'''構造体'''や'''共用体'''、名前付きの列挙値を表現する'''列挙型'''、変数や関数が存在するアドレス値を扱う'''ポインタ'''をサポートしています。 |
| | 82 | |
| | 83 | === アセンブリ言語との関係 === |
| | 84 | |
| | 85 | C 言語のことを、アセンブリ言語の (割と単純な) ラッパーマクロであるという人もいます。実際の所、 C 言語のプログラム中に、アセンブリ言語のプログラムを埋め込むことができる処理系もあります。例えばコンパイラに GCC を使用する場合、以下のように !__asm__ キーワードを用いたインライン構文により、アセンブリ言語のプログラムを埋め込むことができます。 |
| | 86 | |
| | 87 | {{{ |
| | 88 | #include <stdio.h> |
| | 89 | |
| | 90 | int main() |
| | 91 | { |
| | 92 | int a = 10; |
| | 93 | int b; |
| | 94 | |
| | 95 | __asm__( |
| | 96 | "movl %1, %%eax\n\t" |
| | 97 | "addl $100, %%eax\n\t" |
| | 98 | "movl %%eax, %0" |
| | 99 | :"=r"(a) |
| | 100 | :"r"(a) |
| | 101 | :"%eax" |
| | 102 | ); |
| | 103 | |
| | 104 | b = a * 20; |
| | 105 | |
| | 106 | printf("b = %d\n", b); |
| | 107 | |
| | 108 | return 0; |
| | 109 | } |
| | 110 | }}} |
| | 111 | |
| | 112 | このプログラムを実行すると、以下のように表示されます。 b の値は 200 ではなく、 2200 になっています。 |
| | 113 | |
| | 114 | {{{ |
| | 115 | b = 2200 |
| | 116 | }}} |
| | 117 | |
| | 118 | 何故そうなるかというと、埋め込んだアセンブリ言語のプログラム中で、変数 a に 100 を加算しているからです。 |
| | 119 | |
| | 120 | 逆に、このプログラムをアセンブリ言語に変換すると、以下のようになります[[FootNote("アセンブリ言語プログラムの生成に利用した環境は、一般的な Intel CPU を搭載した Windows XP パソコンと、 MinGW 版 GCC 4.5.0 です。 gcc -O2 -S hoge.c として生成しています。")]]。 |
| | 121 | |
| | 122 | {{{ |
| | 123 | .file "hoge.c" |
| | 124 | .def ___main; .scl 2; .type 32; .endef |
| | 125 | .section .rdata,"dr" |
| | 126 | LC0: |
| | 127 | .ascii "b = %d\12\0" |
| | 128 | .text |
| | 129 | .p2align 2,,3 |
| | 130 | .globl _main |
| | 131 | .def _main; .scl 2; .type 32; .endef |
| | 132 | _main: |
| | 133 | pushl %ebp |
| | 134 | movl %esp, %ebp |
| | 135 | andl $-16, %esp |
| | 136 | subl $16, %esp |
| | 137 | call ___main |
| | 138 | movl $10, %edx |
| | 139 | /APP |
| | 140 | # 8 "hoge.c" 1 |
| | 141 | movl %edx, %eax |
| | 142 | addl $100, %eax |
| | 143 | movl %eax, %edx |
| | 144 | # 0 "" 2 |
| | 145 | /NO_APP |
| | 146 | leal (%edx,%edx,4), %eax |
| | 147 | sall $2, %eax |
| | 148 | movl %eax, 4(%esp) |
| | 149 | movl $LC0, (%esp) |
| | 150 | call _printf |
| | 151 | xorl %eax, %eax |
| | 152 | leave |
| | 153 | ret |
| | 154 | .def _printf; .scl 2; .type 32; .endef |
| | 155 | }}} |
| | 156 | |
| | 157 | "/APP" と書かれた行から "/NO_APP" と書かれた行までの間の部分が、インライン構文によって埋め込んだアセンブリ言語の部分で、その前後が C 言語で書かれたプログラムをアセンブリ言語に変換したものです。特に、 C 言語のプログラムにおける以下の計算式 |
| | 158 | |
| | 159 | {{{ |
| | 160 | b = a * 20; |
| | 161 | }}} |
| | 162 | |
| | 163 | が、アセンブリ言語では以下のような記述に変換されていますが、 |
| | 164 | |
| | 165 | {{{ |
| | 166 | leal (%edx,%edx,4), %eax |
| | 167 | sall $2, %eax |
| | 168 | }}} |
| | 169 | |
| | 170 | これは、変数 a の値に、変数 a の値を 4回足したもの (従って、 a の 5倍の値に相当) を用意し、その値をビットシフト演算によって左に 2bits 移動する、という内容です。左方向へのビットシフト演算は 1bit 移動する毎に整数値は 2倍になりますので、結果として a の 5倍の 4倍、即ち 20倍の値を得ることができる、というわけです。 |
| | 171 | |
| | 172 | このアセンブリ言語を生成した環境である 80x86 系の CPU には、整数の掛け算を行うマシン語の命令 imull というのも存在するので、この部分のアセンブリ言語プログラムは以下のようにも書き表せるはずなのですが、 |
| | 173 | |
| | 174 | {{{ |
| | 175 | movl %edx, %eax |
| | 176 | imull $20, %eax |
| | 177 | }}} |
| | 178 | |
| | 179 | 観念的に分かりやすいのは C 言語の中だけでよく、変換されたマシン語においては、分かりやすい表現よりも動作効率や実行ファイルサイズの小ささの方が重要になります。 GCC は imull 命令を用いるより leal 命令と sall 命令を用いた方が動作が速くなると判断し、そのようなアセンブリ言語プログラムを生成したのでした。 |
| | 180 | |