質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

3回答

2656閲覧

文字列のポインタを使って、長さが短い順に連結させる

ryu72639

総合スコア14

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2018/07/15 01:57

英単語を入力し,それらを「長さが小さい順に」連結したひとつの文字列を作成し,画面に表示するプログラムを作成しています。しかし、うまく行きません。なぜなのでしょうか?

理想の結果が

英単語何個ですか?4
1個目r
1
r
2個目eee
3
eee
3個目wwwww
5
wwwww
4個目ii
ii
連結結果 :riieeewwwww

ですが、
自分のやつは

英単語何個ですか?4
1個目r
1
r
2個目eee
3
eee
3個目wwwww
5

4個目ii
2
ii
連結結果 :r???

です。

c

1#include <stdio.h> 2#include <string.h> 3 4//英単語の長さ 5int str_length(const char *s) 6{ 7 int len = 0; 8 9 while (*s++){ 10 len++; 11 } 12 return len; 13} 14 15int main(void) 16{ 17 int i, j, k, l, m; 18 int number; //英単語の個数 19 char eitanngo[number][34]; 20 int size[100];//英単語の長さ 21 int tmp; 22 char TMP[34]; 23 24 25 printf("英単語何個ですか?"); 26 scanf ("%d", &number); 27 for(j = 0; j < number; j++){ 28 printf("%d個目",j + 1); 29 scanf("%s", eitanngo[j]); 30 size[j] = str_length(eitanngo[j]); //英単語の長さ 31 printf("%d\n", size[j]); 32 printf("%s\n", eitanngo[j]); 33 } 34 //文字の長さに並べ、連結 35 for(l = 0; l < number; l++){ 36 for(m = l+1; m < number; m++){ 37 if (size[l] > size[m]){ 38 tmp = size[l]; 39 size[l] = size[m]; 40 size[m] = tmp; 41 strcpy(TMP,eitanngo[l]); 42 strcpy(eitanngo[l], eitanngo[m]); 43 strcpy(eitanngo[m], TMP); 44 } 45 } 46 } 47 48 printf ("連結結果 :"); 49 for(k= 0; k < number; k ++){ 50 printf("%s", eitanngo[k]); 51 } 52 printf("\n"); 53//sizeが短い順になってるかの確認 54 //for(k = 0; k < number; k++){ 55 //printf("%d ", size[k]); 56 //} 57 printf("\n"); 58 return 0; 59}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

色々考慮して書き換えたらこうなりました。

C

1#ifdef _WIN32 2#ifdef __GNUC__ // GCC or Clang 3// msvcrtのprintfではzuが使用できないため 4#define __USE_MINGW_ANSI_STDIO 1 5#elif _MSC_VER // VC++ 6// VLA無効 7#define __STDC_NO_VLA__ 1 8// strcpy等の警告無効(わかってやっている) 9#define _CRT_SECURE_NO_WARNINGS 0 10// 0サイズ配列が構造体に最後に来る場合の警告無効 11#pragma warning(disable : 4200) 12#endif // __GNUC__ or _MSC_VER 13#endif // _WIN32 14 15#include <stdbool.h> 16#include <stddef.h> 17#include <stdint.h> 18#include <stdio.h> 19#include <stdlib.h> 20#include <string.h> 21 22#define BUFF_SIZE 1024 23 24struct word { 25 size_t len; 26 char str[]; 27}; 28 29// 行の読み込み 30char *get_line(void); 31// 英単語の個数読み取り 32size_t input_kosu(void); 33// 英単語の読み取り 34struct word *input_word(size_t no); 35// 長さ比較 36int word_len_compare(const void *a, const void *b); 37 38int main(void) 39{ 40 // 英単語の個数を取得する。 41 size_t number = input_kosu(); 42 if (number == 0) { 43 fprintf(stderr, "%s\n", "取得できませんでした。"); 44 exit(1); 45 } 46 47 // 英単語の配列 48#ifdef __STDC_NO_VLA__ 49 struct word **words = 50 (struct word **)malloc(number * sizeof(struct word *)); 51 if (words == NULL) { 52 fprintf(stdout, "%s\n", "メモリ割り当てに失敗しました。"); 53 exit(1); 54 } 55#else 56 struct word *words[number]; 57#endif // __STDC_NO_VLA__ 58 59 // 繋げたときの長さ 60 size_t total_len = 0; 61 for (size_t i = 0; i < number; i++) { 62 words[i] = input_word(i); 63 if (words[i] == NULL) { 64 fprintf(stderr, "%s\n", "取得できませんでした。"); 65 exit(1); 66 } 67 // ついでに長さを追加 68 total_len += words[i]->len; 69 } 70 71 // 長さ順にソート(安定ソートではない) 72 qsort(words, number, sizeof(words[0]), word_len_compare); 73 74 // 連結した文字列 75 char *renketsu = (char *)malloc(total_len + 1); 76 renketsu[0] = '\0'; 77 for (size_t i = 0; i < number; i++) { 78 strcat(renketsu, words[i]->str); 79 // ついでに解放 80 free(words[i]); 81 } 82 printf("%s: %s\n", "連結結果", renketsu); 83 free(renketsu); 84#ifdef __STDC_NO_VLA__ 85 free(words); 86#endif // __STDC_NO_VLA__ 87 88 return 0; 89} 90 91char *get_line(void) 92{ 93 char *line = NULL; 94 size_t line_size = BUFF_SIZE; 95 size_t ptr_size = 0; 96 while (line_size <= SIZE_MAX) { 97 char *new_line = (char *)realloc(line, line_size); 98 if (new_line == NULL) goto error; 99 line = new_line; 100 char *ptr = line + ptr_size; 101 // line_size - ptr_size が intの範囲を超えることはないはず。 102 char *result = fgets(ptr, (int)(line_size - ptr_size), stdin); 103 if (result == NULL) { 104 if (line == ptr) goto error; 105 return line; 106 } 107 size_t read_size = strlen(ptr); 108 if (read_size == 0) return line; 109 if (ptr[read_size - 1] == '\n') return line; 110 // next 111 ptr_size += read_size; 112 line_size += BUFF_SIZE; 113 } 114error: 115 free(line); 116 return NULL; 117} 118 119size_t input_kosu(void) 120{ 121 while (true) { 122 printf("%s: ", "英単語何個ですか?"); 123 fflush(stdout); 124 char *line = get_line(); 125 if (line == NULL) return 0; 126 char **line_end = NULL; 127 long long num = strtoll(line, line_end, 10); 128 free(line); 129 if (0 < num && num <= SIZE_MAX) { 130 return (size_t)num; 131 } 132 printf("%s\n", "1以上の正の整数を入力してください。"); 133 } 134} 135 136struct word *input_word(size_t no) 137{ 138 while (true) { 139 printf("%zu%s: ", no, "個目"); 140 fflush(stdout); 141 char *line = get_line(); 142 if (line == NULL) return NULL; 143 // 単語にするために空白や改行で区切る 144 for (char *ptr = line; *ptr != '\0'; ptr++) { 145 if (strchr(" \t\r\n\f\v", *ptr) != NULL) { 146 *ptr = '\0'; 147 break; 148 } 149 } 150 size_t len = strlen(line); 151 if (len == 0) { 152 free(line); 153 continue; 154 } 155 printf("%zu\n", len); 156 printf("%s\n", line); 157 struct word *word_p = 158 (struct word *)malloc(sizeof(struct word) + len + 1); 159 if (word_p == NULL) { 160 free(line); 161 return NULL; 162 } 163 word_p->len = len; 164 word_p->str[0] = '\0'; 165 strcpy(word_p->str, line); 166 free(line); 167 return word_p; 168 } 169} 170 171int word_len_compare(const void *a, const void *b) 172{ 173 const struct word *a_w = *(const struct word *const *)a; 174 const struct word *b_w = *(const struct word *const *)b; 175 ptrdiff_t diff = a_w->len - b_w->len; 176 // diffはintの幅を超える可能性が一応ある、たぶん 177 if (diff > 0) { 178 return 1; 179 } else if (diff < 0) { 180 return -1; 181 } else { 182 return 0; 183 } 184}

Visual C++(Visual Studioの一部としてインストール)、MinGW-w64、Clang(Windows)、BCC32C、WSL上UbuntuのGCCで動作確認しています。GCCやClangでは"-std=c11"を付けてください。それぞれ最新バージョンです。特に古いVC++では未対応のためエラーになる場合があります。

  1. VLAは使える環境と使えない環境がある。
    VLAはC11ではオプション機能であり、環境によって使えません。C11準拠のコンパイラでは__STDC_NO_VLA__が有るかどうかで判断してください。ただし、Visual C++はVLAが使えないにもかかわらず__STDC_NO_VLA__は設定されていません(これはVisual C++がC11に準拠していないからです)。自分で足しておいてください。
    VLAが使えない環境では素直にmalloc()等でメモリを確保しましょう。free()を忘れずに。
  2. scanf()で数値を読み取る場合は、整数型の範囲を超える場合に動作が未定義である(ここら辺は実はよくわかっていない、仕様としてはstrtol()と同じように動作と書いてあったりもする)という問題があります。strtoll()など型の範囲外の場合でも動作が未定義とならない関数で取得してください。
    今回は面倒だったのでget_lineで1行全てを取得するようにしています。
  3. scanf()で文字列を読み取る場合は、サイズを指定、つまり、"%s"ではなく"%256s"のように取得する大きさを必ず指定しなければなりません。"%s"のような書き方はgetsと同じで、バッファオーバーフローを防ぐ方法がありません。
    ※ 取得サイズを別途指定するscanf_s()(オプション)を使用する方法もあります。
  4. アルゴリズムに拘りがなければソートにはqsort()を使いましょう。ただ、安定ソートとは限らないため、安定ソートである必要がある場合は一工夫必要になります。
  5. 配列のサイズにはsize_tを使いましょう。ただ、printf()で出力は%zuになるのですが、MinGW GCCが標準で利用するC標準ライブラリ(Windows標準のmsvcrt)や古いVisual C++では対応していません。__USE_MINGW_ANSI_STDIOでC標準に準拠したprint()を使うようにしてください。
  6. 英単語の構造体を作って長さを入れているのは長さを英単語と一緒に保存するためです。strlen()はO(n)必要になるため、呼び出し回数が多くなると重くなります。特に、ソートの中で呼び出してしまうと、顕著な速度低下を招きます。
  7. 構造体の最後が大きさ0の配列(大きさ未指定)にする機能はCの標準機能なのですが、Visual C++はMicrosoftの独自拡張だと警告を出します。標準機能のはずなのですが、よくわかりません。
  8. 色々チェックしていないところがあります(途中で面倒になった)。安全性を見込むならassert等で真面目にチェックする必要があるでしょう。
  9. 適度に関数に分けてください。上のコード例ですら、分け方が足りないと思っています。

投稿2018/07/15 23:24

raccy

総合スコア21735

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

pepperleaf

2018/07/16 00:26

ご苦労さまです。 普段、ここまで注意して使ってないので参考になります。 ただ、質問者さんに取っては消化不良になりそうな予感。(個人の感想)
yumetodo

2018/07/16 02:29

私の回答の「C言語で安全に標準入力から数値を取得」に書きましたが、scanfもatoi系もstrtol系を呼び出します。しかし、エラーハンドリングを行わないので使用してはだめです。errnoの値とendptrの2つを確認することが必要です。
yumetodo

2018/07/16 02:29

私の回答の「C言語で安全に標準入力から数値を取得」に書きましたが、scanfもatoi系もstrtol系を呼び出します。しかし、エラーハンドリングを行わないので使用してはだめです。errnoの値とendptrの2つを確認することが必要です。
raccy

2018/07/16 02:46

strtol系はエラーや数字でないなら0、アンダーフローやオーバーフローならLONG_MINやLONG_MAXを返すので、0より大きくSIZE_MAX以下をチェックしているのであれば、errnoのチェックまではしなくてもいいかと思っています。
guest

0

c

1 int number; //英単語の個数 2 char eitanngo[number][34];

numberが未初期化なため、eitanngoをVLAとして作れません。大きさがわからないので。

prog.c:19:3: warning: 'number' is used uninitialized in this function [-Wuninitialized] char eitanngo[number][34]; ^~~~

それはそうと、scanf系関数で数値の入力を受けてはいけません。

C言語で安全に標準入力から数値を取得

投稿2018/07/15 02:26

編集2018/07/15 04:21
yumetodo

総合スコア5850

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

pepperleaf

2018/07/15 03:02

VC++ 2017 だと、 "error C2057" でコンパイル通りませんでした。 > char eitanngo[100][34]; だと期待する結果に一致。
yumetodo

2018/07/15 04:18

>VC++ 2017 だと Visual StudioはC99のVLAに対応していません。通らないのは当然。
yumetodo

2018/07/15 04:19

じゃなくてscanf ("%d", &number);のあとに定義しないとだめだよね。
pepperleaf

2018/07/15 07:10

質問者さんの環境が不明なので、VLA と言われても困る。 それにそんな事まで、元のコードは考慮している?
yumetodo

2018/07/15 18:14

あきらかに元のコードはC99のVariable Length Arrayを使おうとしていますよね?
Eki

2018/07/15 18:49 編集

分かる人から見ると VLA になっていることは分かりますが、質問者さんはそこまでは考えず単に number 個の配列を作りたいなーくらいの感覚なんだろうとだと思います (つまり VLA が普通の配列とは少し違うものだという認識はおそらくないはずです) 。 質問者さんがその後 Visual Studio を使われることがあれば別のエラーを誘発して困られたかもしれませんし、例えばリアルで使用が認められない環境にいる (ex: 大学の授業で教師が認めない) 場合もあると思うので、一言補足があってもよかったのかなとは思います(どこまで回答者がすべきかというところもあるので、あくまで個人的な意見です)。
guest

0

うまく行きません。なぜなのでしょうか?

あなたのコードにバグがあるから。

というのはともかく、

int str_length(const char *s)

これはstrlen という標準関数があります

int number; //英単語の個数
char eitanngo[number][34];

こういう定義はできません。そもそもコンパイルエラーになるはずですが

scanf("%s", eitanngo[j]);

入力文字数が33文字以上あった場合、このコードは破綻します

scanf関数は使用禁止としましょう
業務に使えないコードを書くのは無駄でしかないですぜ

投稿2018/07/15 03:54

y_waiwai

総合スコア87749

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

raccy

2018/07/15 04:47

配列の大きさに変数を使うのはVLAというC99(標準)およびC11(オプション)の機能です。Visual C++のようにC++がサポートする範囲のC99(全体の一部)にしかCについては準拠していないコンパイラでは対応していませんが、GCCやClangのようにC99やC11に準拠したコンパイラではVLAに対応しています。ですので、そのようなコンパイラを使えばコンパイルエラーにはなりません。yumetodoさんが言うように未初期化の変数利用による警告(エラーではない)が出るだけです。
y_waiwai

2018/07/15 05:06

ワーニングなんだから気にしなくてOKOK。っていうわけにもいかないですわな。 どっかのコンパイラがエラー出さないからという理由で、これはエラーではない、と強弁するのは考えものですぜ。
yumetodo

2018/07/15 18:13

warningはすべて確認して不必要なら#pragmaやattributeで部分的に消す。という重要性が改めて。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問