値渡し、ポインタ渡しについて

アバター
BEAT
記事: 4
登録日時: 14年前
住所: 兵庫県S市杜王町
連絡を取る:

値渡し、ポインタ渡しについて

投稿記事 by BEAT » 14年前

前回の日記でわかったことは

・値の変更がなく、値だけがほしい時は「値渡し」
・値の変更が必要な場合は「ポインタ渡し」

その後いろいろ考えたんですがまたまた疑問点が。

sftyaさんからいただいた「プログラミングの禁じ手」で見たんですが

「グローバル変数が多すぎる」「構造体のメンバ変数が多すぎる」

というのはかなり深刻な問題なようですね。
俺のゲームは完全にその二つに当てはまっているので、これを直そうと考えました。

まず「グローバル変数を減らす」ということについて

すっごい馬鹿らしい質問かもしれませんが

main.c、A.c、B.c、とhead.hいうファイルがあって
mainは関数のコールが記述してあって、AとBに関数本体があるとします。

AとB両方で使わなければならないグローバル変数はヘッダファイルでexternで宣言しますよね?
そのグローバル変数を減らすってことは、AかBかmainかのローカル変数にしなければならない。

ここで疑問に思ったんですが。

このローカル変数を置く場所は関数のコールをするmainに置いておくのが普通なんでしょうか?

自分の考えだと「値の変更をする、しない、にかかわらず関数の引数に必要なんだからmainに書くのが普通」と思うんですが・・・

でも「グローバル変数を計算した結果を返す関数」ならAやBのローカル変数でもできますよね。

その辺がちょっとよくわからないのですが・・・
最後に編集したユーザー BEAT on 2011年6月18日(土) 08:48 [ 編集 1 回目 ]

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前

Re: 値渡し、ポインタ渡しについて

投稿記事 by softya(ソフト屋) » 14年前

ファイル内に閉じる変数でファイル内の関数で参照する場合は関数外のstaticにします。ファイルスコープの変数ですね。
関数内だけで参照する場合はローカル変数にします。関数内スコープの変数です。
その場合でも出来るだけ引数で渡します。

それ以外のファイル間で参照できる変数が俗に言うグローバル変数です。
それとA.cとB.cのファイル間で参照する変数(構造体)は出来るだけ引数で渡します。
私の経験が言えば、ファイル分けが的確ならそんなに多くなることは殆どありません。

あっ後値を返すと言っても戻り値か引数で返すのが基本ですよ。
なぜグローバル変数で戻すのがダメかと言うとソースコードを追いかけているときに関数を呼び出で何が変更されるか関数内のコードを見ないと分からないからです。
ちゃんと引数と戻り値で処理されていて関数名が的確なら関数の中を見えなくてもどういう動作をしてどういう値を戻してくれる関数か分かるはずです。
最後に編集したユーザー softya(ソフト屋) on 2011年6月18日(土) 00:46 [ 編集 1 回目 ]

アバター
へろりくしょん
記事: 92
登録日時: 14年前

Re: 値渡し、ポインタ渡しについて

投稿記事 by へろりくしょん » 14年前

どっちに置かなければならないといったルールはありませんよ。

通常、プログラムはファイルにしろ、関数にしろ、クラスにしろ、機能別に分割します。
そして、データは誰が管理するのが、一番自然かを考えて各機能に所持させます。

一般にこれらの機能は、階層構造となるようにするのが理想的です。
そうする事で、データの受け渡しは、基本的に上流から下流へ流れるようになりますし、どのデータがどのレベルの階層で必要なのかが明確になります。
また、特定の階層に閉じこめる事が出来るので、よりセキュアになりますね。

グローバル変数を使用すると、この流れを思いっきりぶったぎっちゃいます。

この当たりを意識すると、そもそも本当にグローバル変数でなければならないところなんて、相当限られている事に気づくはずです。
後ついでに、リエントラントを意識すると、きっとより一層幸せになれると思いますよ。

アバター
BEAT
記事: 4
登録日時: 14年前
住所: 兵庫県S市杜王町
連絡を取る:

Re: 値渡し、ポインタ渡しについて

投稿記事 by BEAT » 14年前

siftyaさんへ

すみません。確認と質問をさせてもらいます

>ファイル内に閉じる変数でファイル内の関数で参照する場合は関数外のstaticにします。ファイルスコープの変数ですね。
「そのファイル全体で扱い、かつ他のファイルでは扱わない変数」にstaticを付けるということですよね?

>それとA.cとB.cのファイル間で参照する変数(構造体)は出来るだけ引数で渡します。
これは構造体のメンバ変数の値を変更するしないに関わらず、引数で渡す方がよいということでしょうか?

>あっ後値を返すと言っても戻り値か引数で返すのが基本ですよ。
「引数で返す」というのはポインタで受けったアドレスの値を変更するということでしょうか?

どうやらポインタ以外にも関数やらなにやらさらに深く勉強しなおす必要がありそうです。頑張ります!
最後に編集したユーザー BEAT on 2011年6月18日(土) 08:38 [ 編集 1 回目 ]

アバター
BEAT
記事: 4
登録日時: 14年前
住所: 兵庫県S市杜王町
連絡を取る:

Re: 値渡し、ポインタ渡しについて

投稿記事 by BEAT » 14年前

へろりっちさんへ

なるほど、機能別、役割別に分割されるのなら必要になる情報も限られてくるから
どこからでも値をいじれるグローバル変数を使うよりも全体の流れがわかりやすくなっていいということですね。

「リエントラント」って簡単に言うと「グローバル変数を使わない」ってことでいいですよね?
最後に編集したユーザー BEAT on 2011年6月18日(土) 08:49 [ 編集 1 回目 ]

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前

Re: 値渡し、ポインタ渡しについて

投稿記事 by softya(ソフト屋) » 14年前

>ファイル内に閉じる変数でファイル内の関数で参照する場合は関数外のstaticにします。ファイルスコープの変数ですね。
「そのファイル全体で扱い、かつ他のファイルでは扱わない変数」にstaticを付けるということですよね?
そう言うことです。まぁ、これも減らせるだけ減らしたほうが無難ですし、出来るだけ引数渡しを心がけてください。
ただ、スタック容量にも限界があるのでむやみにローカル変数にするのも考えものですし、staticじゃないと値を保持してくれない問題もあります。

見える範囲を制限するって事では、外部公開の必要のない関数にstaticを付けることも重要ですね。
>それとA.cとB.cのファイル間で参照する変数(構造体)は出来るだけ引数で渡します。
これは構造体のメンバ変数の値を変更するしないに関わらず、引数で渡す方がよいということでしょうか?
そうです。出来るだけ引数にすることで、その関数がどの構造体に関わるのか明確になります。
それと構造体の値渡しとポインタ渡しを使い分けて、この関数は構造体の内容を変更するのか参照するのかを明確にしたほうが良いでしょう。
>あっ後値を返すと言っても戻り値か引数で返すのが基本ですよ。
「引数で返す」というのはポインタで受けったアドレスの値を変更するということでしょうか?
※ポインタで受けったアドレスの値を変更
ちがいますね。ポインタの指し示す先の変数の内容を変更する事です。
それと、ポインタ渡ししたものは値が入って返ってくると考えるべきだと思います。

ポインタ渡しの例:

CODE:

int setA(int *a)
{
 *a = 2;
}

int main()
{
 int b = 1;
 setA(&b);
 printf( "%d\n",b); 
}
どうやらポインタ以外にも関数やらなにやらさらに深く勉強しなおす必要がありそうです。頑張ります!
ソースコードを見せてもらえば、もっと細かいアドバイスが出来ると思います。
最後に編集したユーザー softya(ソフト屋) on 2011年6月18日(土) 12:33 [ 編集 1 回目 ]

アバター
へろりくしょん
記事: 92
登録日時: 14年前

Re: 値渡し、ポインタ渡しについて

投稿記事 by へろりくしょん » 14年前

BEAT さんが書きました:「リエントラント」って簡単に言うと「グローバル変数を使わない」ってことでいいですよね?
リエントラントであるためには、グローバル変数を使わない事が最低条件になりますが、それだけでなく。
関数に必要なデータは呼び出し元から与えて貰うようにし、関数内部で static な変数を宣言してもいけませんし、リエントラントでない関数を呼び出してもいけません。

ただ、今回私が言いたいのは、データはそのデータを必要とする機能に可能な限り閉じこめてしまいましょう。 ということです。
関数で言えば、1関数1機能を心がけ、呼び出し元はデータを渡す。 関数は結果を返す。 というシンプルな構造を保持しましょうという事です。

アバター
BEAT
記事: 4
登録日時: 14年前
住所: 兵庫県S市杜王町
連絡を取る:

Re: 値渡し、ポインタ渡しについて

投稿記事 by BEAT » 14年前

関数で言えば、1関数1機能を心がけ、呼び出し元はデータを渡す。 
>関数は結果を返す。 というシンプルな構造を保持しましょうという事です。

わかりました。今後、心がけていきたいと思います。