「C言語/関数」の版間の差分
削除された内容 追加された内容
static変数について勘違いしてたので修正。長いのでいったん投稿。 |
修正中 |
||
11 行
=== 関数の定義 ===
ユーザーが自分で定義する関数について、記法の例を表す。
<source lang=c>
//例 2つの整数を引数にとりその和を返却する関数
24 ⟶ 25行目:
関数の定義の記述は次のようになっている。
<source lang=c>
返却値のデータ型 関数名(引数の型と引数名)
30 ⟶ 32行目:
}
</source>
関数名とはその関数を他の関数と区別するために付ける名前のことで、
関数名に使えるのは半角英数、「_(下線)」である。
36 ⟶ 39行目:
関数は他の関数の中で呼び出すことができる。
また、引数は、省略
<source lang=c>
返却値のデータ型 関数名()
43 ⟶ 47行目:
}
</source>
のように関数を記述する場合もある。
80 ⟶ 85行目:
:まず、パソコン視点から見れば、たとえポインタやアドレス機能を使って子サイドを呼び出したところで(たとえアドレスを実引数(親サイド関数の引数)として呼び出しところで、ポインタを仮引数(子サイド(呼び出し先)関数の引数)として呼び出したところで、)パソコンは、通常の関数呼び出しで引数を新アドレスにコピー代入するのと同様に、その引数にされたポインタの値やアドレスの値も新アドレスにコピー代入しようとするのである。(けっして、呼び出し方法時のコピー作業が、ポインタ使用時と非使用時で有無が変わるわけではない。)
:なので、アドレスが引数にされた関数呼び出しのさいにも、パソコンは新アドレスに親(呼び出し元)引数のアドレス値をコピーする。
:だが、関数呼び出しのさいに新アドレスに親サイド(呼び出し元)引数のアドレス値をコピーしたところで、けっして、ポインタに格納されている大もとの親サイド(呼び出し元)引数のアドレスの種類が増えるわけではない。
97 ⟶ 102行目:
このC言語の「ローカル変数」システムこそが、C言語の「関数」システムの真骨頂(しんこっちょう)なのです。
通常仕様のベーシックでサブルーチンを呼び出しても、けっして、ルーチン内で使用する変数に呼び出し前の変数とは別アドレスは与えられませんし、そもそもサブルーチン呼び出し時に「引数」のコピーをしませんし、そもそもサブルーチン呼び出しに「引数」は不要ですし、そもそも「引数」のような概念じたいベーシックのサブルーチンの仕様には無いのです。
ベーシックでのサブルーチンの呼び出しは、単に「GOTO 100」みたいにGOTO文で行指定して指定先にジャンプするかわりに、行番号100にルーチン名を書いておいて、ルーチン名でジャンプ先を指定できるようにしたものに過ぎません。
117 ⟶ 122行目:
「関数」というが、C言語の関数(function)は、数学の関数(function)とは、まったく違う。
これが
Visual C++やC#などの言語でいう「メソッド」は、上述のC言語の「関数」と、ほぼ同じ機能、もしくは機能拡張したものである。
130 ⟶ 135行目:
<source lang=c>
// static変数の使用例(コンパイルできる)
#include <stdio.h>
#include <stdlib.h> // 「続行するには何かキーを押してください . . .」を表示するのに必要。
164 ⟶ 169行目:
また、初期化がされる(cに0が代入される)のは、最初に呼び出された1回だけである。
なお、下記のように書くと、エラーになる
<source lang=c>
// static宣言でエラーになる例1
#include <stdio.h>
#include <stdlib.h> // 「続行するには何かキーを押してください . . .」を表示するのに必要。
192 ⟶ 198行目:
}
</source>
このように、static変数は、呼び出し元の関数は、存在を感知できない仕組みである。これによって、バグ発生時に原因箇所を特定しやすくしている。
210 ⟶ 215行目:
int main(void) {
int b = 0; // main関数で定義を行った場合
static int c = 0;
237 ⟶ 242行目:
なお、最初の「例1」で紹介した例では、main関数で使用される変数bとcの上にある関数kansuuで定義されてるので、順序的には問題ないのですが、しかしC言語では、アクセスできないようになっています。staticかどうかに関わらず、アクセスできません。kansuuの終了時に、その関数の型などの情報も消えるので、エラー例1のコードでは変数bとcの宣言内容を発見できず、利用できない。
結局、冒頭に書いたコンパイル成功例のような書き方しか、ありません。
実用では、子サイド(呼び出し先)の関数で、親サイド(呼び出しもと)の変数の値も変更したい場合も多く、その場合には、「 static 変数」(静的変数)もしくは「ポインタ」という機能をつかう。▼
などと static をつけて宣言すると、その名前の変数は、どこの変数で呼び出されても、共通のアドレスで管理しているので、呼び出した(子サイドの)関数の先で、親サイド(呼び出し元の)関数も書き換えできる機能をもつ。▼
▲いったい何が「静か」(static)なのか不明であるが、おそらく、関数呼び出し時にもアドレス領域を新規確保しないままでいるという意味で、「静か」なのだろう。
なお、static宣言した変数を、他の関数とも共有したい場合には、さらに「グローバル変数」という物を使います。
すべての関数ブロックの外の、コード前半部の部分(グローバル領域)で、変数宣言をすると、その変数は、すべての関数からアクセスできます。
▲実用では、子サイド(呼び出し先)の関数で、親サイド(呼び出しもと)の変数の値も変更したい場合も多く、その場合には、「グローバル変数としての static 変数」(静的変数)もしくは「ポインタ」という機能をつかう。
▲
さて、C言語の入門書での「関数」の章の前半に書いてあるプログラムで、static変数やグローバル変数やポインタを使わなくても利用できる(ユーザー定義)関数は、むしろ珍しいプログラムなのである。画面に「こんにちは!」などの文字などを表示するだけなら、
なお、「return a;」などのように戻り値をつかって 計算結果の数値を 親(呼び出しもと)の関数に送信したりする方法は、じつは、あまり実用的ではなく、現実には不便な場合が多いのである。
366 ⟶ 370行目:
通常のローカル変数は、関数ブロックが終了するときに、変数の結果が消去される。そして、再度関数を呼び出したときは、変数の宣言時に変数が作成されたり、あるいは関数ブロックに入るときに作成され、そしてまた、その関数ブロックが終了するときに、消去される。
この消去により、
たとえば、高校数学で習うような数列の漸化式のような計算ですら、困難になってしまう。(数列の項を、関数に見立てている。)
416 ⟶ 420行目:
</pre>
なので、他の関数と計算結果を共有する必要のない変数であれば、なるべく、個別の関数の内部で変数宣言をするのが良い。
いっぽう、他の関数と計算結果を共通したい場合、どうしてもグローバル変数を使わざるを得ない場合も多い。(もし、グローバル変数を用いないと、代わりにポインタを使わざるを得ない場合がある。)
これはつまり、そもそも、複数の関数どうしでの変数の共有は、なるべく、ひかえる必要がある、という事である。
|