• ベストアンサー

二次元文字列をポインタで操作するには

お世話になります、フジと申します。今回皆様にお聞きしたいことは、かなり基本的な部分なのですがどうしても理解できず質問させて頂きます。 まず以下のようなプログラムを作成しました。 char *data[10]; void func(char* d) { static int i=0; data[i] = d; i++; } int main(void) { int i = 0; char a[10]; while(1){ if(i>2)break; if(i==0) strcpy(a,"Hello"); if(i==1) strcpy(a,"Morning"); if(i==2) strcpy(a,"Night"); func(a); i++; } i = 0; while(1){ if(i>2)break; printf("data is %s\n",data[i]); i++; } return 0; } すると出力されるdata[]の値は全て"Night"になります。これはポインタを使っているから当たり前だと思います。 これをどうにしかして上のようにポインタを用いて, data is Hello data is Morning data is Evening と出力させることが出来ないでしょうか? 大変基本的な部分とは思いますが、宜しくお願い致します。

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

  • ベストアンサー
  • chie65536
  • ベストアンサー率41% (2512/6032)
回答No.1

結局、data[0]もdata[1]もdata[2]も、aのポインタを指しているので、aに最後に入ってた文字列「Night」が表示される。 data配列が指す文字列を書き換えないなら、 char *data[10]; void func(char* d) {  static int i=0;  data[i] = d;  i++; } int main(void) {  int i = 0;  char *p;  while(1){   if(i>2)break;   if(i==0) p="Hello";   if(i==1) p="Morning";   if(i==2) p="Night";   func(p);   i++;  }  i = 0;  while(1){   if(i>2)break;   printf("data is %s\n",data[i]);   i++;  }  return 0; } data配列が指す文字列を書き換える可能性があるなら、 char data[10][10]; void func(char* d) {  static int i=0;  strcpy(data[i],d);  i++; } int main(void) {  int i = 0;  char *p;  while(1){   if(i>2)break;   if(i==0) p="Hello";   if(i==1) p="Morning";   if(i==2) p="Night";   func(p);   i++;  }  i = 0;  while(1){   if(i>2)break;   printf("data is %s\n",data[i]);   i++;  }  return 0; } となる。 ここで注意しないとならないのは「charポインタの配列と、charの2次元配列は、まったくの別物」って事。 これは「charポインタと、charの配列は、まったくの別物」ってのと同じ。 「charポインタは文字列はどっか別のトコにあるが、charの配列には文字列が入ってる」と言う違いがあるのは判ると思う。 同様に「charポインタの配列は文字列はどっか別のトコにあるが、charの2次元配列には文字列が入ってる」と言う違いがある。 「charポインタの配列」で、つまり「char *data[10]」って書きたいなら「文字列は、すべて、別々の場所に格納されてるのを、それぞれ用意しないとならない」って事。 質問者さんのプログラムは「別々の文字列を、毎回、a配列に格納し、data[0]もdata[1]もdata[2]も、a配列を指すようにしている」ので、結局は char *data[10]; int main(void) {  int i = 0;  char a[10];  strcpy(a,"Hello");  data[0]=a;  strcpy(a,"Morning");  data[1]=a;  strcpy(a,"Night");  data[2]=a;  printf("data is %s\n",data[0]);  printf("data is %s\n",data[1]);  printf("data is %s\n",data[2]);  return 0; } というプログラムと等しい。 strcpy処理と、data配列にaを代入する処理には、依存関係は無いから、順序を入れ替えても同じ。つまり char *data[10]; int main(void) {  int i = 0;  char a[10];  strcpy(a,"Hello");  strcpy(a,"Morning");  strcpy(a,"Night");  data[0]=a;  data[1]=a;  data[2]=a;  printf("data is %s\n",data[0]);  printf("data is %s\n",data[1]);  printf("data is %s\n",data[2]);  return 0; } と同じ。この時、  strcpy(a,"Hello");  strcpy(a,"Morning"); は  strcpy(a,"Night"); で上書きされるので無いのと等しい。最終的には char *data[10]; int main(void) {  int i = 0;  char a[10];  strcpy(a,"Night");  data[0]=a;  data[1]=a;  data[2]=a;  printf("data is %s\n",data[0]);  printf("data is %s\n",data[1]);  printf("data is %s\n",data[2]);  return 0; } と同じになる。 これじゃ「Nightが3回表示されて当然」でしょう。

fujicafe
質問者

お礼

とても分かりやすい説明ありがとうございます。 私は完璧にcharポインタの配列は二次元の文字列配列の別の表現の方法だと勘違いしていました。 確かに、ポインタを外部関数の引数にした場合に、返り値なしで、値の変化が反映されるという考えから考えても、何か違うんじゃないかと思っていました。 今回の件で、配列とポインタの違いをさらに認識できました。本当にありがとうございました。

その他の回答 (1)

  • php504
  • ベストアンサー率42% (926/2160)
回答No.2

while(1){ if(i>2)break; if(i==0) func("Hello"); if(i==1) func("Morning"); if(i==2) func("Night"); i++; }

fujicafe
質問者

お礼

変数を間にはさまず、直接ポインタ配列に代入すると出力されるのですね。 ありがとうございます。

関連するQ&A

  • 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]’ このような書き方はやはりやめたいいのでしょうか。 また、その際はサイズ別に関数を作るしかないのでしょうか。 他にいい方法があれば教えていただけると助かります。

  • ポインタ

    #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が 国語 とかになったりします。 なんかもうさっぱりわからないんですが どうすればいいのでしょうか? 変換したソースがほしいです。

  • ポインタと文字列

    最近やっとポインタを理解できるようになってしきました。 でも、ちょっとわからない問題があるので質問させていただきます。 問)a~z,A~Z及び,.?空白などで構成される文字列を入力して、大文字と小文字を区別しないでa~zの出現頻度をポインタを使ってそれぞれ表しなさい。 ※文字列のほうはこちらで用意させていただきました。リダイレクトを使用してください。単語数は250です。 http://ayumuatsuco.fc2web.com/mit.htm 一応途中まで作ってある私のプログラムを参考にしてみてください。実行結果が変ですが。。。 #include <stdio.h> int main(void) { char buffer[250], hindo[26], *p; int i; while(1){ if(gets(buffer)==NULL) break; p=buffer; while(*p!='\0'){ hindo[*p-'a']++; for(i=0; i<10; i++){ if('a'<=*(p+i)) *(p+i)=*(p+i)-32 } for(i=0; i<26; i++){ printf("hindo[%d]=%d\n",i,*(p+i)); } } } return 0; }

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

    毎度お世話になります。 構造体のメンバが、文字列ポインタの配列だった場合、それに文字列をコピーすることは可能なのでしょうか? とりあえず下記のように書いてみたのですが、実行エラーで終了してしまいます。 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で文字列を入れるのか? } よろしくお願いします。

  • 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; }

  • 配列とポインタを使って特定の文字だけ大文字にする

    配列とポインタを使って特定の文字だけ大文字にするプログラムを作りたいのですがどのように作ればいいのでしょうか? 例えば、sportsのsだけ判別してSportSというふうにしたいです。 一応以下のようなプログラムを作ったのですが、実行してもsportsのままで何も変わりません・・・。 #include<stdio.h> void mojihenkan(char *); main(void) { char moji[8]={"sports"}; char *p; int i; mojihenkan(moji); p=moji; for(i=0;i<8;i++){ printf("%c",*(p+i)); } return 0; } void mojihenkan(char *a) { int i; for(i=0;i<8;i++){ if(*(a+i)=='s'){ a-32; } } }

  • ポインタについて

    #include<stdio.h> int main(void){ int i; char *pt = "Hello World"; char a[11]; pt = &a; for(i=0;i<=11;i++){ printf("%c %x\n",pt,&a[i]); pt++; } return(0); } H abcd80(←アドレス) E abcd81(←アドレス) のように表示したいんですが、アドレスだけうまくできてHELLO WORLD のほうがうまく表示できません。 夏休みの間ブランク空けたんでポインタ全て抜けてしまったみたいです^^;  よろしくおねがいします。

  • ポインタに ~0を入れること

    見かけたCのプログラムで、 ポインタに~0を代入するものを見ました。 そのプログラムをそのまま載せるのはわかりにくいので、 代わりに以下のプログラムを作って実行しました。 #include <stdio.h> int main(void) { char *pa[3]; int i; pa[0]=0; pa[1]=~0; pa[2]="Hello"; printf("sizeof(char*)=%d\n", sizeof(char*)); for(i=0; i<=2; i++) { if(pa[i]==NULL) printf("pa[%d] はNULLです。\n", i); if(pa[i]==(char*)0xFFFFFFFF) printf("pa[%d]は全ビット1です。\n", i); if(pa[i]==~0) printf("pa[%d]は~0です。\n", i); } return 0; } 結果 sizeof(char*)=4 pa[0] はNULLです。 pa[1]は全ビット1です。 pa[1]は~0です。 このプログラムはコンパイル時にエラーも警告も出ず、 動作も意図したとおりです。 pa[1]に入っている ~0 は、int型の定数なのでしょうか。 それならば、 pa[1]=~0; という代入や if(pa[i]==~0) という比較は 左辺はchar*型で右辺はconst int型であって型が異なりますが、 問題ないのでしょうか。 ~0は0の否定なので、全ビットは1なのでしょうけど、 int型(の定数)だと思います。 ~0というのは何か特別な値なのでしょうか。 ポインタに~0を入れるというのは、意味があるのでしょうか。 (例えば、「ポインタに0を入れるということは、ヌルポインタであって、ポインタとして無効なんですよ」のようなこと。)

  • ポインタを用いた文字列操作について…。

    C初心者です。 「ある文字列を読み込んだ後、その文字列中にある文字(複数)を指定する。指定した文字を最初の文字列から消す」という関数を作りたいのですが、うまくいきません。 例を挙げると、 文字列 :OshieteGoo 指定文字:to 文字列改:OshitG のような感じにしたいです。 #define NULLC '\0' void strCpy(char *ptr1, char *ptr2, char *cMain) { char *cSub = cMain; /* ポインタバックアップ */ for(;*ptr1!=NULLC;ptr1++){ for(cSub=cMain;*cSub!=NULLC;*cSub++){ if(*ptr1!=*cSub){ *ptr2 = *ptr1; ptr2++; } break; } } *ptr2 = NULLC; return; } 自分なりにこう考えてみたのですが、これを実行すると 文字列 :OshieteGoo 指定文字:to 文字列改:OshitGoo となり、指定文字の2文字目以降がうまく削除できません…。 この問題を解決できるお知恵を拝借させて頂けると幸いです^^;

  • 不透明なデータのポインタ宣言

    ライブラリ内で使用し、ライブラリの外には、 中身を公開したくない構造体があります。 ライブラリ関数には必ず、ポインタとしてやりとりされます。 これを外部向けに不透明なデータのポインタとして宣言したいのですが・・ typedef const void *PRIVATE_DATA ; このように宣言してみたのですが、 これでは、否応にもvoidポインタとなってしまい、 char *等と暗黙のキャストが行われてしまいます。 たとえば・・ int lib_func( PRIVATE_DATA priv ) ; こんなライブラリ関数に対して、 lib_func( "HELLO" ) ; こんなおかしな呼び出し方が書けてしまいます。 不透明で、他の型へ暗黙のキャストが行われないポインタの宣言の仕方って、 ありませんでしょうか。。? ちなみに、コンパイラは gcc version 3.4.4 です。

専門家に質問してみよう