main 関数の引数、typedef、プリプロセッサ

main 関数の引数、typedef、プリプロセッサ


サイト全体の目次

稿


稿

main 関数の引数

このセクションのソース

この章では先に進む前に少し雑多な機能を解説します。まずは、main 関数の引数です。

/*01*/ #include <stdio.h>
/*02*/ 
/*03*/ main(int argc, char *argv[])
/*04*/ {
/*05*/   int i;
/*06*/   for(i = 0; i < argc; i++){
/*07*/     printf("%d: %s\n", i, argv[i]);
/*08*/   }
/*09*/ 
/*10*/   return 0;
/*11*/ }

list-7.1.1-03

実は main 関数は引数をとることができます。C言語のプログラムは基本的にコマンドラインから実行されるのですが、その際に引数(オプション)を与えることができるのです。たとえば、

> testprog.exe argument1 argument2 argument3

といった具合です。この場合、main 関数は「argument1」「argument2」「argument3」という3つの文字列を受け取ることができます。なお、それとは別に必ず実行されたプログラムのパスが渡されます。

では main 関数の引数を見てみましょう。

main(int argc, char *argv[])

最初の argc は与えられた文字列の数です。必ずプログラムのパスが渡されるため、最低でも「1」となります。argv は渡された文字列へのポインタを格納する配列です。つまり、
argv[0] : 一つ目の文字列へのポインタ(=プログラムのパス)
argv[1] : 二つ目の文字列へのポインタ
argv[2] : 三つ目の文字列へのポインタ
...

といった具合です。このように、必ず文字列として渡されることに気をつけてください。ちなみに、引数の名前は普通 argc と argv を使いますが、自由な名前でかまいません。また *argv[] は **argv としても同じ意味です。

typedef 1

このセクションのソース

/*01*/ #include <stdio.h>
/*02*/ 
/*03*/ typedef unsigned char BYTE;
/*04*/ 
/*05*/ main()
/*06*/ {
/*07*/   BYTE x = 227;
/*08*/   printf("%d\n", x);
/*09*/ 
/*10*/   return 0;
/*11*/ }

list-7.1.03

コンピュータの世界ではデータ処理を行う際にバイト単位で処理することがよくあります。その場合、C言語では「unsigned char」型を使うとちょうどよいのですが、毎回「unsigned」を書くのは大変です。そのうち「BYTE」なんて型があれば便利なのになぁ、と思えてくるでしょう。それなら BYTE 型を作ってしまえ、というのがtypedefの機能です。

書式は

typedef 元になる型 新しい型;

例:
typedef unsigned char BYTE;

です。このようにすると、新しい型を元の型の代わりとして使うことができます。この場合、BYTE とすればそれは unsigned char と同じ役割を果たすことになるわけです。ちなみに、Visual C++ では unsigned char と同じ BYTE 型が、unsigned short と同じ WORD 型が、unsigned long と同じ DWORD 型がそれぞれ typedef によって用意されています。

typedef 2

このセクションのソース

/*01*/ #include <stdio.h>
/*02*/ 
/*03*/ struct score{
/*04*/     char name[16];
/*05*/     int math;
/*06*/     int english;
/*07*/     int kei;
/*08*/ };
/*09*/ typedef struct score SCORE;
/*10*/ 
/*11*/ main()
/*12*/ {
/*13*/   SCORE a;
/*14*/ 
/*15*/   return 0;
/*16*/ }

(untitled)

unsigned char と同様、省略して書きたいもののひとつが構造体でしょう。list-7.1.3 では「struct score」と書く代わりに「SCORE」として構造体を使えるようにしています。これには省略的な書き方があって

typedef struct {
	char name[16];
	int math;
	int english;
	int kei;
} SCORE;

のようにすることもできます。

プリプロセッサ #define

このセクションのソース

/*01*/ #include <stdio.h>
/*02*/ 
/*03*/ #define NUM 123
/*04*/ #define STRING "Hello"
/*05*/ 
/*06*/ main()
/*07*/ {
/*08*/   void func();
/*09*/ 
/*10*/   printf("%d\n", NUM);
/*11*/   printf("%s\n", STRING);
/*12*/   func();
/*13*/ 
/*14*/   return 0;
/*15*/ }
/*16*/ 
/*17*/ #undef NUM
/*18*/ #define NUM 321
/*19*/ 
/*20*/ void func()
/*21*/ {
/*22*/   printf("%d\n", NUM);
/*23*/ }

list-7.1.4-03

プリプロセッサ前処理と訳され、コンパイルが行われる前に処理されます。

プリプロセッサのひとつである#defineはまさに定義という意味合いの処理です。例えば

#define NUM 123

は「NUM」が「123」である、と定義していて、それ以降、プログラム中で「NUM」が出てくるとコンパイル前に「123」へ置き換わります。#define の面白いのは、型を書かない点です。これは、#define がコンパイル前にテキストの置換を行う処理であるといってもよいからでしょう。つまり、
printf("%d\n", NUM);


printf("%d\n", 123);

と処理されてからコンパイルされるのです。ですので、配列の宣言で変数を使うことができなくても、#define されたものは使うことができるのです。
/* 間違い */
int NUM = 32;
char name[NUM];

/* 正しい */
#define NUM 32
char name[NUM];	

使い道ですが、例えばある10個のデータをしょるするケースを考えてください。あなたは配列の宣言や for の中、メッセージの出力など、さまざまな場所で「10」という値を直接書き込みました。このように値を直接書くプログラミングスタイルをハードコーディングといいます。さて、プログラムも仕上がってデータの処理も無事に終わりました。ところが数日後、今度はデータ数を15個として処理しなければいけなくなりました。あなたはソースのあちこちで使われている「10」という値を「15」に書き換えなければいけません。このような場合、「10」という値を #define しておけば、あとで変更があってもそこだけ変更すればよいことになります。#define の場合、配列でも使える点を有効に活用しましょう。

list-7.1.4-17

プリプロセッサ#undefは一度 #define で定義されたものを忘れさせます。この例では、NUM の 123 を忘れさせて、新しく NUM を 321 と定義しています。

プリプロセッサ #ifdef

このセクションのソース

/*01*/ #include <stdio.h>
/*02*/ 
/*03*/ #define DEBUG
/*04*/ 
/*05*/ main()
/*06*/ {
/*07*/ #ifdef DEBUG
/*08*/   printf("debug mode\n");
/*09*/ #else
/*10*/   printf("release mode\n");
/*11*/ #endif
/*12*/   printf("hello, world\n");
/*13*/ 
/*14*/   return 0;
/*15*/ }
/*16*/ 

(untitled)

#define は

#define DEBUG

のように置き換わる対象を指定しなくても定義されたことになります。これを利用して、プリプロセッサ#ifdefと組み合わせるとコンパイルする内容を制御することができます。このようにコンパイルする内容を制御する方法を条件付コンパイルといいます。

#ifdef ~ は「~」が定義されていたらコンパイルせよ、となります。#elseは #ifdef とペアになるプリプロセッサで、「そうでなければ」を意味します。#endifは、条件処理の終了を意味します。このリストは、そのまま実行すれば「debug mode」が表示され、#define の行を削除するかコメントアウトすれば「release mode」が表示されます。

#ifdef には反対の意味を持つ#ifndefがあります。#ifndef ~ は「~」が定義されていなかったらコンパイルせよ、となります。

プリプロセッサ #if

このセクションのソース

/*01*/ #include <stdio.h>
/*02*/ 
/*03*/ #define DEBUG1 0
/*04*/ #define DEBUG2 10
/*05*/ #define DEBUG3 20
/*06*/ 
/*07*/ main()
/*08*/ {
/*09*/ #if DEBUG1
/*10*/   printf("debug mode 1, value=%d\n", DEBUG1);
/*11*/ #elif DEBUG2
/*12*/   printf("debug mode 2, value=%d\n", DEBUG2);
/*13*/ #elif DEBUG3
/*14*/   printf("debug mode 3, value=%d\n", DEBUG3);
/*15*/ #else
/*16*/   printf("release mode\n");
/*17*/ #endif
/*18*/   printf("hello, world.\n");
/*19*/ 
/*20*/   return 0;
/*21*/ }

list-7.1.6-09

さらに細かくコンパイルする条件を制御したいならば、#ifを使うことができます。#if ~ は「~」が真(つまり非ゼロ)ならばコンパイルせよ、となります。また、#elifは if 文に対する else if に相当し、#elif ~ とすれば、そうでなくて ~ ならばコンパイルせよ、となります。ただしいずれも定数しか評価することができません。つまり、比較演算などは行えないことに注意してください。

ここまでであげた #if、#ifdef、#ifndef、#elif、#else、#endif はいずれも相互に組み合わせて使用することができます。また、#if ~ #endif の間にさらに #if ~ #endif を含めるなど、ネスとすることも可能です。

プリプロセッサ #include

このセクションのソース

/*01*/ /* test.c */
/*02*/ #include "myheader.h"
/*03*/ 
/*04*/ main()
/*05*/ {
/*06*/   func1();
/*07*/   func2();
/*08*/ 
/*09*/   return 0;
/*10*/ }
/*01*/ /* myheader.h */
/*02*/ #include <stdio.h>
/*03*/ 
/*04*/ void func1()
/*05*/ {
/*06*/   printf("myfunc1\n");
/*07*/ }
/*08*/ 
/*09*/ void func2()
/*10*/ {
/*11*/   printf("myfunc2\n");
/*12*/ }

list-7.1.7.a-02

#includeは指定されたヘッダーファイルをそこに展開してからコンパイルを行います。この例では myheader.h をそこへ展開します。つまり、list-7.1.7.a は

/*01*/ /* test.c */
/*02*/ /* myheader.h */
/*03*/ #include <stdio.h>
/*04*/ 
/*05*/ void func1()
/*06*/ {
/*07*/   printf("myfunc1\n");
/*08*/ }
/*09*/ 
/*10*/ void func2()
/*11*/ {
/*12*/   printf("myfunc2\n");
/*13*/ }
/*14*/ 
/*15*/ main()
/*16*/ {
/*17*/   func1();
/*18*/   func2();
/*19*/ 
/*20*/   return 0;
/*21*/ }

となってコンパイルされるわけです。さて、ヘッダーファイルはさらに別のヘッダーファイルをインクルードすることができますが、複雑な構成になってくるといつのまにか同じファイルをインクルードしてしまっている場合も出てきます。そうすると、多重定義でエラーになってしまうので、そういった事態を避けなければなりません。そこで、ヘッダーファイルに次のような工夫を施します。
/*01*/ /* myheader.h */
/*02*/ #ifndef __MYHEADER_H
/*03*/ #define __MYHEADER_H
/*04*/ 
/*05*/ #include <stdio.h>
/*06*/ 
/*07*/ void func1()
/*08*/ {
/*09*/   printf("myfunc1\n");
/*10*/ }
/*11*/ 
/*12*/ void func2()
/*13*/ {
/*14*/   printf("myfunc2\n");
/*15*/ }
/*16*/ 
/*17*/ #endif

こうすると、一回目のインクルード時には __MYHEADER_H が定義されていないので最後の #endif までが展開されます。この際、__MYHEADER_H が定義されるので、次にインクルードされたときは #ifndef によってはじかれます。このようにすれば、同じヘッダーファイルが複数回展開される事態を回避することができます。


読み込み中・・・
10秒待っても表示が変わらない場合、次の理由が考えられます。
・Javascript が無効になっています。
・検索エンジンのキャッシュを見ています。
サイトホームへ / 上位ページへ / ページトップへ / PAROFトレンドショッピングへ
Copyright (C) 2010 totobon all right reserved.