• ベストアンサー

ポインタ配列

"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'; }

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

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

★ポインタを使いこなしていませんね。 ・あまり、ポインタを使いすぎるとわけが分からなくなりますよ。 >a = strmp( *(pp+i), *(pp+t) );  の『*(pp+i)』や『*(pp+t)』の部分は pp[i]、pp[t] でも同じにアクセスできますので  配列と同じように表記した方が私は見やすいと思います。もしも、ポインタを使うなら  『*(pp+i)』で参照するのがこだわりがあってのことなら pp[i] にしなくても良いが…。 ・あと並びを入れ替えるときに文字列の中身を入れ替えています。  文字列のポインタだけを、ポインタ同士で入れ替えれば簡単ですよ。  ●間違い  cpy( p,*(pp+i) );  cpy( *(pp+i), *(pp+t) );  cpy( *(pp+t), p );  ●正しい  char *tp[ 1 ]; ←これを宣言(temp の tp です)    tp[ 0 ] = pp[ i ];  pp[ i ] = pp[ t ];  pp[ t ] = tp[ 0 ];  となります。cpy() 関数は文字列の内容をコピーするようですが、ポインタ配列の中身を  書き換えたいのですか?文字列定数を書き換えようとするとエラーになりませんか?  もしも、文字列の内容をアルファベット順に書き換えたい場合は、  char s0[] = "oneee";  char s1[] = "twooo";  char s2[] = "three";  char s3[] = "fourr";  char s4[] = "fivee";  char s5[] = "sixxx";  char s6[] = "seven";  char s7[] = "eight";  char s8[] = "ninee";  char s9[] = "tennn";  char *x[10]={ s0, s1, s2, s3, s4, s5, s6, s7, s8, s9 };  と定義しないといけません。  この方法ならば、cpy() 関数で中身をコピーして入れ替えできます。そういう考えでしたら  上の『間違い』が間違いではなくて本当に正しくなります。どっち? 参考ソース: int main( void ) {  char *x[10]={"oneee","twooo","three","fourr","fivee","sixxx","seven","eight","ninee","tennn" };  char *p[1]; ←宣言  int i, t, cmp;    /* バブルソート(降順) */  for ( i = 0 ; i < (10 - 1) ; i++ ){   for ( t = i + 1 ; t < 10 ; t++ ){ ←t には i+1 を代入しないとマズイ!    cmp = strmp( x[i], x[t] );        if ( cmp < 0 ){     p[ 0 ] = x[ i ];     x[ i ] = x[ t ];     x[ t ] = p[ 0 ];    }   }  }  /* ソート結果表示 */  for ( i = 0 ; i < 10 ; i++ ){   printf( "%s, ", x[i] );  }  printf( "\n" );  return 0; } /* 文字列の比較 */ int strmp( const char *x, const char *y ) {  while ( *x == *y ){   if ( *x == '\0' ){    return 0;   }   x++;   y++;  }  return (*x - *y); } その他: ・http://www.codereading.com/algo_and_ds/algo/bubble_sort.html→『バブルソート』 ・http://oshiete1.goo.ne.jp/qa2577202.html→『ポインタで詰まりました;』 ・上記も参考に!

参考URL:
http://www.codereading.com/algo_and_ds/algo/bubble_sort.html
higyaaa
質問者

お礼

char s0[] = "oneee";  char s1[] = "twooo";  char s2[] = "three";  char s3[] = "fourr";  char s4[] = "fivee";  char s5[] = "sixxx";  char s6[] = "seven";  char s7[] = "eight";  char s8[] = "ninee";  char s9[] = "tennn";  char *x[10]={ s0, s1, s2, s3, s4, s5, s6, s7, s8, s9 }; とやった結果どうにかできました。 自分としてはどうにかポインタ配列とポインタのポインタでやってみようと思っていたので、こんなおかしな物になってしまいました。 私のそのあたりの説明が不足していたようで、申し訳ありませんでした。 とてもわかりやすい説明ありがとうございました。

その他の回答 (3)

  • DT200
  • ベストアンサー率38% (63/164)
回答No.3

複雑に考え過ぎのようです。違うやり方で処理してます。 #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"}; // // このままだとセグメント違反になるので、この配列は固定にしてインデックス // のソートをする。オリジナル方式でやる場合、二次元配列に移し替えてから最初のfor分を実行する。 // // 以下の p[ 10 ]がインデックスの配列。 int p[ 10 ]; char k[100]; // これは不要 int i,t,a,b,c,d; a=0; // インデックスを初期化する for(i=0;i<10;i++){ p[ i ] = i; } /* ポインタ配列を自作関数を使って、ASCIIコードの大きいほうからに並び替える */ for(i=0;i<9;i++){ // 初期値は1固定だと期待した結果にはならない。 i+1 です。 for(t=i+1;t<10;t++){ // 大小の判定 a = strmp( x[ p[ i ] ], x[ p[ t ] ] ); if( a < 0 ){ // インデックスの入れ替え b = p[ i ]; p[ i ] = p[ t ]; p[ t ] = b; } } } for(i=0;i<10;i++){ // 並び替えられたインデックスで、元もとの文字列のポインタの // 配列を指す。 printf("%s ,", x[ p[i] ]); } printf("\n"); return 0; } /* 文字の比較をする関数 */ int strmp(char *x,char *y) { int i; // ポインタのみで実行するほうが処理は速い。 while( *x != '\0' ){ if( *x++ < *y++ ){ return -1; } else{ return 1; } } return 0; } /* 文字をコピーする関数 */ void cpy(char *a,char *b) { // ポインタのみで実行するほうが処理は速い。 while( *b != '\0' ){ *a++ = *b++; } *a ='\0'; }

higyaaa
質問者

お礼

わかりやすい説明ありがとうございました。 ポインタ配列とポインタのポインタだけでどうにかやろうと思ったんですが どうやら無理があったようですね。 もっと広い視野でみないとだめですね

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

> "one","two","three","four","five","six","seven","eight","nine","ten" のポインタ配列 についてソートしたいのですから、 > char *x[10]={"oneee","twooo","three","fourr","fivee","sixxx","seven","eight","ninee","tennn"}; こうではなく char *x[10]={"one","two","three","four","five","six","seven","eight","nine","ten"}; と定義して、プログラムが正しく動くようにしなければなりませんね。

noname#30727
noname#30727
回答No.1

並び替える時に文字をコピーする必要はありません。 (二次元配列の時はコピーが必要です) char *x[2]={"one","twoooooooooo"}; char *t; /* x[0]とx[1]を交換する */ t = x[0]; x[0] = x[1]; x[1] = t; /* 交換終了 */ strmp関数は、ぱっと見うまく出来ているように見えます。 あとは、バブルソートで交換していく部分を作りこんでいけば動くと思います。

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

  • できているとは、思うのですが。ポインタの配列を

    コンパイラではちゃんと動いてます。 1 2 3 0 1 2 3 という具合です。 気になるのは、printarrayの部分が正しいのか、ちょっと悩んでいます 問題としては main関数では0の値を読み込むまで最大99(MAX-1)個の値を配列 xに読み込んでいる。引数のポインタからの値を、値が0になるまで すべて1行に1つづつ画面に出力する関数printarray()を作成し、 プログラムを完成せよ。 引数はアドレスとして受け取る事。(配列としてでなく) フォーマットは、 "%d¥n" とする。(余計な出力はしない事。) (0は出力しない。) main内部を変更してはならない。 以下がソースです。 ご指摘よろしくお願いします。 #include <stdio.h> #define MAX 5 void printarray(int *); int main() { int x[MAX], i; int *p; x[MAX-1] = 0; for (i = 0, p = x; i < MAX-1; ++i, ++p) { scanf("%d", p); if (*p == 0) { break; } } printarray(x); return 0; } void printarray(int *a) { int i,*p; for(i = 0, p = a; i < MAX-1; ++i,++p) { if(*p == 0){ // continue; break; }else{ printf("%d\n", *(a+i)); } } }

  • ポインタ配列

    配列をポインタでとって、配列要素を足していこうと思ったのですが、 #include <stdio.h> int main (void) { int p[11]={11,23,43,66,54,67,51,88,22,43,-1}; int *e=p; int x=0; int i=0; while( *e!=-1 ) { x + = ( * (e+i) ); i++; } printf("%d\n",x); return 0; } x = ( * ( e+i ) ); ↑この部分がなぜだめなのか理解できません どなたか教えてくださるとありがたいです

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

    配列とポインタを使って特定の文字だけ大文字にするプログラムを作りたいのですがどのように作ればいいのでしょうか? 例えば、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> #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));   ・・・ ポインタの配列を宣言して、配列の各要素に動的確保するのと ポインタのポインタを宣言し、ポインタ配列を動的確保して、再度配列の要素に動的確保するのとでは、何か違いがあるのでしょうか? ポインタのポインタを宣言し、ポインタ配列を確保する必要性が良く分かっていないのです。 ネット等で調べて見たのですが、理解力がないのかよく分かりませんでした。 どうか教えてください。

  • C言語で、他の関数で配列を書き換えられないようにしたい

    下のCのプログラムでは、func関数は配列aの先頭要素へのポインタを返します。 main関数の側では配列aの中身を表示します。 しかし、main関数のfor文の中の★の部分をコメントアウトせずに入れると、この配列の中身が書き換わってしまいます。  私はfunc関数以外では、この配列の中身をいじられたくないのです。  なんとかfunc関数を工夫して作成して、func関数以外では、配列の中身が変わらないようにしたいのですが、どうすればよいでしょうか。    とは言ったものの、多分できないだろうなあ、という気がします。  できないならばできないでも仕方ないのですが、確信が持てないのです。 条件があります。 funcでは表示は行なわない。 配列aの中身を表示できるように、funcから呼び出し元へ、aのアドレスまたはaの先頭要素のアドレスがわかるような情報を返す。 #include <stdio.h> char *func(int i) { static char a[]="AAAA"; a[i]='z'; return a; } int main(void) { int i; for(i=0; i<4; i++) { char *p=func(i); /* p[i]='X'; ★配列の中身を書き換えてしまう。 */ puts(p); } return 0; }

  • ポインタ配列のアドレスについて

    #include <stdio.h> int main() { int i; char *ary[] = { "a" , "b" , "c" }; for(i=0; i<3; i++) printf("%u\n" , &ary[i]); return 0; } /*実行結果 6618616 6618620 6618624 */ 上記のプログラムを実行したときポインタ配列*aryがchar型だろうがdouble型だろうがアドレスが4つとびになるのですがなぜなんでしょう?アドレスを求めるプログラム自体が間違っているのでしょうか?

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

    林晴比古さんの「新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です) 他にも色々試してみましたがうまくいかず、かなり考えてみたのですがどうしても分かりません。お分かりの方、どうすれば正しくなるのが教えてください、よろしくお願いします。

  • ポインタを使って構造体の配列を戻り値にするには

    関数の戻り値を構造体の配列(アドレスを受け渡しを利用して)にしたいのですがうまくゆきません。 以下のプログラムではコンパイルはできるのですが、 a0 = 2 a1 = 4198512 a2 = 4329332 と表示されてしまいa1,a2がうまくゆきません。 ********************************************* #include<stdio.h> struct test{ int a; }; struct test *func(void); void main(void) { struct test *data;//構造体ポインタ int i; data = func(); //ポインタにtest関数の戻り値(アドレス)を代入 for(i=0;i<=2;i++){   printf("a%d = %d\n",i,(data+i)->a); //構造体要素を表示 } } struct test *func(void) { struct test data[3]={1,2,3}; //構造体配列を定義 return (&data[0]); //構造体配列の先頭アドレスを返す } ************************************************* test関数から受ける取ったアドレス(&data[0])をポインタ(data)に代入して1づつずらして表示させれば a0=1,a1=2,a=3 となると思ったのですがどこが間違っているのでしょうか? よろしくお願いします。

  • ポインタ配列の開放について

    いつもお世話になっております。 C++言語初心者です。 ポインタ配列の開放(delete)について質問です。 ※includeは省略します。 int main(){   int *a[10];   for(int i=0;i<10;i++){     a[i]=new int[5];   }   delete[] *a;   return 0; } ポインタ配列を開放する場合、 上記のような書き方で正しいのでしょうか? ただ、上記のような記述方法が間違っている場合、 for(int i=0;i<10;i++){   delete[] a[i]; } とやるのはスマートでない気がするので、 もし他に方法がありましたらお願いします。

専門家に質問してみよう