• ベストアンサー

文字列ポインタの配列の扱い方

毎度お世話になります。 構造体のメンバが、文字列ポインタの配列だった場合、それに文字列をコピーすることは可能なのでしょうか? とりあえず下記のように書いてみたのですが、実行エラーで終了してしまいます。 typedef struct data{ char name[30]; int age; char *ss[2]; }DATA; int main() { DATA dt[3] = {{"AA",10}, //ssも初期化出来るのか? {"BB",15}, {"CC",20}}; int i,j; for(i=0;i<3;i++){ for(j=0;j<2;j++) strcpy(dt[i].ss[j],"なし"); //それとも、ここでstrcpyで文字列を入れるのか? } よろしくお願いします。

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

  • ベストアンサー
  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.3

★strdup() を使えば良いと思います。 >文字列ポインタの配列だった場合、それに文字列をコピーすることは可能なのでしょうか?  ↑  直接コピーは出来ません。文字列ポインタですから。  でも確保された領域がポインタにセットされていればその領域に対してはコピーできます。 ・文字列をコピーではなく文字列領域を確保してからそのポインタをセットすればよい。  方法は malloc() 関数でメモリを確保してから文字列をコピーするか、strdup() 関数を  利用して下さい。strdup() 関数の方が楽ですので紹介します。 サンプル: int main( void ) {  DATA dt[ 3 ] = {   { "AA", 10 },   { "BB", 15 },   { "CC", 20 },  }; int i, j;    for ( i = 0 ; i < 3 ; i++ ){   dt[ i ].ss[ 0 ] = strdup( "なし" );   dt[ i ].ss[ 1 ] = strdup( "なし" );  }  :  処理  :  /*  必要に応じて ss にセットされた領域を free します。  main() から抜けるだけなら自動的に解放されます。  でも malloc()、free() や strdup()、free() は対で処理した方が良いかな。  */  return 0; } その他: ・構造体の ss に固定の文字列をセットするだけなら直接文字列のポインタをセットできます。  つまりコピーというよりはセット。  dt[ i ].ss[ 0 ] = "なし";  dt[ i ].ss[ 1 ] = "なし";  ↑  これだけでよい。 ・問題は ss のポインタはどのような使われ方をするかです。  最終的にどんな事したいの?  そうしないと適切な回答が出せません。 ・以上。補足要求します。

sikimori
質問者

補足

いつも回答ありがとうございます。 ただ単に、ss[4][10]と二次元配列にしてもいいのですが、*ss[4]も使えるのでなかなか使わない方で試してみようと思っただけなので、ポインタにする必要性は本当はないのですが(汗) ssは、今は"なし"ですが、条件判定で当てはまれば、要素別に"あり"にして"あり"の場合の処理をします。 例えば、 if(flg == 1){ dt[0].ss[0] = "あり"; dt[0].ss[1] = "なし"; dt[0].ss[2] = "あり"; } みたいな感じに使いたいのです。

その他の回答 (4)

  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.5

★どうしても文字列にしたいの? ・回答者 No.3 さんのアドバイスに『列挙型』となっていますがフラグ情報でもいいのでは。  その方が『あり』、『なし』の状態を判定しやすいと思いますけど。  もちろん文字列でも行えますがその場合は同じ『あり』と『なし』の文字列ポインタを  使った方が良いです。つまり、 // 記号定数として定義 #define STR_NO  yesno[0] #define STR_YES yesno[1] // あり/なし状態の文字列テーブル static const char *yesno[] = {  "なし",  "あり", }; int main( void ) {  DATA dt[ 3 ] = {   { "AA", 10, STR_NO, STR_NO }, ←初期化可能。   { "BB", 15, STR_NO, STR_NO },   { "CC", 20, STR_NO, STR_NO },  }; int i, j;    for ( i = 0 ; i < 3 ; i++ ){   // 判定   switch ( dt[i].ss[0] ){ ←ss[0]に対して判定    case STR_NO:     /*     『なし』と判定した処理     */     break;    case STR_YES:     /*     『あり』と判定した処理     */     break;    default:     /*     設定エラーの処理     */   }  }  return 0; } その他: ・決まった文字列リテラルを使うことでポインタ値で『あり』、『なし』をチェックできます。  比較や代入は記号定数 STR_NO、STR_YES で行います。  st[n].ss[0] を直接 printf() すれば文字列ですので表示も可能です。  フラグを使っても同じように出来ますがどうしても文字列でセットしたい場合は  static const char *yesno[] という決まった文字列を用意して使います。 ・以上。

sikimori
質問者

お礼

確かにフラグでやるのも考えましたが、そっちが完成したので今度は文字列でやろうとしていたのです。 教えていただいたのを参考に、頑張ります。 ありがとうございました。

  • mikaemi
  • ベストアンサー率50% (33/65)
回答No.4

”あり”か”なし”だけ入れるならポインタの配列のままで、 文字列リテラルのアドレスをそのまま代入するだけでいいでしょう。 そうすれば、メモリリークもないですし。 てか、それなら、”あり”か”なし”かを示す値(列挙型とか)でもいいのか^^

  • maku_x
  • ベストアンサー率44% (164/371)
回答No.2

こんな感じで書けば、data 構造体のメンバ ss も初期化できます。 int main() {   DATA dt[3] = {{"AA",10,{"aa1","aa2"}}, //ssも初期化出来るのか?          {"BB",15,{"bb1","bb2"}},          {"CC",20,{"cc1","bb2"}}};   int i,j;   for (i=0;i<3;i++) {     for (j=0;j<2;j++) {       dt[i].ss[j] = "なし"; //それとも、ここでstrcpyで文字列を入れるのか?     }   } } なお、strcpy は確保されたメモリが無い場合には使えませんので、この場合は直接文字列のポインタを ss に代入します。

sikimori
質問者

お礼

的確なアドバイス、ありがとうございました。

  • mikaemi
  • ベストアンサー率50% (33/65)
回答No.1

char *ss[2] は文字へのポインタ2つの要素からなる配列です。 領域をとっておいてあげないと strcpy(dt[i].ss[j],"...")は無理ですね。 ss の初期化はできるので、 DATA dt[3] = { { "AA", 10, { malloc(30), malloc(30) } }, { "BB", 15, { malloc(30), malloc(30) } }, { "CC", 20, { malloc(30), malloc(30) } }, }; とかしておきますか?^^ strcpy したいということは、 あとで何か作り出した文字列を入れたいということですよね? わたしなら、NULL で初期化しておいて、 使う段になったらアロケートするか、すでにアロケートしている 文字列のアドレスを入れますけど。 free() し忘れたりしないように…でも、妙なアドレスを free() に 渡すより、メモリリークの方がマシですか?^^

sikimori
質問者

お礼

ご回答ありがとうございます そういうやり方もあるんですね。また一つ勉強になりました。

関連するQ&A

  • ファイル読込時に構造体の文字列ポインタに割当てたいと

    ファイル読込時に構造体の文字列ポインタに割当てたいと思っています。 (new 演算子を使用します。) 文字列の長さが不定です。 どうすれば、文字列の長さを知ることができますか? 以下のようなところまでは作れましたが、 困っています。 void loaddata()のfscanf関数の部分です。 ほかにも関数の void outputdata() void deletedata() がありますが、長いので省略しました。 ********************************************************** #include<stdio.h> #include<string.h> class data { public: struct basic { char *name; int age; struct basic *next; }; private: struct basic *base; struct basic *base_top; int cnt; public: data::data() { cnt=0; } void inputdata(char *name,int age) { if(cnt==0) { base=new basic; base_top=base; base->age=age; int len=strlen(name); base->name=new char[len+1]; strcpy(base->name,name); cnt++; } else { base->next=new basic; base=base->next; base->age=age; int len=strlen(name); base->name=new char[len+1]; strcpy(base->name,name); cnt++; } } void savedata() { base=base_top; FILE *fp; fp=fopen("dat.txt","w"); for(int i=0;i<cnt;i++) { fprintf(fp,"%s\t%d\n",base->name,base->age); base=base->next; } fclose(fp); } void loaddata() { if(cnt!=0){deletedata();} cnt=0; FILE *fp; fp=fopen("dat.txt","r"); while(1) { fscanf(fp,"%s\t%d\n",base->name,base->age); } } };

  • 配列やポインタに文字列を設定することについて

    ◎1------------------------- #include<stdio.h> int main(void) { char ss[80]; scanf("%s",ss); printf("%s\n",ss); return 0; } ---------------------------- ◎2--------------------------- #include<stdio.h> int main(void) { char *ss="abcde"; printf("%s\n",ss); return 0; } ------------------------- ◎3---------------------- #include<stdio.h> int main(void) { char *ss; ss="abcde"; printf("%s\n",ss); return 0; } ------------------------- 以上3つプログラムで疑問をいだいたのですが、 まず◎1で、これは例えば、 cahr ss[80]="abc"; のように配列ssに文字列"abc"そのものを入れているのか、 char *ss="xyz"; のようにまず"xyz"という文字列をメモリ上のどこかに設定し、その先頭番地をssに代入しているのか、どちらの考えでいいのかわかりません。 次に、◎2、3ではどちらも正常に実行できたのですが、特に◎3で「ss="abcde";」と記述していますが、ssにはアドレスを代入するという認識かあるのですが、文字列定数を代入しても問題ないのか?という疑問があります。 教えていただけたら嬉しいです。

  • ポインタについて

    途中までのソースコード typedef struct node{ char moji[128]; --------(1) }NODE; int main(int argc, char *argv[]){ FILE *fp; char buf[128]; NODE p[128]; char *tp; int i = 0; int j,k; int res; NODE temp; fp = fopen(argv[1],"r"); while(fscanf(fp,"%s",&buf) == 1){ tp = strtok(buf," ,.-"); strcpy(p[i].moji,tp); ---------(2) i++; while(tp != NULL){ tp = strtok(NULL," ,.-"); if(tp != NULL){ strcpy(p[i].moji, tp); --------(2) i++; } } } for(j = 0;j < i; j++) puts(p[j].moji); return 0; } file.txt これ以下の文字を読み込む -------------- The Java programming language is a general-purpose, concurrent, class-based, object-oriented language. It is designed to be simple enough that many programmers can achieve fluency in the language. 例えばこんなソースコードがあって、(1)をポインタにして、(2)のstrcpyを使わずにポインタだけで表現するとしたらどのようになるんですか?

  • ポインタ

    #include "stdafx.h" #include <ctype.h> #include <string.h> #include <stdlib.h> typedef struct { char number[6]; char class_type[20]; char name[8]; char subject[5]; } my; my data[100]; int main(int argc, char* argv[]) { FILE *fp; int field = 0, line = 0; char buf[1000], *str; char bufG[1111]; int i; if((fp=fopen("test.txt","r"))==NULL){ printf("ファイルが開けません"); } while(fgets(buf,1000,fp) !=NULL){ str=buf; while(*str != '\0'){ if(*str != ','){ for(i = 0; *str != ',' && *str != '\0' ; i++){ if(*str == '\n'){ } else{ bufG[i] = *str; } str++; } bufG[i] = '\0'; // printf("%s", bufG); switch(field){ case 0: strcpy(data[line].number, bufG); break; case 1: strcpy(data[line].class_type, bufG); break; case 2: strcpy(data[line].name, bufG); break; case 3: strcpy(data[line].subject, bufG); break; } field++; } else{ str++; } } line++; field = 0; } printf("%s", data[2].subject); fclose(fp); return 0; } このプログラムをベースにしてメモリの無駄を省けるような プログラムに修正したいのですが、 ポインタほんとできなくて困ってます。 教えていただいてメモリを取る位置とかは大体わかりました。 まず構造体のメモリをとります。しかしこのままでは固定長になってるので 構造体を少しいじくりますよね。 構造体の中身なのですが typedef struct{ int number; char *class_type; char *name; char *subject; } my; my *data; にして data = malloc(100); このような形でとります。 文字列の型ですがchar *class_typeのようにポインタで宣言しないと bufGを代入して値を入れるときに型が合いませんので 配列にしないのであればポインタ型宣言でいいと思います。 しかしポインタで宣言してstrcpy(・・)の所を data[line].class_type = bufG にするとエラーはでませんが*strの値が変わる度に data[line].class_typeの値が変動するのでdata[line].class_typeが 国語 とかになったりします。 なんかもうさっぱりわからないんですが どうすればいいのでしょうか? 変換したソースがほしいです。

  • 文字列のコピー

    文字列のコピーで長さの違う文字列をコピーすると変なふうになってしまいます。たとえば下のようにやるとdogeetみたいになってしまいます。dogだけを表示したいんです。strcpyやstrcatを使わずに文字の長さの違った文字列のコピーはどうやるんでしょうか? #include <stdio.h> int main (void){ char spelA[] = "street"; char spelB[] = "dog"; int i; for(i=0;spelB[i]!=0;i++){ spelA[i] = spelB[i]; } printf("%s\n",spelA); return(0); }

  • 構造体の文字列データをファイルへ書き込む方法は?

    構造体の文字列データをファイルへ書き込む方法として、構造体のデータを一気に書き込まずに、データ毎に書き込むことを勉強としてやってみようとしました。 数字の場合はうまくいったのですが、文字列の場合が分かりません。 ご存知の方、よろしくお願いいたします。 <数字の場合> typedef struct { int m1, m2; } Data; fwrite(&data[i].m1, sizeof(int), 1, fp); fwrite(&data[i].m2, sizeof(int), 1, fp); <文字列の場合> #include <stdio.h> typedef struct { char m1[10], m2[10]; } Data; int main() { static Data data[3] = { { "a1", "b1"}, { "c1", "d1"}, { "e1", "f1"}, }; Data data2[10]; FILE *fp; int i, n; fp = fopen("file.dat", "wb"); if (fp == NULL) return 1; for (i = 0; i < 3; i++) { fwrite(&data[i].m1, sizeof(Data.m1[10]), 1, fp); ← sizeofでエラーが出る fwrite(&data[i].m2, sizeof(Data.m2[10]), 1, fp); } fclose(fp); 以下省略

  • 配列の中を変更

    入力したnameに、入力したageの数だけ文字を進める(例えばnameがOda、ageが12→表示結果がAmp)にする関数を作成しようとしたのですが、やり方がまったくわかりません。 そもそもこのような場合、文字1つ1つに別の配列を使わなければならないのでしょうか? #include <stdio.h> #define N 1 #define NAME 20 typedef struct{ char name[NAME]; char age; } person; int main(void){ int i; person persons[N]; for(i=0;i<N;i++){ printf("name > "); scanf("%s" , persons[i].name); printf("age > "); scanf("%d" , &persons[i].age); } for(i=0;i<N;i++){ printf("name = %s\n" , persons[i].name); printf("age = %d\n" , persons[i].age); } return 0; }

  • 動的に生成した文字列の配列を返す関数について

    動的に生成した文字列の配列を返す関数について お世話になります。 動的に文字列の配列を生成する関数を作ったのですが、 配列をうまく受け渡すことができず困っています。 以下のように入力された件数の数だけ "abc 0"~"abc n"という文字列を生成を行っています。 関数自体は期待通りの動作をしているようなのですが、 (Test1関数の最後でbfを確認しました) 呼び出し側にうまく配列を渡すことができません。 以下にソースを掲載いたしますのでどなたかご教示いただけたらと思います。 環境はVisualStudio2005です。 よろしくお願いします。 #include <stdio.h> #include <string.h> #include <stdlib.h> void Test1(char **bf, int *cnt) { int i; int kensu; int charlength; char num[10]; char **nm1 = NULL; char **nm2 = NULL; printf("件数を入力:"); scanf("%d",&kensu); for(i=0; i < kensu; i++) { nm2 = (char**)realloc(nm1, sizeof(char*) * (i + 1)); nm1 = nm2; charlength=128; nm1[i] = (char*)malloc(sizeof(char) * (charlength)); strcpy(nm1[i], "abc "); itoa(i, num, 10); strcat(nm1[i], num); } bf = nm1; *cnt = i; return ; } void main() { int cnt; char **bf = NULL; Test1(bf, &cnt); printf("START\n"); printf("全部で%d件。\n", cnt); for(int i=0;i < cnt;i++) { printf("%s\n",bf[i]); } free(bf); printf("END\n"); }

  • 構造体の配列について(2)

    前回「構造体の配列について」という質問タイトルで、質問させていただいたのですが、理解が完全ではないため同じようなプログラム内容ではありますが、疑問を書かせていただきます。よろしくお願いします。 ----------------------------------------------------------------------- #include<stdio.h> #include<string.h> struct person{ char name[1];    //// (1) //// int height; int weight; }; int main() { struct person dt[10]; strcpy(dt[0].name,"日本一郎"); strcpy(dt[2].name,"関東次郎"); strcpy(dt[9].name,"関西三郎"); dt[1].weight=99; dt[1].height=168; printf("%s %s %s %d %d \n",dt[0].name,dt[2].name,dt[9].name,dt[1].weight,dt[1].height); return 0; } ----------------------------------------------------------------------- 以上のプログラムの(1)の部分で、文字を1文字しか格納出来ないのに、 strcpy(dt[0].name,"日本一郎"); strcpy(dt[2].name,"関東次郎"); strcpy(dt[9].name,"関西三郎"); としても何故正しく実行できるのかわかりません。 前回いろいろとご回答いただいたのに、しっかりと理解できない者ですが、教えていただければ嬉しいです。

  • 二次元配列による文字列の配列の受渡しについての質問です。

    二次元配列による文字列の配列の受渡しについての質問です。 #include <stdio.h> void print_pname(char str[][5], int n) { int i, j; for (i = 0; i < n; i++) { printf("str[%d] = \"", i); for (j = 0; str[i][j] != '\0'; j++) putchar(str[i][j]); printf("\"\n"); } } int main(void) { char ary[][5] = {"Lisp", "C", "Ada"}; print_pname(ary, sizeof(ary) / sizeof(ary[0])); return 0; } 上のプログラム中の関数print_pnameの引数char str[][5]についてですが char (*str)[5](配列のポインタ)と変更した場合にwarningが多数発生します。 これはどうしてでしょうか? また、上のプログラムを配列のポインタを使って変更することは可能でしょうか? 以上、よろしくお願いします。

専門家に質問してみよう