• ベストアンサー

結果が不安定なプログラム

配列に入れられた文字列を、別の配列に逆にして入れ、表示するというプログラムを作っています。 #include<stdio.h> #include<string.h> #include<stdlib.h> void main() {   char str1[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ", str2[] = "";   int i = 0, len = strlen(str1);   char *p1 = str1 + len - 1, *p2 = str2;   while(*(p1 - i) != str1[0] - 1) *(p2 + i) = *(p1 - i++);   printf("str1 = %s\n",p1 = str1);   printf("str2 = %s\n",p2); } 実行結果: str1 = ABCDEFGHIJKLMNOPQRSTUVWXYZ str2 = ZYXWVUTSRQPONMLKJIHGFEDCBA と出るのですが、この文字列を例えば"TANGOHYOJI"とかに変えてみると、実行は出来るんですがprintfの部分が表示されません。 また表示できても、文字が違う文字になって表示されたりします。 あと、この場合に使われている<stdlib.h>はどういった役割を果たしているのでしょうか?これがないとこのプログラムは動かなかったのですが・・。 どうかよろしくお願いします。

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

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

★アドバイス ・文字列の char 型配列の宣言が正しくありません。  正しくはサイズを指定します。  char str1[ 27 ] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";  char str2[ 27 ] = "";  ※str1 にはサイズを省略しても良いですが、str2 はサイズを指定しないと   str2[1] となってしまいます。メモリが足りないため実行時にメモリ破壊   となります。とても危険です。 ・あと p1、p2 でポインタを使っているので i カウンタとか添え字での参照を  行わなくてポインタとして移動、参照すればスッキリしますよ。ソースが。  下にコメント付きで書き直してみました。参考にして下さい。 サンプル: #include<stdio.h> #include<string.h> // 逆順コピー int main( void ) {  // 格納領域(分かりやすく2行で記述)  char str1[ 27 ] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";  char str2[ 27 ] = "";    // 先頭,末尾ポインタの設定  char *p1 = str1 + strlen(str1) - 1;  char *p2 = str2;    // 逆順にコピー  while ( p1 >= str1 ){ ←これだけで良い(ポインタで比較)   *p2++ = *p1--; ←i カウンタを使わない(ポインタなのでスッキリ)  }  *p2 = '\0'; ←忘れずに追加    // 表示  printf( "str1 = %s\n", str1 ); ←p1=str1 としないで素直に str1 で良いと思う  printf( "str2 = %s\n", str2 ); ←ここも同じ  return 0; } 最後に: >あと、この場合に使われている<stdlib.h>はどういった役割を果たしているのでしょうか?  stdlib.h に属する関数を利用したいときにインクルードします。  今回は必要ないと思います。よって >これがないとこのプログラムは動かなかったのですが・・。  ↑  これは何かの偶然でしょうね。たまたま動いただけ。 ・ヘッダファイルは必要に応じてインクルードするものです。  例えば  printf() 関数を利用するには stdio.h をインクルードします。  strlen() 関数を利用するには string.h をインクルードします。  という事になります。 ・以上。

sikimori
質問者

補足

御回答ありがとうございます。 私もこのやり方を何回も試したのですが、駄目だったのでとても助かりました。 ただ私にはイマイチまだ分からないのですが、このやり方は俗に言う「ポインタの値を変えずにデータを参照」するパターンと、「ポインタの値そのものを更新してデータを参照」するパターンのどちらなのでしょうか? 今後の為に教えて頂けたらと思いますm(_ _)m

その他の回答 (3)

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

★??? >私もこのやり方を何回も試したのですが、駄目だったのでとても助かりました。  ↑  これは i カウンタを使わないでポインタで処理する方法のことですか?  何回も試しても駄目だったのは str2 の配列サイズを指定していなかったからですよ。 >ただ私にはイマイチまだ分からないのですが、このやり方は俗に言う  ↑  『このやり方』とはどんなやり方ですかね。私のサンプルの方法ですか? >「ポインタの値を変えずにデータを参照」するパターンと、  ↑  これは『*(p1 - i)』とか、『*(p2 + i)』というパターンです。 >「ポインタの値そのものを更新してデータを参照」するパターンのどちらなのでしょうか?  ↑  これが『*p1』とか、『*p2』というパターンです。 ・上記の解説よりどっちだと思いますか?  『このやり方』とはどんなやり方か分からないのでご自分で判断してみて下さい。 ・以上。

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

> while(*(p1 - i) != str1[0] - 1) *(p2 + i) = *(p1 - i++); whileの条件がstr1の先頭を指すまでとしたいのでしょうが、これではだめ。 ここは文字数が既知である為、素直にfor分の方が良いかも... また、str2にデータをコピーし終わったら'\0'で終端する必要あり。

sikimori
質問者

お礼

御回答ありがとうございます。 確かにfor文の方が確実ですね。 うっかりNULLを入れるの忘れてました(汗)。 おかげ様で無事動きました。ありがとうございました。

  • aris-wiz
  • ベストアンサー率38% (96/252)
回答No.1

提示されているコードでは 実行結果: str1 = ABCDEFGHIJKLMNOPQRSTUVWXYZ str2 = ZYXWVUTSRQPONMLKJIHGFEDCBA になりません。 (確立的にはそうなるかもしれませんが) >str2[] = ""; この記述の意味を理解していますか? > *p2 = str2 ここでstr2のポインタを入れているわけですが、 そのstr2のポインタp2に対して >*(p2 + i) としています。 str2はここではサイズ1の配列です。 確保もしていない領域に対して、 モノを入れてはいけません。 ここまでが分かれば、どうすれば直るか分かると思います。

sikimori
質問者

お礼

御回答下さりありがとうございました。 str2の領域を確保したところ、stdlibがなくても動くようになりました。 確保していない領域に文字を入れようとしていたのですね。 str2[]=""で何かいくらでも領域が取れていると思っていました。 御指摘感謝しています。ありがとうございました。

関連するQ&A

  • C言語に詳しい方お願い致しいます

    文字列Aから文字列Bを除いた結果を表示するプログラムなのですが 一ヶ所分からない部分があります。 #include <stdio.h> #include <string.h> int main(void){  char str1[] = "abcdefg";  char str2[] = "cde";  char str3[128] = "";  char *p1 = str1, *p2;  size_t len;  len = strlen(str2);  while((p2 = strstr(p1,str2)) != NULL) {   strncat(str3,p1,p2 - p1);   p1 = p2 + len;   printf("----\n");   printf("p2=%s\n", p2);   printf("str3=%s\n", str3);   printf("p1=%s\n", p1);  }  strcat(str3,p1);  printf("%s\n",str3);  return 0; } while文中の p1 = p2 + len; なのですが、何故これでp1の内容が「fg」になるのでしょう。

  • プログラム(初心者)

    文字列を入力し中に含まれる空白を削除した文字列を作るプログラムなのですが #include <stdio.h> #include <stdlib.h> int main(void){ char str[100]; int i,j,c; printf("文字列を入力してください\n"); gets(str); i=j=0; do{ c=str[i++]; if(c!= ' ') str[j++]=c; }while(c); printf("空白を削除した文字列は%sです\n",str); return EXIT_SUCCESS; } do文の所何やってるかさっぱりわかりません 教えてください

  • どこがちがうのでしょうか?

    以下の二つのプログラムはユーザーが文字を入力し、80文字以下なら ピリオドを追加して表示するというものです。上はうまくいきますが、下はうまくいきません。なぜでしょうか? #include<string.h> #include<stdlib.h> int main() { char str[80]; int i; printf("文字列を入力してください。\n"); gets(str); if(strlen(str)<80) { for(i=strlen(str);i<79;i++) strcat(str,"."); } printf("%s",str); } #include<stdio.h> #include<string.h> #include<stdlib.h> int main() { char str[80]; int i; printf("文字列を入力してください。\n"); gets(str); if(strlen(str)<80) { for(i=strlen(str)+1;i<79;i++) str[i] = "."; } printf("%s",str); }

  • 下記のプログラムがコンパイラでエラーになります。

    いつも教えて頂きありがとうございます。 実は基本を理解しておらず、キーパンチの練習になっています。 もう1冊自分にも解る書籍を購入しようと思います。 このプログラムの誤りだけでも教えて頂きたいのです。 2000文字では足りないので、2回に分けて投稿させて頂きます。 下記のプログラムです。 /***************************************************** 基本交換法による並べ替えを行うプログラム ******************************************************/ #include <stdio.h> #define N 7 /*データ個数*/ #define ON 1 /*sw : 交換しました*/ #define OFF 0 /*sw : 交換していません*/ #define DATA_LEN 20 /*データ配列の1列の要素数*/ /*関数のプロトタイプ宣言*/ void printData(char p[][DATA_LEN], int n); /*文字列を表示する関数*/ void sort(char p[][DATA_LEN], int n); /*文字列を並び替える関数*/ void str_change(char *ps1,char *ps2); /*文字列と文字列とを交換する関数*/ void atr_cpy(char *pd,char *ps) /*文字列を複写する関数*/ int str_cmp(char *ps1,char *ps2) /*文字列と文字列とを比較する関数*/ main() { /*データの宣言*/ char p_data[N][DATA_LEN] = {"ふじさわ","えのしま","しちりがはま", "ごくらくじ","はせ","ゆいがはま","かまくら"}; /*最初の状態を表示*/ printData(p_data,N); /*並べ替え*/ sort(p_data,N); /*結果を表示*/ printf("¥nソ¥ートしました¥n¥n"); printData(p_data,N); } /**************************************************** ポインタ型から文字列を表示 ****************************************************/ void printData(char p[][DATA_LEN], int n) /* p : 二次元配列*/ /* n : 文字列の要素数*/ { int i; /*添え字*/ for(i=0;i<n;i++) { printf("%s¥n",p[i]); } } /***************************************************** 基本交換法 *****************************************************/ void sort(char p[][DATA_LEN],int n) /* p : 二次元配列*/ /* n : 文字列の要素数*/ { int sw; /*交換したか*/ int i,j; /*添え字*/ sw = ON; for(i = n-1 ; i>=1 && sw == ON ; i--) { sw = OFF; for(j = 0; j< i ;j++) { /* p[j] <= p[j+1]となるようにする*/ if(str_cmp(p[j],p[j+1]) > 0) { str_change(p[j],p[j+1]; /*交換*/ sw = ON; } } } } もう1回投稿致します。

  • C言語のプログラム(ポインタについて)

    /* コマンドライン引数で与えられた長い文字列を長さ10文字ずつに分割してp[100][11]に順番に入れ,表示するプログラム <例>./a.out 1234567890ABCDEFG p[1] = "1234567890" p[2] = "ABCDEFG" */ #include <stdio.h> int main(int argc, char **argv) { int i, len; char *str; len=0; for(i=1; i<argc; i++){ len += strlen(argv[i]); } if((str=(char *) malloc(len+1))==NULL){ perror("malloc"); exit(1); } strncpy(str,argv[1],10); for(i=2; i<argc; i++){ strcat(str,argv[i]); } printf("p[%d] = %s\n", i-1, str); } 上に示した処理をするプログラムを作成したいのですが、 今のソースは単に10文字表示するだけで、ここからどうしたらいいのか見当がつきません・・・・・。 なのでどういった改良をすればよいのか教えていただければ助かります。 /*配列aにファイルから値を読み込み、それを表示した後*/ /*配列に格納された値を下に1つずつずらして表示するプログラム(ポインタを使用して)を作成*/ /*(なお、一番上a[0]にはa[99]の値を入れる)*/ /*<例>*/ /*a[0] = 0 */ /*a[1] = 1 */ /*  ・ */ /*  ・ */ /*a[98] = 98*/ /*a[99] = 99*/ /*a[0] = 99 */ 1つずらしたもの /*a[1] = 0 */ /*  ・ */ /*  ・ */ /*a[98] = 97*/ /*a[99] = 98*/ (読み込むファイル[file-100.dat]には0~99の数字が1つずつ改行しながら入っています) できればこちらにも答えていただければ嬉しいです。 この問のソースは書きませんが、ファイルを配列に読み込んで表示してからの処理がいまいちわからず困っています。 すばやい御回答お待ちしております。よろしくお願いします。

  • 再帰プログラム

    #include<stdio.h> int rstrlen(char*); int main(void) { char str[100]; printf("文字列を入力してください\n"); gets(str); printf("文字数は %d です\n",rstrlen(str)); return 0; } int rstrlen(char *p) { if(*p){ p++; return 1+rstrlen(p); } else return 0; } 文字数を計算するプログラムです。 if(*p)の*pとはNULLを表しているのですか?

  • 作ったプログラミングC何だけど、評価していただけませんが?不足があれば、教えてください!

    文字列「abc」の各アルファベットを1文字ずらすと文字列「bcd 」になり、 文字列「nisidate」の各アルファベットを2文字ずらすと文字 列「pkukfcvg」となる。さらに、 文字列「kibishii」の各アルファベットを2文字ずらすと文字 列「mkdkujkk」となる。 このような、任意の文字列の各要素を任意の文字数ずらすプロ グラムを作成する、なお、zを1文字ずらしたとき のアルファベットはaとする。 #include <stdio.h> #include <string.h> int main(void) { char str[27]="abcdefghijklmnopqrstuvwxyz",str1[50], str2[50]; int x,i,j,len; printf("文字列を入力---> "); scanf("%s", str1); printf("何文字ずらすかを入力---> "); scanf("%d",&x); len=strlen(str1); for(i=0;i<len;i++) { for(j=0;j<27;j++) { if(str1[i]=='z') { str2[i]=str[x-1]; } if(!(str1[i]=='z')) { if(str1[i]==str[j]) { str2[i]=str[j+x]; } } } } printf("%d文字ずらした文字列は%sです\n", x, str2); return 0; } よろしくお願いします!!

  • C言語

    文字列を逆順にするプログラムを考えているのですが分かりません。(例)qwerならrewqです。入力終了は、EOFです。考えたのですが、分かりません。(コンパイルエラーです。)教えてください。宜しくお願いします。#include <stdio.h> unsigned str_length(const char str[]) { unsigned len=0; while (str[len]) len++; return (len); } void put_rstring(const char str[]) { unsigned i = str_length(str): while (i-- >0) putchar(str[i]); } int main(void) { char str[30]; int ch; printf("文字列を入力\n"); /* ----この文字列を入力したあとに、Ctrl+Zを押すと、逆から表示               で反対から、文字列が表示----*/ while (1) { ch=getchar(); if (ch==EOF) break; } printf("逆から表示"); put_rstring(str); puts("です。"); return(0); }

  • C言語で分からないところがあるのですが

    すみません。C言語のポインタで分からないことがあって来ました。 ポインタの理屈は理解してはいるのですが、いざソースコードを書いてみようということになると全く手がつけられずにいます。 以下のソースコードですが、strlen()と同じ働きをする関数mystrlen()と、strcmp()と同じ働きをする関数mystrcmpを、ポインタを使って作成するものです。どこをどうすればいいのか教えてくださいませんか。 #include <stdio.h> int main(void) { char str1[80], str2[80]; int i, j; int len1, len2; printf("第1の文字列を入力してください: "); gets(str1); printf("第2の文字列を入力してください: "); gets(str2); /* * 文字列の長さを確認する */ /* NULL文字(文字列の最後)まで読み飛ばす */ for (len1 = 0; len1 < 80 && str1[len1] != '\0'; len1++) ; /* ループ終了後、len1 に文字列の長さが入っている */ if (len1 < 80) { printf("%s は %d 文字の長さです\n", str1, len1); } else { printf ("第1の文字列が80字以上あります\n"); } /* str2 についても同様 */ for (len2 = 0; len2 < 80 && str2[len2] != '\0'; len2++) ; if (len2 < 80) { printf("%s は %d 文字の長さです\n", str2, len2); } else { printf ("第2の文字列が80字以上あります\n"); } if (len1 < 80 && len2 < 80) { for (i = 0; i < 80 && str1[i] != '\0' && str2[i] != '\0' && str1[i] == str2[i]; i++) ; if (str1[i] == str2[i]) { /* 両者同時に == '\0' のはず*/ printf("文字列は等しい\n"); } else if (str1[i] < str2[i]) { /* str1[i] == '\0' のはず*/ printf("%s は %s より小さい\n", str1, str2); } else { /* str2[i] == '\0' のはず*/ printf("%s は %s より大きい\n", str1, str2); } } /* * 十分なスペースがあれば、str2をstr1の最後に連結する */ if (len1 + len2 < 80) { /* str1 の末尾を探す */ for (i = 0; str1[i] != '\0'; i++) ; /* ループを抜けた段階では i は len1 と同じはずなので、 上記のループを作らず、i の代わりに len1 を用いるのも可 */ /* それ以降に str2 の中身をコピーする */ for (j = 0; str2[j] != '\0'; j++) { str1[i+j] = str2[j]; } str1[i+j] = '\0'; printf("%s\n", str1); } else { printf ("文字列をつなげた長さが80字以上あります\n"); } /* * str2をstr1にコピーする */ if (len1 + len2 < 80) { for (i = 0; str1[i] != '\0'; i++) { str1[i] = str2[i]; } str1[i] = '\0'; printf("%s %s\n", str1, str2); } return 0; }

  • 文字列

    下のプログラムは何をするためのプログラムなのか教えてください。 個人的にはJISコードに関係していると思うのですがさっぱりわかりません。 どなたか詳しい説明お願いします。 #include <stdio.h> #define LEN 255 int main(void) { char str[LEN]; char cipher[LEN]; int ikey; int i,n; printf("文字列を入力せよ : "); scanf("%s", str); printf("鍵を入力 : "); scanf("%d", &ikey); i = 0; while (str[i]!='\0') { n = (str[i]-'A'+ikey)%26; cipher[i] = 'A'+n; ++i; } cipher[i] = '\0'; printf("%s\n", cipher); return 0; }

専門家に質問してみよう