6プログラミング序論 page3(update:2017/04/27)
[ index | prev | next]

3.Cプログラムの作成と実行

コマンドラインで計算機に指示を出し、C言語のプログラムを作成し実行するまでの流れと、各段階で何が行われているのかを確認します。

レポートツールはこの手順をボタン1つで自動的に行います。しかし、この手順をブラックボックスにしてしまうとプログラミングを具体的に理解することはできません。ここで皆さんの記憶に残しておくために手順を体験してもらうことにします。

3-0 前回の復習

C言語プログラムの実行までの手順

  1. ソースプログラム(ソースコード)をテキストエディタで作成し1個のソースファイルとして保存する。
  2. 実行可能プログラムファイルの作成:OS上で実行可能な機械語プログラムに翻訳する。
  3. 実行:OSを使って実行可能ファイルをメモリーに読み込んで実行する 。

ファイルの拡張子

上の図のようにC言語のプログラミングに関係するファイルはソースファイル、オブジェクトファイル、実行可能ファイルなど幾つかに分類できる。WindowsやUNIXのようなOSでは、ファイル名の最後に拡張子を付けることでファイルの種類が判別できるようにしている。

種類 Windows UNIX 中身
Cのソースファイル  .c  .c C言語プログラムのテキストデータ
Cのヘッダーファイル  .h  .h ライブラリの関数を使うための
情報を記述したテキストデータ
オブジェクトファイル  .obj  .o ソースを翻訳した機械語など
ライブラリファイル  .lib .dll  .a .sdl 色々な機械語の関数をまとめたもの
実行ファイル  .exe 実行可能性は拡張子によらない OSで実行できるリンク済みの機械語

ファイルの拡張子が表示されない場合

スタート/コントロールパネル/(デスクトップのカスタマイズ)フォルダオプシオン/表示タブの詳細設定で

「登録されている拡張子は表示しない」のチェックを外す。OKボタンを押すこと

3-1.プログラム作成から実行までを体験

CUI(character user interface)

皆さんはGUI(Graphical User Interface)と呼ばれる計算機が画像を表示し、利用者がマウス操作で計算機に指示を出す、GUIと呼ばれる計算機との会話環境に慣れていると思います。

しかし、GUIは計算機の中の仕組みを隠してしまうので、GUIだけを使っているとC言語処理系の仕組みは理解できません。ここでは、あえてCUI(Character User Interface)を使ってみることにします。

CUIは計算機利用者が文字列を使って計算機との会話を行う仕組みです。利用者が文字列を入力すると計算機から文字列が返ってきます。以下で使うコマンドプロンプト(Command Prompt)は1つの画面の中で利用者と計算機の文字列による会話を実現します。

スタートメニューから System Tools/Command Prompt を選ぶと次のようにコマンドプロンプトの画面が開きます。

※環境によってCommand Prompt がある場所やコマンドプロンプトの設定が異なります。その場合は
スタートメニューを右クリックで、ファイル名を指定して実行 でcmd.exeでコマンドプロンプトが起動できます。この場合は下とは異なるカレントディレクトリになるので、画面上でコマンド「cd /d Z:\」を実行してください

ここで

Z:\>

のように 何かの文字列 の部分をコマンドプロンプトと言います。コマンドプロンプトは計算機が利用者の入力を待っていることを示す文字列です。

まずZドライブの中にgengo0フォルダを作ります。もし既にあれば下の画面のようにメッセージが出ます。

Z:\>md gengo0

次にそのフォルダに移動します。(作業場所をZ:\gengo0にする)

Z:\>cd gengo0

ここで、C言語のコンパイラなどを使うための準備をするためのバッチファイルc.batを作りましょう。作成にはメモ帳(notepad)を利用します。

Z:\>notepad c.bat

と利用者がキーボードから入力しEnterキーで計算機に送信すると、メモ帳が開いて次のコマンドプロンプトが表示されます。

※c.batの名前のファイルが無い場合は「新しく作りますか」と聞いてきますから。作ってください。

CUIは文字列を入力するだけなので、あらかじめ利用者の入力する文字列をファイルにしておいて、これを自動実行することができます。この目的で作られるファイルは拡張子がbatとします。バッチファイルと言います。メモ帳でc.batの中身を下記のテキストのようにしてください。ここはコピペしてかまいません。

cd /d Z:\
md gengo0
cd gengo0
explorer /root,Z:\gengo0
call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\Tools\vsvars32.bat"
cmd /S

※必要なら上記のcall "C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\Tools\vsvars32.bat"の部分はPCにインストールされているVisual Studioのバージョンに合わせて変更してください。

書き終わったら、保存してください。Z ドライブのgengo0フォルダにc.batファイルが保存されるはずです。

コマンドラインによる操作環境を作る

c.batの中身は1~6の命令を実行順に並べたものです。この処理で開かれたコマンドプロンプトではMicrosoft Visual StudioのC言語処理系がコマンドラインで使えるようになります。

  1. Zドライブのトップに移動
    (学科演習室のPCでなければ、この行は不要です)
  2. 作業フォルダgengo0を作成
  3. そのフォルダに移動
  4. エクスプローラを起動してそのフォルダ(Z:\gengo0)をrootとしてファイルを表示する
  5. Microsoft Visual Studioの実行環境を設定するバッチファイルを呼んで実行
    (Visual Studioのバージョンやインストール場所が異なる場合は書き換える必要があります)
  6. コマンドプロンプトを開く

※c,batがうまく作れない場合はc.batをダウンロードしてください(IEならマウスの左クリックでメニューから保存、Firefoxなら右ボタンからファイルに保存)ここで、勝手に名前が変更されることがあります。ファイル名がc.bat.txtとなってしまったらc.batに名前を戻してください。

演習室の環境ではデスクトップでバッチプログラムを実行できません。c.batをZドライブのgengo0フォルダに入れてから実行してください。c.batを実行すると次の警告が出ますが かまわず実行します。

実行すると、次のエクスプローラの画面とコマンドプロンプトの画面(黒い画面)が開きます。


このコマンドプロンプトの画面がOSとテキスト形式で会話するCUIです。画面の最後の > の付いている行コマンドプロンプトと言い、OSが命令を待っていることを示すものです。この後に利用者が命令文を入れてEnterキーを押すことでOSにテキスト形式の命令が渡されます。OSからの返事もテキストとして画面に追加されます。

※同じ画面を使ってOSとユーザが会話するので、ユーザー命令入力が可能な状態をコマンドプロンプトで示します。コマンドプロンプトが出ている状態でのみユーザーは命令の入力が可能です。

テキストエディターでソースファイルを作る

まずWindowsの標準のテキストエディタ notepad(メモ帳)を使ってソースファイルhello.cを作ることにします。次の太字の部分を書き込んでEnterキー(改行に使うキーです)を押してください。

コマンドライン>notepad hello.c

※キートップがEnterになっているのは命令を入れるキーだからです。Enterキーは送信ボタンみたいなものです。計算機をワープロとしてしか使わないと、なぜ改行としないのか不思議に思うはずです?

hello.cの名前のファイルが無いときは次のように新しく作るか聞いてきますから「はい(Y)」で作ってください。

下のようにテキストエディタの画面が開きます。

ここで次のようにプログラムを書きます。

注意: 
/*と*/で囲まれた部分はコメントなので書かないでください。かな入力の状態にすると、C言語のコンパイラが理解できない全角文字を入力してしまい、コンパイルエラーになることが多い。

 

C言語の解説書によっては 「」の部分が「」」となっています。 
これはフォントの違いによるものです。次のように「書式/フォント」でフォントを変更するとバックスラッシュ「\」に表示される形が変わります。

 

※計算機の中では文字は文字コードと呼ばれる数値で表現されています。米国のASCIIコードと日本のJISコードは16進数の5C(十進数では92)に異なる文字「」と 「」を対応させているので、このような違いが起こります。C言語を機械語に翻訳するコンパイラはフォントではなくて、コード値を読みます。人には違って見えますが計算機には同じに見えるのです^^.;

プログラムが書けたら保存します。

確認:ソースファイル名がhello.cになっていることを確認してください。

実行可能ファイルを作る

前処理コンパイルリンクを一度に実行します。

次のコマンドラインを書き込んでEnterキーを押します。

コマンドライン>cl hello.c 

 次のように計算機から回答があれば成功です。

※エラーを指摘されたらテキストエディターでソースファイルを修正してください。コマンドラインは

コマンドライン>notepad hello.c

です。
(何度やってもエラーが出て自分で作るのをあきらめた人は、先に進むために
hello.cをダウンロード。)

実行可能ファイルまで作れたでしょうか?

確認:hello.cのファイルと同じ場所にhello.objとhello.exeが出来ていますか?

hello.objはオブジェクトファイルと呼ばれるもので、ソースファイルを機械語に翻訳したものですが、いろいろ足りない部分があって、これ自体は実行できません。

hello.exeが実行可能ファイルでOS(この場合Windows10)の環境で実行できる機械語プログラムです。hello.objと実行に必要な沢山のプログラムを結合(リンク)して作られます。

分割コンパイル

ここではコンパイルとリンクを連続して一度に行いました。 しかし、数千行を超えるような実用的なプログラムでは、複数のプログラマがプログラミング作業を分担し 複数のソースファイルに分けてプログラムを作成します。各ソースファイルを個々にコンパイル(分割コンパイル)し複数のオブジェクトファイルを作ります。

最後に、これらのオブジェクトファイルをリンクで1つにまとめて実行可能ファイルを作リます。 

このような作り方をすると、個々のオブジェクトファイルを他のプログラムで利用することもできます。printfという関数をmain関数の中で呼びました。このprintf関数は誰かがソースコードを作りコンパイルしてオブジェクトファイルを作成し、他の多くの標準関数のオブジェクトファイルと一緒に標準ライブラリーに纏められています。

※C言語ではprintfなどの標準関数のオブジェクトを標準ライブラリーファイルに纏めています。ソースファイルが1個の簡単なプログラムでは1個のオブジェクトファイルと標準ライブラリーをリンクするだけなので1度にコンパイルからリンクまでを行えます。しかし、複数のオブジェクトファイルや非標準のライブラリーをリンクする場合はリンクコマンドの使い方を知ることが必要になります。

実行可能ファイルを実行する

実行は実行可能ファイル名をそのまま命令としてコマンドラインでOSに渡します。

コマンドライン>hello.exe 

exeを省略して コマンドライン>hello でもかまいません

 

Hello Worldと画面に書き出されてコマンド入力待ちになれば成功です。

[メモ] OSに戻された値を見る

Windowsの場合、プログラム実行直後に errorlevel の値を表示すると、戻り値の値が解ります。

/*
戻り値として123を戻すだけのCプログラム
ソースファイル    test123.c
実行可能ファイル test123.exe
*/
int main(void)
{
    return 123;
}

これをコンパイルして実行後に errorlevel の値を表示させると以下のようになります


 

コマンドライン>test123

でtest123.exeを実行しても何も表示されませんが実行が終わって次のコマンドプロンプトが出ます。ここでtest123の戻り値はerrorlevelと言う名前の変数に格納されているので

コマンドライン>echo %errorlevel%

でerrorlevelを表示するように命令すると確かに123と値が表示されます。

3-2.何が起きているのか確認しよう

ソースファイルの中身

C言語処理系はCプログラムの文字の形を見て翻訳するわけではありません。ソースファイルのバイトデータの並びを見て翻訳します。中身を16進数として見てみましょう。自作のC言語で作ったプログラムdump.exeを用意しました。

dump.exe をZ:\gengo0のフォルダにダウンロードしてください。

 

そのうえで先ほどのコマンドラインから 

コマンドライン>dump hello.c

と命令するとhello.cの中身が16進数で出力されます。

 

表示はアドレス、16バイト分の値、その値に対応する文字コード表の文字となっています。

00000000:23 69 6E....となっている部分はファイルの先頭から順番に23 69 6Eの値が書かれていることを示しています。1バイト分の値が16進数2桁で、横に16バイト分を表示しています。アドレスが16個進むので次の行は00000010のアドレスからになります。破線で囲った16進数の並びがコンパイラが読む部分を示しています。

右側に16進数に対応する文字を書き出していますがプログラムそのままなのが分かると思います。16進数「0D 0A」には対応する文字がありませんがテキストの改行を示しています。このようなコードを制御コードといいます。 

文字コード

文字をそのままデジタルデータとして記録することはできません。そこで、良く使う文字を選んで対応する数値を文字コードとして決めています。例えば、米国でよく使われていたASCIIコード表と呼ばれるものはアルファベット大文字小文字、数字、記号と改行やベルを鳴らすなどの制御命令に対して7ビットの2進数を対応付けています。教科書の裏表紙を見てください。他の多くのコード表もこれを取り込む形で作られていて、例えば8bitのJISコードはASCII文字に加えてカタカナまで対応付けしています。 このカタカナを俗に半角仮名といっています。しかし、ASCIIコードの5Cの位置の文字:バックスラッシュ「\」がJISコードでは円記号「¥」に変わっているなど、同じでない部分が有ります。

※7ビット(2進数7桁)で表せる数値の範囲は十進数0~127までの128個。ASCIIコード表ではこの範囲の数値に制御コードやアルファベット大文字、小文字、数値、記号等を対応付けしています。
 8ビットで表せる数値は0~255までです。JISコードでは0~127まではASCIIコードとほぼ同じ割り当てを行い、数値の161~223の範囲にカタカナや追加の記号を割りあてています。

Windows の日本語環境では「¥」と表示される文字が。C言語の教科書によっては「\」となっていることがあります。この原因は使用する文字コード表の違いです。8ビットの数値5C(十進の92)にASCIIでは「\」をJISでは「¥」を割り当てています。

ソースファイルで使える文字

Cコンパイラはアルファベット大文字、小文字、英数字と一部の記号のみでプログラムが書かれていることを前提に作られています。仮名や漢字には対応していません。基本的にはASCIIコード表の英数字、記号、改行文字やタブ文字のみでプログラムを書く必要があります。

日本語対応のCコンパイラもコメントと文字列の中身のみ漢字や仮名を使うことができるにすぎません。次に示すように、太字で色を変えた部分だけです。
コメント /*漢字などの2バイト文字*/
文字列 "こんにちは"

注意
空白(スペース文字)にも半角と全角の文字が有って、全角の空白はコメントや文字列中以外には使えません。しかも空白なのでテキストエディターで見ても見えないのです。コンパイラに渡すと不正な文字が有るといってコンパイルエラーを出してきます。似たようなことが「”」など半角と全角の両方用意された文字で起こります。

前処理

-------以下は少し古いバージョンのコンパイラでの結果です。現状とは少し異なります------

翻訳する前にソースコードにいろいろと記述を追加したり成形したりする処理です。例題のプログラムでは#include<stdio.h>と書かれた部分が前処理への命令で、stdio.hファイルの中身をここに挿入するように指示しています。

次のようなコマンドで前処理だけ行うと。結果をhello.iという名前のファイルに書き出します。

コマンドライン>cl /P hello.c

このhello.iはテキストファイルなのでメモ帳で中身を見ることが可能です。下記のように#includeの1行が6千行を超えるテキストに置き換えられています。さらに、プログラムのコメントが削除されています。

0001行目 #line 1 "hello.c"
0002行目 #line 1 "C:\\Program Files (x86)\\Microsoft Visual Studio 11.0\\VC\\INCLUDE\\stdio.h"
     ..........中略..........
 
5795行目 int __cdecl printf( const char * _Format, ...);
 
     ..........中略..........
6385行目 int main(void)
6386行目 {
6387行目     printf("Hello World\n"); 
6388行目     return 0; 
6389行目 }

この挿入されたテキストの中にprintf 関数に渡すデータの形と、戻り値のデータ型が示されているので、main関数を翻訳するときにprintf 関数を呼び出す部分の機械語が作れます。

オブジェクトファイル

hello.objはオブジェクトファイルと呼ばれるもので、ソースファイルを機械語に翻訳した部分と、ソースファイルに関係する情報が記録されています。

このプログラムの場合はmain関数のみなので機械語の部分はごく一部です。中身を16進数で見ると次のようになります。

 

ファイルの中は幾つかの部分にわかれているのですがこの中身をみるための専用プログラムdumpbinが用意されています。コマンドラインから

コマンドライン>dumpbin /DISASM /RAWDATA hello.obj

を入力してみてください。アセンブラコードが示された部分が機械語のデータ部分です。ほんとにごく一部です。上の図で黄色い線で示した部分はアセンブりコードに逆変換すると次のようになっています。

この機械語のプログラム。関数printfをcallで呼び出して、戻り値を0にしてret(リターン)で戻っているだけです。しかも「HelloWorld」の文字列のアドレスも、printf関数のアドレスも0のまま。オブジェクトファイルはソースコードを機械語に翻訳しただけです。これだけでは文字列の場所もprintf関数の場所もわからないので実行できません。

実行可能ファイル

これも同様にして中身をみることができますが、膨大な出力結果になります。この増加した部分はリンク処理でライブラリーなどから取り込まれたものです。

コマンドライン>dumpbin /DISASM /RAWDATA hello.exe

実行結果は載せません、書ききれないような長さになるからです。

オブジェクトファイルに有ったmain関数の機械語部分のみを見ると次のように具体的なアドレスに変わっていました

00401000: 55               push ebp
00401001: 8B EC            mov ebp,esp
00401003: 68 00 B0 40 00   push 40B000h<<Hello Worldの文字列の先頭アドレス
00401008: E8 07 00 00 00   call 00401014<<printf関数の先頭アドレス
0040100D: 83 C4 04         add esp,4
00401010: 33 C0            xor eax,eax
00401012: 5D               pop ebp
00401013: C3               ret

計算機の中でプログラムが動くためには、いろいろな初期化を行い、さらにOSとデータの交換を行うことも必要です。その部分の機械語プログラムがたくさん取り込まれています。

次の図のように、リンク処理ではhello.objファイルにあった機械語に加えてライブラリーファイルから使う関数の機械語が取り込まれて並べられます。並べると機械語のアドレスが決まるので、printfなどの関数のアドレスが決まります。この呼び出し先のアドレスを機械語の必要な部分に上書きすることで、main関数からprintfなどの機械語の関数を順次呼び出せるようになります。

ファイルの大きさは次の表のように大きくなっています。

ファイルの種類 バイト数
ソースファイル:hello.c 186
オブジェクトファイル:hello.obj 618
実行可能ファイル:hello.exe 46,080


現在の環境で同じことを一度に見せる自作ツール


[ index | prev | next]