C言語で二次元配列の関数化と初期値設定の方法について

このQ&Aのポイント
  • C言語で二次元配列を関数化する方法について悩んでいます。特に第一引数の処理が上手くいかないようです。
  • 質問のコードを読みましたが、関数の内部を変更せずに上手くいく方法はありますか?
  • また、char型の二次元配列に対しても同様の関数を作成したい場合は可能でしょうか?
回答を見る
  • ベストアンサー

引数として二次元配列を利用する方法について(C言語)

#define PR_SIZE 40 char hp[PR_SIZE][3]; char *initial; initial = "aa,aa"; GetPrivateProfileString("Setting", "hp", initial, strText, 1024, ini_path); initial = strtok(strText, ","); for(i = 0; i < PR_SIZE; i++) strcpy(hp[i], "\0"); for(i = 0; initial != NULL && i < PR_SIZE; i++){   strcpy(hp[i], initial);   initial = strtok(NULL, ","); } という部分を関数化しようとして、 ReadIni_pr(&(pr.hp[0][3]), "hp", "aa,aa"); //関数呼び出し void ReadIni_pr(char *a[], const char* str, char* initial){   char strText[128];   GetPrivateProfileString("Setting", str, initial, strText, 1024, ini_path);   for(int i = 0; i < PR_SIZE; i++)     strcpy(a[i], "\0");   initial = strtok(strText, ",");   for(int i = 0; initial != NULL && i < PR_SIZE; i++){     strcpy(a[i], initial);     initial = strtok(NULL, ",");   } } と書いてみたのですが、第一引数の処理がどうしても上手くいきません できるだけ関数の内部を変更しない形で、上手くいく方法はないでしょうか (インクルードに関しては省略してあります) あと、char sp[PR_SIZE][5];に関しても同様の関数で処理したいのですが、可能でしょうか?

  • vtr9
  • お礼率50% (5/10)

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

  • ベストアンサー
  • asuncion
  • ベストアンサー率33% (2126/6286)
回答No.3

現行の > char hp[PR_SIZE][3]; を当該関数に渡す場合は、 > void ReadIni_pr(char *a[], const char* str, char* initial){ を void ReadIni_pr(char (*a)[3], const char* str, char* initial){ とすればよいと思います。第一引数の意味は、 「aは、要素数3の配列へのポインタ」です。 こうすれば、呼び出し時の > ReadIni_pr(&(pr.hp[0][3]), "hp", "aa,aa"); は ReadIni_pr(pr.hp, "hp", "aa,aa"); でよいと思います。 なお、 > char sp[PR_SIZE][5];に関しても同様の関数で処理したい のであれば、可能ならば当該配列の定義を char hp[3][PR_SIZE]; や char sp[5][PR_SIZE]; と変更して、当該関数を void ReadIni_pr(char (*a)[PR_SIZE], const char* str, char* initial){ とすれば、当該配列の第一要素数がいくつであっても対応できます。

vtr9
質問者

お礼

なるほど。その場所を括弧で囲むという考えが出てきませんでした。 a[][3]をポインタへ変形させたという捉え方でしょうか。 a[][3] → (a[])[3] → (*a)[3] といった具合に。 (真ん中はソースとしては変ですが…) sp[PR_SIZE][5]に関しては、文字列なので逆転させると別の箇所での扱いが不便になるため、別の関数を使ってみます。 仮に*a[3]としたときには、a[3][]の方でしょうか。 これだと関数の引数としては不適格ではありますが。

vtr9
質問者

補足

一応、C++も扱える状況なので、引数だけを代えて同名で別の関数を作っておくことで回避してみます ありがとうございました

その他の回答 (2)

  • bushclean
  • ベストアンサー率26% (6/23)
回答No.2

>ReadIni_pr(&(pr.hp[0][3]), "hp", "aa,aa"); //関数呼び出し あ、宣言細かく見てませんでした。 pr.hp[0][2]ですよね?

vtr9
質問者

補足

ReadIni_pr(&(pr.hp[0][3]), "hp", "aa,aa"); //関数呼び出し すいません、元々構造体だったものを簡略のために省いていたのですが、消し忘れてしまっていました。 ReadIni_pr(&hp[0][3], "hp", "aa,aa"); //関数呼び出し と解釈してください。 ちなみにこの &hp[0][3] の部分なのですが、暫定的にこういった状態になっているだけであって、例えば、 hp、hp[3]、&hp[0][0] などとしても上手く動作しません(型の合わないものも含まれますが、そのレベルも含めて動作云々というよりもコンパイルのレベルでの問題でもあります) さらに関数内にあるstrcpy(a[i], "\0");の部分に関して上手く動作するようにしたい(ここを動作させることが目的です)ので、できればこれを主眼において引数を考えていただけるとありがたいです。 引数を…というのは、関数の呼び出しも勿論ですが、関数の宣言内の方も考慮して下さい。

  • bushclean
  • ベストアンサー率26% (6/23)
回答No.1

>ReadIni_pr(&(pr.hp[0][3]), "hp", "aa,aa"); //関数呼び出し これ、pr.hp[0][3] という、一つの値への参照だけしか渡してませんよ。 変数の最後の値を渡しているので、関数内で変数サイズを超えて 書き換えが起こっているのでしょう。 >あと、char sp[PR_SIZE][5];に関しても同様の関数で処理したいのですが、可能でしょうか? という課題も含めて配列の二次元目の大きさも関数の引数に入れれば よいでしょう。 (えーと、「ReadIni_pr(char *a[], ...」で受け取って良いかどうかはパスしておきます(汗  私もその程度のレベルなので)

関連するQ&A

  • C言語を使って、ファイルの読み込みをして切り出して2次元配列に格納した

    C言語を使って、ファイルの読み込みをして切り出して2次元配列に格納したいのです。 1,2行目に配列の行の数と列の数が書かれ、3行目から改行とカンマ、スペースで区切られて配列が書かれているテキストを読み込んで2次元配列に格納する。 テキストの例) 4 3 1.1 1.2 1.3 1.4 1.5 2.1 2.2 2.3 2.4 2.5 3.1 3.2 3.3 4.4 3.5 というプログラムを書いています。色々と参考書やサイトを参考してとりあえずの形にはなったと思ったのですが、実行してもエラーが出ます。 どこまで動いているか調べたところ、一行ごとに読み出してそれを切り出して行くところでおかしな事をしてしまっているようですが、どう変えたらいいものか分かりません。 なので、その点のアドバイスと 大きさの分からないファイルから1,2行目を読み出すのはこれで変な動きをする恐れはないか の2点についてヒントでも構わないので、教えてください。 以下、書いたソースです(申し訳ないのですが、文字数の関係で一部省略しています。) #include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, char *argv[] ) { double ** mainhairetu; int size_x, size_y; /* size_x 行 size_y 列 */ int i,j,count=0,count2; int *cut,*temp2; double temp; char s2[] = " ,"; char gyou[10],*num; FILE *fil; while((fgets(gyou,10,fil)) !=NULL){ if(count == 0){ size_x=atoi(gyou); count++; }else if(count ==1){ size_y=atoi(gyou); count=count+1; }else{ break; } } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ここでmallocを使ってcutとmainhairetuの2つの配列を作っています。 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ count=0; for (i = 0; i < size_y+2; i++) { mainhairetu[i][0] = atof( strtok( fgets(cut,50,fil),s2 ) ); for (j = 1; j < size_x; j++){ if(count <=1){ count++; break; }else{ mainhairetu[i][j] = atof( strtok( NULL,s2 ) ); } } } for(i=0;i<size_y;i++){ for(j=0;j<size_y;j++){ printf("%f",mainhairetu[i][j]); } printf("\n"); } return(0); }

  • 多次元配列を動的に取る方法で悩んでいます。

    現在、以下のコードで三次元配列を動的に取る方法を作成しています。 { int i,j; char ***Regdata; LPVOID heapAdr; Regdata = (char ***)HeapAlloc(GetProcessHeap(),HEAP_NO_SERIALIZE,sizeof(char) * 1000); for(i=0;i<=1000;i++){ heapAdr = HeapAlloc(GetProcessHeap(), //ここでエラーが発生します。 HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, sizeof(char) * 6); if(heapAdr != NULL) { Regdata[i] = (char **)heapAdr; for(j=0;j<=6;j++){ heapAdr = HeapAlloc(GetProcessHeap(), HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, sizeof(char) * STRING_LENGTH); if(heapAdr != NULL) { Regdata[i][j] = (char *)heapAdr; } else { break; } } } else { break; } } } この方法だと、約40~50回ループした時点でHeapAlloc関数のところで 実行時エラーが発生してしまいます。 GetLastError関数を用いてエラーメッセージを取得したところ、 「No 998:メモリの場所に無効なアクセスがありました。」 というメッセージが返ってきました。 現状、どうしたらよいのかわからない状態です。 ご存知の方がいらっしゃれば、お教えいただきたいです。 以上、宜しくお願いします。

  • C言語 strtok

    失礼します。現在こちらでアドバイスを頂きfgetcを使用して配列に格納をすることができたのですが、CSVをカンマ区切りで格納したいのですが上手くいかず困っています。strtokを使用方法をドキュメントを読んでもうまく区切ったものを配列に入れる方法がわかりません 何卒よろしくお願いします。 ソースコード #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include<string.h> #define MAXITEM 1400 int split(char *str, const char *delim, char *outlist[]) { char *tk; int cnt = 0; tk = strtok(str, delim); while (tk != NULL && cnt < MAXITEM) { outlist[cnt++] = tk; tk = strtok(NULL, delim); } return cnt; } int main(void) { FILE *fp; char *fname = "testfile.csv"; char *tp; char *array[1400]; char *test[11][1400]; char c; int i = 0; int n,y; char *tp[1400]; fp = fopen(fname, "r"); if (fp == NULL) { printf("%sファイルが開けません¥n", fname); return -1; } while ((c = fgetc(fp)) != EOF) { array[i] = (char)c; i++; } tp = strtok(array, ","); puts(*tp); while (tp != NULL) { tp = strtok(NULL, ","); if (tp != NULL)puts(tp); } for (n = 0; n < 11; n++) { for (y = 0; y < 1400; y++) { test[n][y] = tp[y]; printf("%c", test[n][y]); } } fclose(fp); return 0; }

  • 2次元配列とポインタの引数受け渡しについて

    2次元配列を関数に渡すときは、引数に渡す2次元配列と同じサイズを指定、もしくは2次元目のサイズのみ合わせて渡す方法がありますが、両方とも違うサイズで同じ関数を使いたいです。 最初は中身が同じで引数で受け取る2次元配列のサイズだけ、それぞれに合わせた引数を持つ関数を2つ作っていたのですが、なんだか冗長な気がしました。 そこで、2次元配列の先頭ポインタとサイズを受け取るようにすればいいのかと思い、テストとして次のプログラムを作成してみました。 #include <stdio.h> void func(unsigned char *a, int y, int x); int main(void) { unsigned char a[10][10]; func(a, 10, 10); printf("%d\n", a[7][4]); return 0; } void func(unsigned char *a, int y, int x) { int i, j; for (i = 0; i < y; i++) { for (j = 0; j < x; j++) { *(a + i*y + j) = i * j; } } } もちろんこれでも動くのですが、やはりこういう書き方はルールにはないので、コンパイルで警告が出ます。 a.c: In function ‘main’: a.c:10: warning: passing argument 1 of ‘func’ from incompatible pointer type a.c:4: note: expected ‘unsigned char *’ but argument is of type ‘unsigned char (*)[10]’ このような書き方はやはりやめたいいのでしょうか。 また、その際はサイズ別に関数を作るしかないのでしょうか。 他にいい方法があれば教えていただけると助かります。

  • c#でC言語のstrtokに相当する関数は何か

    文字列から指定した文字でデータを区切る関数strtokがC言語にはある これに相当するC#の関数は何か 例えばCでは以下のように書く。 char data1[]= " 123 , 456 Yamada " ; char *token ; strtok( data, " ," ) ; /* スペースとカンマを区切りに文字列を抽出 */ token = strtok( str, " ." ); printf(" token chat = %s\n", token ) ; while ( token != NULL ) { token = strtok( NULL," ." ); if ( token != NULL ) printf(" token chat = %s\n", token ) ; } これに相当するc#のSplit関数を使用すると 不要な空白を取り出しているようである 知っている方がおりましたら、教えて下さい。

  • C言語 文字列操作

    トリム関数とリムーブ関数を作成してみました。改良点はありますでしょうか? ~~~~以下ソース~~~~ #include <stdio.h> #include <stdlib.h> #include <string.h> char *Trim(char *str); char *Remove(char *str, char *rmv); void main(void) {  char str[10], rmv[10], *p;  int c;  /* " abcd "をトリムする */  strcpy(str, " abcd ");  printf("トリム前 |%s|\n", str);  p = Trim(str);  printf("トリム後 |%s|\n", str);  /* 指定文字列を削除する */  printf("削除する文字列を入力してください :");  scanf("%s", rmv);  Remove(str, rmv);  printf("削除後 |%s|\n", str);  exit(0); } char *Trim(char *str) {  char space[] = " ";  char null[] = "";  int index = 0;  while(1){   if(strcmp(&(str[index]), null) == 0){    index--;    if(strncmp(&(str[index]), space, 1) == 0){     strcpy(&(str[index]), &(str[index]) + 1);    }else{     break;    }   }else{    if(strncmp(&(str[index]), space, 1) == 0 && index == 0){     strcpy(&(str[index]), &(str[index]) + 1);    }else{     index++;    }   }  }  return str; } char *Remove(char *str, char *rmv) {  int c, size, i;  char *p;  c = '\0';  p = strchr(rmv, c);  size = p - rmv;  for(i = 0; i < size; i++){   c = (int)rmv[i];   p = strchr(str, c);   if (p != NULL) {    strcpy(&(str[p-str]), p + 1);   }   else{    printf("""%c""は見つかりませんでした\n", c);   }  }  return str; }

  • C言語で、ファイルを読み込んで数字と名前に分けて配列に格納に関する質問

    C言語で、ファイルを読み込んで数字と名前に分けて配列に格納に関する質問です! ファイルを開いた後でエラーとなるのですが、何が足りないのでしょうか? ファイル内容 20 田中 10 鈴木 #include <stdio.h> #include <string.h> #include <stdlib.h> int main(int argc,char *argv[]) { FILE *fp; char str[256]; char *tp; int k,i=0; int num[10]; char na[10][20]; fp=fopen(argv[1],"r"); if(fp==NULL){ printf("ファイルを開けません\n"); return 1; }else{ printf("開けた\n"); } while(fgets(str,sizeof str,fp)!=NULL){ tp=strtok(str," "); num[i]=atoi(tp); tp=strtok(NULL," "); strcpy(na[i],tp); i++; } printf("%d\n%s\n",num[0],na[0]); printf("%d\n%s\n",num[1],na[1]); fclose(fp); return 0; }

  • プログラミング(配列と関数の引数)

    a : ABCDE a : ABCDEFGH Len : 8 a : FGHIJ a : FGH a : FGH, c : FGH 上記のように表示されるプログラムを作りたいのですが、なかなかできません。下記のようなプログラムを作ったのですがどこが間違っているのかよくわかりません。分かる方、指摘をお願いします。 #include <stdio.h> void my_strcpy(char s[], char t[]); int my_strlen(char s[]); void my_strcat(char s[], char t[]); int main(){ char a[10]; char b[10] = "ABCDE"; char c[] = "FGH"; int len; my_strcpy(a, b); printf("a : %s\n", a); my_strcat(a, c); printf("a : %s\n", a); len = my_strlen(a); printf("Len : %d\n", len); my_strcpy(a, "FGHIJ"); printf("a : %s\n", a); a[3] = '\0'; printf("a : %s\n", a); if(strcmp(a, c) == 0){ printf("a : %s, c : %s\n", a, c); } int i, s, t; my_strcpy(a, b + 2); printf("a : %s\n", a); void my_strcpy(char s[], char t[]){ for (i = 0; t[i] != '\0'; i++){ s[i] = t[i]; } s[i] = '\0'; } int my_strlen(char s[]){ int i; for (i = 0; s[i] != '\0'; i++); return i; } void my_strcat(char s[], char t[]){ int i, j; for (i = 0; s[i] != '\0'; i++); for (j = 0; t[j] != '\0'; i++, j++){ s[i] = t[j]; } s[i] = '\0'; } }

  • C言語 ポインタ型引数の呼び出しについて2

    C言語 ポインタ型引数の呼び出しについて2 以前質問した関数ですが、ネットを検索していたら同じような引数の関数を見つけました。 多分これを参考にしたのではないかと思います。 char* GetCSV(char** pBuf) { char *p = *pBuf,*q = p; if (!p) return p; if (*p == '"') { *p++ = 0; do ++q; while (*q && *q != '"'); if (*q == '"') *q++ = 0; } if ((*pBuf = strpbrk(q,",")) != 0) *(*pBuf)++ = 0; return p; } 当方がやりたい処理はVB6でCSVをリードして処理しているのですが、処理速度が遅いため I/OをしているところをCに変更して処理速度が向上するか確認したいのです。 他にも色々調べたのですがstrtokを使って処理されている方が多いようなのですが、 "1,,2,3"と言うデータの場合に"1" "2" "3"と返ってくるようで、 私のほしい値としては"1" "" "2" "3"というように返ってきてほしいのです。 上記のGetCSVでは"1" "" "2" "3"に返ってくるようです。 何とか動くようにと下の関数をかぶして実行して見たところ結果は正しかったのですが どうもメモリリークをしているように思います(タスクマネージャーのメモリ使用量は増えていく) int split(char *ary[], char *s) {   int len = 0;   int lsize = 0; char *strBuff[BUF_SIZE]; *strBuff = strdup(s); lsize = strlen(*strBuff); for (len = 0; len < lsize; len++) { if ((ary[len] = GetCSV(strBuff)) == NULL) break; } free(*strBuff); return len; } strdupの後メモリを開放しているつもりなのですが問題あるのでしょうか? C言語をあまりしたことがないので詳し方ご教示願えないでしょうか?宜しくお願いします。

  • C言語 配列挿入

    失礼します。カンマ区切りのC言語を配列に挿入したいのですが、エラーが出てしまいます。 初心者的な質問で申し訳ございませんが、よろしくお願いします。 途中までですが、コードを掲載させていただきます。 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include<string.h> #define piyo 11 #define hoge 2900 int main(void) { FILE *fp; char test[piyo][hoge]; char buf[hoge]; char *ary[hoge]; int i, k; test[piyo][hoge] = NULL; if ((fp = fopen("testfile.csv", "r")) == NULL) { printf("\aファイルをオープンできません\n"); }else{ while (fgets(buf, hoge, fp) != NULL) { for (k = 0; k < hoge; k++) { ary[k] = strtok(buf, ","); } } fclose(fp); return(0); } エラーメッセージ 警告 C4047 '=': 間接参照のレベルが 'char' と 'void *' で異なっています。 C1075 左側の 中かっこ '{' に対応するものがファイルの最後まで検出されませんでした。

専門家に質問してみよう