lC言語 関数とマクロ '97. 1.10  
l.お知らせ 内容 LINK FILE   HTML Win PC Unix MS-DOS C C++ Mfc Java 
.C言語_ 3 フォ-マットコ-ド 6 演算子 9 FOR  集合デ-タ  目的別
1 表示してみる 4 変数と定数 7 条件判断 10 WHIILE  ポインタ     
2 エスケ-プ コ-ド 5 デ-タ入力 8 分岐   未使用  関数   ★ 
[関数] プロトタイプ宣言  返り値  引数(仮引数・実引数)  値渡しとポインタ渡し 
変数のスコープ  関数へのポインタ    [マクロ]と別名の定義
 

関数
関数は、何度も使うようなプログラムの部分に名前を付けたものです。
プログラムの本体とは別な位置に分けて書きます。
関数を使うときには、本体部分に関数名を書くと、その関数が呼び出されます。
#include <stdio.h>
/* 関数の宣言/定義 */
void abc( ) { printf ("ABC\n"); }
/* プログラムの本体部分 */
void main( )
{
abc( ); /* 関数を呼び出します。*/
}

本体部分に関数名が書かれていると、CPU はその関数の仕事をしてから、再び本体部分の位置に戻って続きの仕事をします。

関数のプロトタイプ宣言
関数はあらかじめ定義していなければ、使用することができません。
しかし、関数同士で互いに呼び出し合うような構造のプログラムでは、関数を定義する順番を決めるのが難しくなります。
関数のプロトタイプを先に宣言すれば、使用する位置よりも後で定義してもよいことになっています。
#include <stdio.h>
/* 関数のプロトタイプ宣言 */
void abc ( );
/* プログラムの本体部分 */
void main( )
{
abc( ); /* 関数を呼び出します。*/
}
/* 関数の定義 */
void abc( ) { printf("ABC\n"); }

関数のプロトタイプ(Prototype:複製に対する原形)宣言 :
 返り値の型 関数名 引数 だけを記述すること。
 具体的な内容の定義は記述しません。

返り値 : 関数が処理した、結果のデータ。
関数を宣言または定義する場合には、返り値のデータの型を指定します。
返り値を使用しない場合は、void と記述します。
次の関数は、引数の値を2倍にして返します。
int func (int x) { x=2*x; return x; }
return によって、返り値の値を指定します。
この関数の使用例を示します。
int y=3; int z;
z = func(y); /* z には 6 が代入されます。*/
printf("%d",func(7) ); /* 14 と表示されます。*/

引数 : 関数に与えて処理させる、元のデータ。
関数を宣言および定義する場合には、引数の型と引き数名を記述します。
引数を使用しない場合は ( ) だけを書きます。
次の例は、与えられた引数の値を 16進数で表示する関数です。
void func(int x) {printf("%x",x); }
この関数を使用する例を示します。
int y=5;
func(y); /* 5 と表示されます。*/
func(11); /* B と表示されます。*/
func(7) のように関数を呼び出すと、引数は関数内部の変数 x にコピーされてから処理されます。

関数を呼び出すときに与える引数(この例では y7 )を実引数といい、関数内部にコピーされる引数(ここの例では x )は仮引数といいます。
(仮引数はその関数の中だけで使用される変数と考えることができます。)

仮引数 x はコピーされた値なので、x=x+2 のように変化させても、実引数 y11 の値は、変化しません。

関数の外部の変数を直接処理する場合には、引数を使用する必要はありません。
#include <stdio.h>
int x=5; /* 変数の宣言/初期化 */
void func( ) {printf("%x",x); } /* 関数の宣言/定義 */

void main( )
{
func( ); /* 5 と表示されます。*/
}
この例の場合は、関数が外部の変数 x を直接 処理していますので、x=x+2 のようにすると x の値が変化します。

値渡しとポインタ渡し
関数の実引数として、変数や配列などを与えて呼び出す場合を値渡しといいます。
上の例は全て値渡しになっています。
これに対して、変数や配列などのポインタを与えて呼び出す方法は、ポインタ渡しと呼ばれます。
#include <stdio.h>
void func(int *p) { printf("%d\n",*p); } /* 関数の定義 */

void main( )
{
int x=5; /* 変数を宣言/初期化 */
int *pp; /* ポインタ変数を宣言 */
pp=&x; /* ポインタ変数 pp に変数 x のアドレスを代入 */
func(pp); /* 変数 x のポインタを与えて、関数の呼び出し */
}
pp に変数 x のアドレスを代入しています。
関数を呼び出すと、ポインタ pp の値はポインタ p にコピーされます。
p はコピーされた値なので、p=p+1 のように変化させても、コピー元の pp の値は変化しません。
しかし、ppp と同じ値なので、p を使って pp と同じように、変数 x を操作することができます。

メモ : int *p の * は、ポインタ p を宣言するために使われています。
 printf("%d\n",*p) の * は、p(ポインタ=データのアドレス)を、そのアドレスの内容に変換するために使っています。

変数のスコープ (変数の有効範囲)
変数には有効範囲があって、有効範囲が異なれば同じ変数名を使用することができます。

#include <stdio.h>
グローバル変数
int x;
char s;
struct st{int x,char s}d1;
自動変数
void func(int *p)
{
int x;
char s,t;
static int h;  スタティック変数
register int r;  レジスタ変数
}
 
自動変数
void main(int argv,char *argc[ ])
{
int x,y;
char s;
}
グローバル変数(外部変数)
そのプログラム全体で有効です。
最初の値はゼロになっています。

自動変数
その関数の中だけで有効です。
main は一番最初に呼び出される特別な関数です。

その関数が働いている間だけ、自動的にメモリ上に格納場所が確保され、 関数の仕事が終るとその部分は別なデータのために使われます。
場所が確保されるだけなので、代入前の値は不定です。

関数の引数の有効範囲も自動変数と同じです。
グローバル変数(ExternalVariable,外部変数,大域変数)は、関数の外側で宣言します。
自動変数(AutomaticVariabl)は、関数定義の中で宣言します。

スタティック変数
自動変数の寿命はその関数が働いている間だけです。
スタティック変数に指定すると、関数が終了してもそのまま保存されます。
一番最初の値は0(文字なら NULL:なし)になります。。
(その関数の中だけで使用できること以外は、グローバル変数と同じです。)

レジスタ変数
変数はメモリ上(RAM)に格納されますが、レジスタ変数に指定すると、レジスタ(CPU内部のメモリ)に格納されます。

メモ : スタック(stack)
メモリ上のデータの位置を指定する方法には、CPU が直接指定する方法と、スタックポインタという装置で指定する方法があります。
スタックポインタは連続したアドレスのデータを高速に指定できます。
スタックポインタが使用するためのメモリ領域は、スタックと呼ばれます。
スタックが不足するとプログラムは正常に動作しません。

自動変数はスタックを使用するように決められていて、そのスタックの領域は LSI C-86 では 2.5KB に固定されています。
変更するには、
lcc prog.c -k'-s 2000'
のように、コンパイラの起動時に指定します。
関数へのポインタ
関数の呼び出しは、ポインタを使って行うことができます。
返り値型 (*ポインタ変数名) (引数); /* ポインタ変数を宣言。*/
ポインタ変数名 = 関数名; /* ポインタに関数のアドレスを代入。*/
(*ポインタ変数名)(引数):  /* 関数を呼び出します。*/
配列名の場合と同様に、関数名はその関数のポインタを表わしています。

#include <stdio.h>
void func(int x)        /* 関数 func の宣言と定義。*/
{
printf("引数 = %d \n",x);
}

void main( )
{
void (*F)(int x); /* 関数用のポインタ F を宣言。*/ F=func; /* ポインタに、関数のアドレスを代入。*/ func(7); /* 普通の方法で、関数を呼び出します。*/ (*F)(7); /* ポインタを使って呼び出します。*/ }
ポインタを宣言するとき、引数(この例では int x )は省略可です。
別な型を書くとエラーになりますが、void (*F)( ); のように省略しても、コンパイラが処理してくれます。

関数のポインタを配列に代入すると、関数を番号で指定することができます。
#include <stdio.h>
void funcA( ){printf("ABC ");}
void funcB( ){printf("DEF ");}
void funcC( ){printf("GHI ");}

main( )
{
int i;
void (*F[3])( )={funcA,funcB,funcC}; /* ポインタを配列に代入 */

for(i=0;i<3;i++)(*F[i])( ); /* 配列番号を使って順番に呼び出し*/
}



マクロ
まず、プリプロセッサ(コンパイラの前処理プログラム)の文字列置換機能について説明します。

プリプロセッサは、ソースプログラムを実行プログラムに変換する前に、別名が宣言されている場合は元の文字列に変換します。

別名を定義する書式は次のとおりです。
#define 別名 文字列
この方法で、円周率や画面をクリアするエスケーブコードなどに別名を定義しておくことができます。
#define CLS \033[2J
#define PI 3.14159265359
 用例 printf("CLS");
printf("%f\n",PI);

文字列の部分がプログラムである場合には特に、マクロと呼ばれます。
文字列の部分に大きな内容が書かれていても、短い別名を使うことができます。

例を示します。
#include <stdio.h>
#define ABC z+5 /* マクロを定義します。*/
void main( )
{
int z=3;
printf ("%d",ABC); /* 8 と表示されます。*/
}
これは実行プログラムに変換される前に、プリプロセッサによって、次のように書き直されます。
#include <stdio.h>
void main( )
{
int z=3;
printf ("%d",z+5); /* 8 と表示されます。*/
}

文字列だけではなく変数も使う場合には、変数の宣言をして、変数は( )で囲んで定義します。
#define マクロ名(使用変数) (変数)を使ったプログラム
変数という言葉を使っていますが、X や Y などの変数を表わす文字をその部分に挿入して置換するだけなので、数と順番だけが分かれば良いわけですから、変数の型は定義しません。

例を示します。
#include <stdio.h>
#define ABC(x,y) ( (x)+(y) ) /* マクロを定義します。*/
void main( )
{
int z;
z=ABC(5,3); /* マクロを使用します。*/
printf("%d %d", z, ABC(z,7) ); /* 表示にもマクロを使用します。*/
}
メモ : (x)+(y) を囲む外側の( )は必要ではなく、マクロのプログラム部分の範囲を示すために書いています。

これは、実行プログラムに変換される前に、次のように書換えられます。
#include <stdio.h>
void main( )
{
int z;
z=5+3;
printf("%d %d", z, z+7 );
}


大きな内容を置換したい場合
文字列や関数ではなく、ファイル全体の内容と置換したい場合は、#include を使います。


定数の別名を使いたい場合
定数は、上記のように別名でも定義できます。
#define PI 3.14159265359
定数は、定数変数として定義することもできます。
const double PI 3.14159265359


T

C言語  ポインタ  関数  目的別 
 
  mtoga@sannet.ne.jp   登録日 '96. 6.15
URL : http://www.page.sannet.ne.jp/mtoga/index.html