この章では先に進む前に少し雑多な機能を解説します。まずは、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*/ }
実は main 関数は引数をとることができます。C言語のプログラムは基本的にコマンドラインから実行されるのですが、その際に引数(オプション)を与えることができるのです。たとえば、
> testprog.exe argument1 argument2 argument3
では main 関数の引数を見てみましょう。
main(int argc, char *argv[])
argv[0] : 一つ目の文字列へのポインタ(=プログラムのパス) argv[1] : 二つ目の文字列へのポインタ argv[2] : 三つ目の文字列へのポインタ ...
/*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*/ }
コンピュータの世界ではデータ処理を行う際にバイト単位で処理することがよくあります。その場合、C言語では「unsigned char」型を使うとちょうどよいのですが、毎回「unsigned」を書くのは大変です。そのうち「BYTE」なんて型があれば便利なのになぁ、と思えてくるでしょう。それなら BYTE 型を作ってしまえ、というのが
書式は
typedef 元になる型 新しい型; 例: typedef unsigned char BYTE;
/*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*/ }
unsigned char と同様、省略して書きたいもののひとつが構造体でしょう。list-7.1.3 では「struct score」と書く代わりに「SCORE」として構造体を使えるようにしています。これには省略的な書き方があって
typedef struct {
char name[16];
int math;
int english;
int kei;
} SCORE;
/*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*/ }
プリプロセッサのひとつである
#define NUM 123
printf("%d\n", NUM);
printf("%d\n", 123);
/* 間違い */ int NUM = 32; char name[NUM]; /* 正しい */ #define NUM 32 char name[NUM];
使い道ですが、例えばある10個のデータをしょるするケースを考えてください。あなたは配列の宣言や for の中、メッセージの出力など、さまざまな場所で「10」という値を直接書き込みました。このように値を直接書くプログラミングスタイルを
プリプロセッサ
/*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*/
#define は
#define DEBUG
#ifdef ~ は「~」が定義されていたらコンパイルせよ、となります。
#ifdef には反対の意味を持つ
/*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*/ }
さらに細かくコンパイルする条件を制御したいならば、
ここまでであげた #if、#ifdef、#ifndef、#elif、#else、#endif はいずれも相互に組み合わせて使用することができます。また、#if ~ #endif の間にさらに #if ~ #endif を含めるなど、ネスとすることも可能です。
/*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*/ }
/*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