Changes between Version 7 and Version 8 of HowTo/CTutorial


Ignore:
Timestamp:
Sep 5, 2010, 1:45:44 PM (14 years ago)
Author:
村山 俊之
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • HowTo/CTutorial

    v7 v8  
    4141
    4242プログラム・ソースを読み込みながら逐次実行するインタプリタ言語 (BASIC など) と比べると、コンパイラ言語はその特徴として、文法的に完結していることがわかりやすいルールになっている、ということが上げられます。 C 言語の場合、関数などの処理の単位はブレース "{" ~ "}" で括られた'''ブロック'''として表現されます。開きブレースと閉じブレースの数が合わなければ、当然文法エラーと見なされ、コンパイルを行う時点でエラーとしてはじかれます。
     43
     44また、 C 言語は変数や関数の宣言や定義にうるさい言語でもあります。定義されていない名前の関数を呼び出そうとしたり、宣言されていない変数を使用しようとするようなプログラムも、やはりコンパイル時にエラーになります。これらの性質は、多くのスクリプト言語が、定義されていない関数の呼び出しは実行時にエラーになったり、宣言されていない変数への代入は許されていたりするのとは対照的です。
     45
     46=== 型の扱い ===
     47
     48C 言語そのものには、文字列を扱う型が存在しません。基本的に、整数値と実数値しか扱えません。
     49
     50しかし、文字列を扱う方法は存在します。 C 言語は配列をサポートしているので、整数値の羅列を配列に納め、それを文字列として扱うことが可能です。
     51
     52初歩のサンプルプログラムとしてよく用いられる Hello World プログラムを以下に示します。このプログラムは、単に画面に "Hello World!" と表示するだけのものです。
     53
     54{{{
     55#include <stdio.h>
     56
     57int main()
     58{
     59    printf("Hello World!\n");
     60    return 0;
     61}
     62}}}
     63
     64文字列リテラルがあるので、「文字列扱えるじゃないか! 嘘をつくな!!」とお怒りかも知れませんが、このプログラムは以下のように書き換えることも可能です[[FootNote("ターミナルが ASCII 文字セット (をサブセットにする文字セットすべて) で動作している必要はありますが、そうでない端末を探す方が難しいでしょう…。")]]。
     65
     66{{{
     67#include <stdio.h>
     68
     69int 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
     85C 言語のことを、アセンブリ言語の (割と単純な) ラッパーマクロであるという人もいます。実際の所、 C 言語のプログラム中に、アセンブリ言語のプログラムを埋め込むことができる処理系もあります。例えばコンパイラに GCC を使用する場合、以下のように !__asm__ キーワードを用いたインライン構文により、アセンブリ言語のプログラムを埋め込むことができます。
     86
     87{{{
     88#include <stdio.h>
     89
     90int 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{{{
     115b = 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"
     126LC0:
     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