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

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

ただいまの
回答率

90.75%

  • C

    3454questions

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

文字列とポインタの関係性について 3

解決済

回答 4

投稿

  • 評価
  • クリップ 0
  • VIEW 293

nonbirikame

score 32

前提

書籍で勉強している学生です。
書籍の解答がないため問題のヒントや解説をしていただけると嬉しいです。

問題

文字列sの中に、文字cが含まれていれば、その文字(複数含まれる場合は、最も先頭側の文字)へのポインタを返し、含まれていなければ空ポインタを返す関数を作成せよ。添字演算子を用いずに実現すること。

char *str_chr (const char *s, int c){/* ... */}

該当のソースコード

char *str_chr (const char *s, int c){
    char *t;
    do{
        if(*s == c){
            *t = *s;
            return t;
        }
    }while(*s++);

    return NULL;
}

疑問点

上記のコードはコンパイルエラーは出ないのですが、実行しようとすると上手く動作してくれません。
どこがいけないのか指摘して頂けると有り難いです。
また、考え方自体が的外れな場合それも指摘して頂きたいです。

補足情報

書籍 : 新明解c言語 入門編
演習 11-6

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • nonbirikame

    2018/01/23 07:36

    有り難うございます。現在Sublime Textを使用しているため変更も考えてみます。

    キャンセル

  • Zuishin

    2018/01/23 07:41

    Sublime Text もいいエディタと聞いています。詳しくありませんが、編集中に C の構文チェックをするプラグインがあるんじゃないでしょうか?

    キャンセル

  • nonbirikame

    2018/01/23 07:42

    調べて、導入していきます! 有り難うございました!

    キャンセル

回答 4

checkベストアンサー

+3

まず、考え方は正しいです。

文字を発見した場所で*t = *sという代入をしていますね。
これはポインタ変数tが指し示しているメモリにsが指し示しているメモリの内容 (=この場合は発見した文字) を代入する、という意味になります。

ところがそのtchar *t;と宣言されていて、初期化されていません。つまりこのtには何かよくわからない変な値が入っているわけです。当然正しいメモリを指してなどいませんので、普通はアクセス違反で落ちてしまいます。

仮に落ちなかった場合でも、何かよくわからない変な数字であるtreturn t;しているわけなので、関数str_chr()の戻り値自体が無意味なものになってしまっているわけですね。

欲しいのはその文字を指すポインタですから、*t = *s ではなく t = s とすればよいです。

char *str_chr (const char *s, int c){
    char *t;
    do{
        if(*s == c){
            // *t = *s;  // <- s の中身 (=文字) を代入するのではなく
            t = s;       // <- s そのもの (=文字を指すポインタ) を代入する
            return t;
        }
    }while(*s++);

    return NULL;
}


ただし代入してすぐ return t;していますね。だったら最初からchar *t;を作らずとも、return s;してしまえばよいことになります。

char *str_chr (const char *s, int c){
    // char *t;
    do{
        if(*s == c){
            // *t = *s;
            // t = s;
            // return t;
            return s;
        }
    }while(*s++);

    return NULL;
}

これでとりあえず、意図する動きはできたと思います。


ところで、tを使うにせよ使わないにせよ、修正するべき点があります。それは戻り値の型で、char * ではなく const char * にするべきです。

// char *str_chr (const char *s, int c){
const char *str_chr (const char *s, int c){
    // ...
}


引数でconst char *として受けとるのは、「私は中身を書き換えないので読み込み専用のものを渡しても大丈夫ですよ」という宣言です。ところがchar *で返してしまうと、戻り値を受けた側がそれを使ってその「読み込み専用のもの」を書き換えることができてしまいます。もちろん、char *で返したとしてもそれを使った書き換えをしなければ別にいいのですが、きちんと const をつけておけば、うっかり書き換えてしまったときにコンパイラがエラーにしてくれます。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/01/23 07:40

    分かりやすい説明有り難うございます。
    cahr *s で宣言した後の s は、ポインタsが指す文字のアドレスを意味する訳ですね。

    キャンセル

  • 2018/01/23 09:47

    一応念のため指摘です。

    > char * ではなく const char * にするべきです。

    それは出題者(=本の著者)に言うべきですね。ただ、出題者はC標準のstrchrと同じものを自分で作らせようとしているのだと思われますが、その本家strchrのプロトタイプが、引数は`const char *`で戻り値が`char *`となっています。Cではそういうものなのでしょう。
    ちなみに、C++標準のstrchrのプロトタイプは`char *`版と`const char *`版で分かれています。

    キャンセル

  • 2018/01/23 10:03

    そうでした、本の出題だということを失念していました。

    恥ずかしながら strchr() の戻り値が char * だということを今知りました。StackOverflow にもその件に関する質問があって (https://stackoverflow.com/questions/14367727/how-does-strchr-implementation-work) 、そちらの回答者は過去に const が導入されたときの互換性維持のためと推察していますね。

    勉強になりました、ありがとうございます。

    キャンセル

+2

*t = *s;は、「ポインタsの指すところの1文字を、ポインタtの指すところにコピーする」という動作ですよ。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/01/23 07:28

    なるほど、ということはtは不要ですね。

    キャンセル

  • 2018/01/23 09:21

    tを使うなら、
    t = s;
    が正しいですね。
    ポインタ自体と、「ポインタの指す先のもの」の区別を意識しましょう。

    キャンセル

+1

ローカル変数tは事実上不要と思われます。条件に合致したらそのときのポインタsをそのまま返せばいい。

その他、ループの書き方が不自然だと思います。多分、文字が見つからないときわざわざ終端文字との比較を行います。終端文字も検索対象とする、という仕様ですか?

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/01/23 07:31

    その意図はありません。
    では、do while 文でなくwhile文だけの方がよろしいのでしょうか。

    キャンセル

+1

char *str_chr (const char *s, int c)
この関数を次のように呼び出した場合のことを考えて見ます。

char buff[5] = "1234";
char *padr;
padr = str_chr(buff, '3');


仮にメモリ上に次のようにデータが配置されたとします。
番地は適当です。

番地|データ
1000|'1'   // buff[0]
1001|'2'   // buff[1]
1002|'3'   // buff[2]
1003|'4'   // buff[3]
1004|'\0'  // buff[4]


str_chrが受け取る引数s1000になります。
つまり

s1000   ← 番地(アドレス)
*s'1'   ← sが持つ番地にある実際の値

sのアドレスを基点に順にデータを見て行き、引数cと同じ値を探します。
つまり
の処理はsをインクリメントする。
同じ値かを見るのは*scを比較する。

発見したらそのアドレスを返します。
つまり
インクリメントしているsを返します。
例で言うとcには'3'が渡されるので、一致する値が格納されているのは1002番地です。

つまり
pdarには1002が入ります。
*pdr'3'になります。

提示されているコードのどこに問題があるかまでは書きません。
上記を良く理解できれば修正箇所も見えてくると思います。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/01/23 22:53

    有り難うございます。
    *s と s の違いがよく分かりました。

    キャンセル

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

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

関連した質問

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

  • C

    3454questions

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