- ベストアンサー
続ポインタによる関数への配列渡し
連続での質問すみません。 林晴比古さんの「新C言語入門」で勉強している初心者です。 ポインタを勉強中で、色々な例文をポインタで書けるかどうか 試しております。 上書P199に「安全な数値入力を行うプログラム」が出ています。 これは入力時問題点を抱えるscanfに入力させるのでなくchar型に入力させ、 それをint型に変換して出力するという内容で、案内メッセージも関数内で表示することになっています。 以下そのプログラムを引用します。 #include <stdio.h> #include <stdlib.h> int getint(char msg[]); int main(void) { int n; n = getint("数値を入力してください:"); printf("入力した数値=%d\n",n); return 0; } int getint(char msg[]) { char ss[80]; printf("%s",msg); gets(ss); return atoi(ss); } (以上林晴比古氏「新C言語入門」P199より引用) これをポインタによって書き換えようとしているのですがうまくいきません。 「本引数として主文でint型のnを設定し、それを関数側のchar型のssをポインタにして 仮引数として受け取れば、最後にreturnで返さなくても、参照できるのでないか」 と思い色々試してみましたがうまくいきません。 どうもコンパイルのエラーを確認すると型が違うので駄目なようです。 なるほどそれはそうでした… それ以外の方法も色々試してみましたが、結局うまくいきませんでした。 どのようにすればポインタでは上の文章を表現できるのでしょうか。 (あるいは表現出来ないのでしょうか) お分かりの方、よろしくお願いします。
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
ポインタを理解する前に、データが何処にあるか、誰がその所在を 知っているか、ということを整理しましょう。データは以下の通り。 (1)画面から人間がキーボードで入力する文字列 (2)最終的に変換した結果である数値 既存のプログラムで考えると、(1)のデータは関数getintの中にあり、 getintだけが所在を知っています。故に、それを知らないprintf、 gets、atoiという関数には文字列のアドレス(ポインタ)を教えて います。配列データを添え字を付けずに表現するとポインタになる ので、atoi(ss)のように記述します。atoi(&ss[0])でも結構です。 (2)のデータは戻り値として返すので、データとしてはCPU内の一時的 データ(レジスタ)を介して受け渡されます。 このデータをmain側で用意すると、getintはその場所を知らないので、 それを教えてもらう必要があります。それで、以下のようになります。 void getint(char msg[], int *n) { ★戻り値はないので、void char ss[80]; printf("%s",msg); gets(ss); *n = atoi(ss); } int *n はnの値ではなく、nの場所という意味です。従って、 *n = atoi(ss); はatoiで変換した数値を教えてもらったnの場所に 格納するという意味です。main側は次のようになります。 getint("数値を入力してください:", &n); 第2引数はnの内容ではなく、nのアドレスを示します。 尚、本題のように基本データ型(intなど)×1個を返す処理では 戻り値を介してデータを受け渡すほうが、処理効率が良いとは言え ます。(メモリよりレジスタの方が処理速度が速い)
その他の回答 (4)
- trapezium
- ベストアンサー率62% (276/442)
安全というのなら、 if(scanf("%d",&d)!=1) err("err"); とかすべきですし、scanf()で"%s"は使うべきでありません。 一旦バッファに読むのならgets()ではなくfgets()を使うべきで、それも改行コードがなければ残りの入力を読み飛ばすか、エラーにするべきです。 if (fgets(buf, sizeof(buf), stdin) == NULL) err("eof or ferror"); if (buf[strlen(buf) - 1] != '\n') err("overrun"); それとatoi()だと数値以外の入力チェックできませんから、前もって必要なフォーマットでトークン切り出すか、strtol()でバッファの残りをチェックすべきです。 n = strtol(buf, &endptr, 10); if (*endptr != '\0') err("format"); ついでに言うならよく使いがちなscanf(),printf()関数群ですが、バッファオーバーには常に要注意で、sprintf()はsnprintf()に置き換えるべきですし、"%s"関連は前もって文字数の把握が必要です。
お礼
回答ありがとうございます。 自分にはまだ難しい部分もありますが、今後への参考にさせていただきます。 ありがとうございました。
#3です。atoi忘れてました。あとscanf使いました。 #include <stdio.h> #include <stdlib.h> void getint(char msg[],char *); int main(void) { char ss[80]; getint("数値を入力してください:",ss); return 0; } void getint(char msg[],char *a) { printf("%s",msg); scanf("%s",a); printf("入力した数値=%d\n",atoi(a)); }
お礼
どうもありがとうございました! 自分が最初に考えていたのはこんなイメージでした。 しかしどうしてもうまくいきませんでした。 atoiはint型を宣言しなくてもこのような使い方が出来るのですね。 こうすると主文側も関数側もchar型メインで 分かりやすいですね。 勉強になりました。ありがとうございました。
#include <stdio.h> #include <stdlib.h> void getint(char msg[],char *); int main(void) { char ss[80]; getint("数値を入力してください:",ss); return 0; } void getint(char msg[],char *a) { printf("%s",msg); gets(a); printf("入力した数値=%s\n",a); } こういうことでしょうか・・・? ↑のソースコードでは、getintの仮引数char *aにss(ポインタssは配列ss[]の先頭アドレスが入っている)を渡し、そのポインタを使ってssの中身を読み書きしています。あと gets(a); の部分は scanf("%s",a); にした方がいいですよ。getsは色々と危険な関数ですから。
- notnot
- ベストアンサー率47% (4900/10358)
悪い本を選びましたね。「安全な数値入力を行うプログラム」といいつつ、getsを使ってる。 もし、80文字以上入力されたらどうなると思いますか? >「本引数として主文でint型のnを設定し、それを関数側のchar型のssをポインタにして仮引数として受け取れば、最後にreturnで返さなくても、参照できるのでないか」 ちょっと間違ってます。 ちゃんとした本で勉強し直しましょう。
お礼
回答ありがとうございます。 はい、ポインタについてさらに勉強し直したいと思います。
お礼
考え方のところから、わかりやすい解説をいただきありがとうございます。 大変よく理解できました。 主文側のint型nに対応させて、 関数側にもそれを受けるint型の引数を用意すればよかったのですね。 最後の*n = atoi(ss);っていうのは考え付きませんでした… どうもありがとうございました。