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

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

ただいまの
回答率

88.03%

C言語 関数 文字列の反転について

解決済

回答 3

投稿

  • 評価
  • クリップ 0
  • VIEW 5,915

score 17

現在C言語の関数を学んでいる学生です。
ユーザーが入力した文字列の初めから4文字を反転して返す関数を作ろうと下のコードを書きました。
#include <stdio.h>

void hanten();

int main(void) {
    char s[80];
    gets(s);
    hanten(s);
    return 0;
}
void hanten() {
    int i, k;
    char s[80];

    k = 0;
    while (1) {
        if (s[k] == '\0')break;
        k++;
    }

    for (i = (k >= 3 ? 3 : k); i >= 0; i--) {
        printf("%c", s[i]);
    }
}
実行したところ

abcdef
フフフフ続行するには何かキーを押してください . . .

といった具合に、どんな文字列を入力しても
フフフフとコンピュータに笑われてしまうのですが、これは何が原因なのでしょうか?
初歩的な質問だと思うのですが、解説頂けると嬉しいです。


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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+1

main関数でsを渡そうとしているのに、hanten関数の宣言に、引数がありません。

文字列を渡せるようにするには、こうします。
void hanten(char *s) {
    int i, k;
    /* char s[80]; */ /* これは要らない */
    /* 以下略 */
}

フフフフとなるのは、何も入っていない+未初期化の文字列sを処理しようとしているからです。
この文字は開発環境によって異なります。


これだけではまだ完成ではありませんが、残りは質問者さんがご自分でお試しになった方が良いでしょう。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/10/03 12:09

    指摘頂いたところを直したところ、目的の出力を得ることができ、理解を深めることができました。有難うございました。

    キャンセル

+1

まず,gets関数を使われていますが,これはバグを引き起こしやすく,現在の新しい規格では削除されているはずです.私の実行環境でも
”warning: this program uses gets(), which is unsafe.”と怒られてしまいます.(下のリンクにも例としてgets関数が紹介されています.)
なのでscanfを使いましょう.
次に,hanten関数ですが,引数を指定するのを忘れてしまっています!hanten関数の中でchar s[80]とまた定義されていますが,これも消しましょう.
ので以下のようにすると良いでしょう.

#include <stdio.h>

void hanten(char[]);

int main(void) {
  char s[80];
  scanf("%s", s);
  hanten(s);
  return 0;
}
void hanten(char s[]) {
  int i, k;

  k = 0;
  while (1) {
    if (s[k] == '\0')break;
    k++;
  }

  for (i = (k >= 3 ? 3 : k); i >= 0; i--) {
    printf("%c", s[i]);
  }
  printf("\n");
}

バッファオーバーフロー

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/10/03 12:14

    丁寧な説明、ありがとうございます。
    gets関数はバグを引き起こしやすいというのは知らなかったので勉強になりました。

    キャンセル

  • 2015/10/03 12:28

    scanfで単純に"%s"とするのはgetsと同じ問題がおきます。単純な"%s"だけは絶対に使用しないでください。scanfを使用する場合は"%79s"など文字列に入るサイズを必ず指定してください。
    参考: https://ja.wikipedia.org/wiki/Scanf#.E3.83.90.E3.83.83.E3.83.95.E3.82.A1.E3.82.AA.E3.83.BC.E3.83.90.E3.83.BC.E3.83.A9.E3.83.B3

    キャンセル

  • 2015/10/03 12:34

    すみません.ご指摘ありがとうございます.

    キャンセル

+1

main関数の中のsとhanten関数のなかのsはそれぞれ独立しており、別物だからです。hanten関数でのsには入力された文字列が入っているわけでは無く、char s[80];で初期化されたときの文字列(何が入るかはコンパイラや環境によって異なります。今回は毎回"フフフフ"から始まる文字列だったというわけです。)が入っています。単純にそれが出力されただけです。

 詳細な解説など

修正してみたコードを下記に示します。
http://ideone.com/f08zmt
このコードで修正した問題点をあげます。

1. マジックナンバー(80の部分)を使用すべきではない。
char配列のサイズを80と直接書いていますが、この書き方はマジックナンバーと言われ、どのようなプログラミング言語であっても推奨されません。マクロを使って定義することを推奨します。
#define BUFF_SIZE 80
...
  char s[BUFF_SIZE];

2. 関数宣言や定義で引数部分を省略(())を使用すべきではない。
C言語において、関数宣言・定義における仮引数部分が無い、つまり()と書くことは、「引数は何でもいい」といいという意味です。これはどのような引数をつけて関数を呼び出してもコンパイルエラーにはなりません。また、別のところで仮引数を定義しないと、その引数を使うこともできません。文字列を渡したいのなら、きちんと宣言・定義両方に書くべきです。逆に、引数を何も渡さないとするなら、(void)と明記してください。
void hanten(char *s);
...
void hanten(char *s) {
    // sは仮引数として使うため、char s[80];をいれてはいけません。
    ...
}

3. gets使用してはいけない
C言語のgetsはセキュリティ上の問題があるため使用してはいけません。代わりにfgets等を使用してください。また、getsは最新のC言語仕様(C11)では廃止されています。
参照: Wikipedia: gets
    fgets(s, BUFF_SIZE, stdin);

4. 文字列の長さを知りたいならstrlenを使おう。
while部分でどこまで文字列があるかを確認しているようですが、標準関数に文字列の長さを調べるstrlenがあります。車輪の再開発はせずに、なるべく標準関数を使いましょう。
#include <string.h>
...
    k = strlen(s);
    for (i = (k >= 4 ? 3 : k - 1); i >= 0; i--) {
        ...
kには長さが入りますので、k - 1が最後の文字になることだけ注意してください。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/10/04 01:29

    遅くなりましたが、わかりやすくまた、深い説明ほんとうにありがとうございます。
    C言語の入門書を順にこなしているだけではわからないことが多くあることに気づかされました。以後、励みます。

    キャンセル

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

  • ただいまの回答率 88.03%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る