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

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

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

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

Q&A

解決済

4回答

3012閲覧

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

kamecha

総合スコア41

C

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

0グッド

0クリップ

投稿2018/01/22 14:09

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

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

lang

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

###該当のソースコード

lang

1char *str_chr (const char *s, int c){ 2 char *t; 3 do{ 4 if(*s == c){ 5 *t = *s; 6 return t; 7 } 8 }while(*s++); 9 10 return NULL; 11} 12

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

###補足情報
書籍 : 新明解c言語 入門編
演習 11-6

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

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

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

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

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

Zuishin

2018/01/22 14:46

エラーは出なくても警告が二つ出るんじゃないですか? もし出ないのならコンパイラのオプションを確認してください。
Zuishin

2018/01/22 22:29

警告の内容はみなさんのおっしゃる通りですが、どこにどんなバグがあるか警告してくれないコンパイラは使いにくいので直した方がいいと思います。またテキストエディタもちゃんとしたものを使えばコンパイルする前に警告してくれて間違いを修正できます。VS Code や ATOM が人気があるようです。
kamecha

2018/01/22 22:36

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

2018/01/22 22:41

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

2018/01/22 22:42

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

回答4

0

ベストアンサー

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

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

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

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

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

c

1char *str_chr (const char *s, int c){ 2 char *t; 3 do{ 4 if(*s == c){ 5 // *t = *s; // <- s の中身 (=文字) を代入するのではなく 6 t = s; // <- s そのもの (=文字を指すポインタ) を代入する 7 return t; 8 } 9 }while(*s++); 10 11 return NULL; 12}

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

c

1char *str_chr (const char *s, int c){ 2 // char *t; 3 do{ 4 if(*s == c){ 5 // *t = *s; 6 // t = s; 7 // return t; 8 return s; 9 } 10 }while(*s++); 11 12 return NULL; 13}

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


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

c

1// char *str_chr (const char *s, int c){ 2const char *str_chr (const char *s, int c){ 3 // ... 4}

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

投稿2018/01/22 16:28

Eki

総合スコア429

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

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

kamecha

2018/01/22 22:40

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

2018/01/23 00:47

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

2018/01/23 01:03

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

0

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

投稿2018/01/22 15:16

otn

総合スコア84555

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

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

kamecha

2018/01/22 22:28

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

2018/01/23 00:21

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

0

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

c

1char buff[5] = "1234"; 2char *padr; 3padr = 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 00:28

ttyp03

総合スコア16998

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

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

kamecha

2018/01/23 13:53

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

0

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

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

投稿2018/01/22 14:24

HogeAnimalLover

総合スコア4830

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

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

kamecha

2018/01/22 22:31

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問