• ベストアンサー

続ポインタによる関数への配列渡し

連続での質問すみません。 林晴比古さんの「新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で返さなくても、参照できるのでないか」 と思い色々試してみましたがうまくいきません。 どうもコンパイルのエラーを確認すると型が違うので駄目なようです。 なるほどそれはそうでした… それ以外の方法も色々試してみましたが、結局うまくいきませんでした。 どのようにすればポインタでは上の文章を表現できるのでしょうか。 (あるいは表現出来ないのでしょうか) お分かりの方、よろしくお願いします。

  • myun
  • お礼率91% (32/35)

質問者が選んだベストアンサー

  • ベストアンサー
  • nda23
  • ベストアンサー率54% (777/1415)
回答No.2

ポインタを理解する前に、データが何処にあるか、誰がその所在を 知っているか、ということを整理しましょう。データは以下の通り。 (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個を返す処理では 戻り値を介してデータを受け渡すほうが、処理効率が良いとは言え ます。(メモリよりレジスタの方が処理速度が速い)

myun
質問者

お礼

考え方のところから、わかりやすい解説をいただきありがとうございます。 大変よく理解できました。 主文側のint型nに対応させて、 関数側にもそれを受けるint型の引数を用意すればよかったのですね。 最後の*n = atoi(ss);っていうのは考え付きませんでした… どうもありがとうございました。

その他の回答 (4)

  • trapezium
  • ベストアンサー率62% (276/442)
回答No.5

安全というのなら、 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"関連は前もって文字数の把握が必要です。

myun
質問者

お礼

回答ありがとうございます。 自分にはまだ難しい部分もありますが、今後への参考にさせていただきます。 ありがとうございました。

noname#113783
noname#113783
回答No.4

#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)); }

myun
質問者

お礼

どうもありがとうございました! 自分が最初に考えていたのはこんなイメージでした。 しかしどうしてもうまくいきませんでした。 atoiはint型を宣言しなくてもこのような使い方が出来るのですね。 こうすると主文側も関数側もchar型メインで 分かりやすいですね。 勉強になりました。ありがとうございました。

noname#113783
noname#113783
回答No.3

#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% (4846/10257)
回答No.1

悪い本を選びましたね。「安全な数値入力を行うプログラム」といいつつ、getsを使ってる。 もし、80文字以上入力されたらどうなると思いますか? >「本引数として主文でint型のnを設定し、それを関数側のchar型のssをポインタにして仮引数として受け取れば、最後にreturnで返さなくても、参照できるのでないか」 ちょっと間違ってます。 ちゃんとした本で勉強し直しましょう。

myun
質問者

お礼

回答ありがとうございます。 はい、ポインタについてさらに勉強し直したいと思います。

関連するQ&A

  • ポインタによる関数への配列渡し

    林晴比古さんの「新C言語入門」でC言語を勉強している初心者です。 現在ポインタの勉強をしています。色々教科書の文例等をポインタで書くとどうなるか試しております。 上書P200練習問題2に「配列の最大値を返す(その際配列の長さを渡す)」プログラムがあり、それをポインタで渡すプログラムに直してみました。 仮引数に「maxdata」を設定し、そのアドレスを関数側に渡し、関数側ではポインタとして受け取る(そうすれば関数側からはreturnで値を返す必要がない)、と考え、下記のように書いてみました。 #include <stdio.h> void max_of_array(int n[], int len, int *ans); int main(void) { int dt[6] = {50,20,80,30,10,40}; int maxdata; max_of_array(dt,6,&maxdata); printf("最大値=%d\n", maxdata); return 0; } void max_of_array(int n[], int len, int *ans) { int i; ans = &n[0]; for (i=1; i<len; i++){ if (*ans < n[i]) *ans = n[i]; } } しかしコンパイルすると、何故か「最大値=1」となってしまいます。(正しくは80です) 他にも色々試してみましたがうまくいかず、かなり考えてみたのですがどうしても分かりません。お分かりの方、どうすれば正しくなるのが教えてください、よろしくお願いします。

  • ポインタ渡しについて

    関数の引数をint型のポインタで出力し、その関数を呼び出し側で値を取得するにはどうしたらいいのでしょうか? 一応ソースを作成しました(下記参照)が、ほしい値が出てきません。 よろしくお願い致します。 ----------------------------------------------- #include <stdio.h> int check(int *num){ int n=10; num = &n; return 0; } int main() { int m_num; check(&m_num); printf("%d\n",m_num); return 0; } ----------------------------------------- m_numに、10が格納されません。 check()でアドレス渡しをしたのですが・・・。 どうすれば、main関数で、数値を扱うことが出来ますか? よろしくお願い致します。

  • ポインタと配列

    次のソースで、結果表示でポインタを使いたいのですが、うまくいきません。1件しか表示されないのです。 ポインタの扱いがおかしいのだと思いますが、どうしたらよいでしょうか? #include <stdio.h> #include <string.h> int search(char key[256],FILE *fp,char *result[256][256]); main(void) { FILE *fp; int rep,n,i; char x[256],key[256],*result[256][256]; printf("検索キーワードを入力してください。\n" "キーワード>"); gets(key); if((fp=fopen("personal.txt","r"))==NULL) { printf("ファイルをオープンできません\n"); exit(1); } printf("=====検索結果=====\n"); n=search(key,fp,result); for(i=0;i<n;i++) { printf("%s\n",result[i]); } printf("検索結果:%d件です。\n",n); fclose(fp); } int search(char key[256],FILE *fp,char *result[256][256]) { int n=0; char *p,word[256],*name; while((p=fgets(word,256,fp))!=NULL) { if(strstr(word,key)!=NULL) { name=strtok(p," "); strcpy(result[n],name); n++; } } return n; } 実行すると、下の警告がでます。 illegal pointer combination(param)

  • ポインタについて

    今初めてポインタというものを勉強しております。 よろしくお願いします。 ◎1---------------------------------- #include<stdio.h> int main(void) { int mydt=1234; int *pt; pt=&mydt; printf("*pt=%d\n",*pt); printf("&mydt=%p\n",&mydt); return 0; } --------------------------------------- ◎1のようにmydtのアドレスをポインタptに代入すれば、このプログラムは正常に動きました。 ◎2----------------------------------- #include<stdio.h> int main(void) { int mydt=1234; int *pt=&mydt; printf("*pt=%d\n",*pt); printf("&mydt=%p\n",&mydt); return 0; } ---------------------------------------- ◎2で「int *pt=&mydt;」があまりどういう意味かはわかりませんが、これも正常に動きました。 ◎3------------------------------------ #include<stdio.h> int main(void) { int mydt=1234; int *pt; *pt=&mydt printf("*pt=%d\n",*pt); printf("&mydt=%p\n",&mydt); return 0; } -------------------------------------- ◎3のように◎2と違って「*pt=&mydt」の代入を後から行うと、「'=' : 'int *__w64 ' から 'int' に変換できません。」といったようなエラーが起きてしまいます。 ◎1と◎2の違い、後何故◎3はダメなのかがわかりません。 教えていただけると嬉しいです。 後補足として、配列とポインタについてですが、 ◎4------------------------------ char ss[10]="ABCDE"; char *ssp=ss; --------------------------------- ◎5---------------------------- char ss[10]="ABCDE"; char *ssp; ssp=ss; -------------------------------- ◎4と◎5も同じような事だとは思いますが違いを教えていただけると嬉しいです。 よろしくお願いします。

  • ポインタ勉強中です。しかも実行するとおかしいです。

    <本に載ってたソース> #include<stdio.h> #include<string.h> int main() { char msg[20]; char *str=NULL; int i; int cnt; str=&msg[0]; printf("文字を入力してください"); scanf("%s",&str); cnt=strlen(msg); str=msg+cnt; for(i=cnt;i>=0;i--){ printf("%c",*(str--)); } printf("\n"); return 0; } char *str=NULL;は、ポインタstrを空にするということでしょうか? いつもながらstrlenとsizeofが混じります。 sizeofがバイトの大きさで、strlenが、文字数でしたっけ?

  • 配列について

    初歩的な質問ですいませんが、質問よろしくお願いします。 ◎1----------------------------- #include<stdio.h> int main(void) { char ss[10]="AB"; printf("ss=%s\n",ss); return 0; } ------------------------------------ ◎2-------------------------------- #include<stdio.h> int main(void) { char ss[10]; ss[0]='A'; ss[1]='B'; ss[2]=0; printf("ss=%s\n",ss); return 0; } ----------------------------------- ◎3------------------------------- #include<stdio.h> #include<string.h> int main(void) { char ss[10]; strcpy(ss,"AB"); printf("ss=%s\n",ss); return 0; } ----------------------------------- ◎4------------------------------- #include<stdio.h> int main(void) { char ss[10]; ss="AB"; printf("ss=%s\n",ss); return 0; } ---------------------------------- 以上4つのプログラムで、◎2と◎3は正常に動くと理解できたのですが、何故、◎1は正常に動き、◎4は「'const char [3]' から 'char [10]' に変換できません。」といったようなエラーが出てしまうか分かりません。 教えていただければ嬉しいです。

  • ポインタのポインタが引数にある関数の使い方。

    ポインタのポインタが引数にある関数の使い方。 現在、このポインタのポインタが引数にある関数の動きがわからず困っています。 int test(int ** head) { int * pTail = (int *)*head; pTail = pTail + 1; } もし、この関数を呼び出して使用した場合どのような動きをするのでしょうか? int * comm_msg; これをグローバルポインタ変数として宣言させて、 test((void **)&comm_msg); このように呼び出したとした場合とさせていただきます。

  • ポインタ配列について

    /************配列 change1.c ***********/ #include <stdio.h> void change(char *c) { c[0]='a'; c[1]='b'; c[2]='c'; } int main(int agrc, char **agrv) { change(*++agrv); printf("%s",*agrv); return(0); } /**********ポインタ change2.c ***********/ #include <stdio.h> void change(char *c) { c="abc"; } int main(int agrc, char **agrv) { change(*++agrv); printf("%s",*agrv); return(0); } ********************* この2つのプログラムchange1.c,change2.cにおいて、次のようにしたとき結果がこうなります \change1.exe 123 abc \change2.exe 123 123 change2.c において、ポインタでの文字の格納 c="abc"; は何故実行されないのですか?

  • ポインタ配列の動的確保

    ポインタの配列の動的確保について教えてください。 入力した数値をポインタ配列に入れるプログラムです。 下記のように書いてみました。(見づらくてごめんなさい) #include<stdio.h> #include<stdlib.h> #define kensu 3 main() { char abc[kensu+1]={'A','B','C','\0'}; char *ptr[kensu]; int i; printf("3つの整数を入力して下さい。\n"); for(i=0;i<kensu;i++){ ptr[i]=(char*)malloc(sizeof(char)*10); if(ptr[i]==NULL){ printf("メモリの取得に失敗しました"); exit(1); } printf("整数%c:",abc[i]); fgets(ptr[i],10,stdin); if(ptr[i][strlen(ptr[i])-1]=='\n') ptr[i][strlen(ptr[i])-1]='\0'; } for(i=0;i<kensu;i++) free(ptr[i]); } ちゃんと動いているようです。 しかし、ポインタ配列の動的確保をネットで調べてみると、ポインタのポインタ(?)を使って、下記のように2度mallocしています。 #include <stdio.h> #include <stdlib.h> #define N 3 int main(void) { char** arr; int i,j; arr = (char**)malloc(N * sizeof(char*)); /* ポインタ配列を確保 */ /* 配列の要素それぞれにつき、メモリ領域を確保 */ for(i=0;i<N;i++) arr[i] = (char*)malloc(N * sizeof(char));   ・・・ ポインタの配列を宣言して、配列の各要素に動的確保するのと ポインタのポインタを宣言し、ポインタ配列を動的確保して、再度配列の要素に動的確保するのとでは、何か違いがあるのでしょうか? ポインタのポインタを宣言し、ポインタ配列を確保する必要性が良く分かっていないのです。 ネット等で調べて見たのですが、理解力がないのかよく分かりませんでした。 どうか教えてください。

  • ポインタ配列

    "one","two","three","four","five","six","seven","eight","nine","ten" のポインタ配列の文字列を、ASCIIコード順に並べ変えようと思ったのですが、 もうどこが間違っているかさえわからないぐらいになってしまいました。 まだまだはじめたばかりなもので、わからないことだらけなんで、 できるだけわかりやすい説明おねがいします。 関数の引数に問題があるのじゃないかと思ったのですが、 何かいいアドバイスありましたら、お願いします。 #include <stdio.h> /* 関数のプロトタイプ宣言 */ int strmp(char *,char *); void cpy(char *,char *); int main (void) { /* ポインタ配列の定義 */ char *x[10]={"oneee","twooo","three","fourr","fivee","sixxx","seven","eight","ninee","tennn"}; /* ポインタのポインタの定義 */ char **pp=x; char k[100]; char *p=k; int i,t,a,b,c,d; a=0; /* ポインタ配列を自作関数を使って、ASCIIコードの大きいほうからに並び替える */ for(i=0;i<9;i++) { for(t=1;t<10;t++) { a=strmp(*(pp+i),*(pp+t)); if(a<0) { cpy(p,*(pp+i) ); cpy(*(pp+i),*(pp+t) ); cpy(*(pp+t),p); } } } for(i=0;i<10;i++) { printf("%s ,",x[i]); } printf("\n"); return 0; } /* 文字の比較をする関数 */ int strmp(char *x,char *y) { int i; for(i=0;*(x+i)==*(y+i);i++) { if( *(x+i)=='\0') { return 0; } } return *(x+i)-*(y+i); } /* 文字をコピーする関数 */ void cpy(char *a,char *b) { int i; for(i=0;*(b+i)!='\0';i++) { *(a+i)=*(b+i); } *(a+i)='\0'; }

専門家に質問してみよう