🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C

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

Q&A

解決済

3回答

300閲覧

[C言語]strspy()の引数と動作について

java_biginner

総合スコア12

C

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

0グッド

0クリップ

投稿2019/11/08 08:58

いつもお世話になっています。
今回質問させていただくのは、string.hライブラリの中のstrcpy関数の引数及び動作についてです。

やりたいこととしては
"abcaabbccaaabbbccc"という定型文に対して、
入力した文字列①が含まれているか?また含まれていた場合にその文字列①を入力した文字列②に置き換えるということです。
以下が私が書いたコードになります。

#include <stdio.h> #include <string.h> void hairetuOkikaeSyutan(char *); //hairetuOkikaeSyutanのプロトタイプ宣言。以下の実装を確認してください void hairetuOkikaeSyutan(char *cntHairetu){ //配列の0番目から順に'\0'を格納するための関数 int cnt = 0; //渡されたchar配列の0番目の要素からの現在地を格納する変数 for(int cnt = 0; cnt < 200; cnt++) *(cntHairetu + cnt) = '\0'; //渡されたchar配列の0番目の要素から順に、'\0'で200字分埋める処理 } int hairetuCount(char *); //hairetuCountのプロトタイプ宣言 int hairetuCount(char *sentouP){ //渡された配列の長さをカウントする関数 int hairetuCount = 0; //カウントした文字列の長さを格納する変数 while( *(sentouP + hairetuCount) != '\0') hairetuCount++; return hairetuCount; //カウントした要素数を返す } int main(int argc, char *argv[]){ //複数回使用される重要な変数の宣言部--------------- char *kentiPlace; //入力した文字列①を検知した場所を示すポインタを格納する変数 char kensaStr[200]; //入力した文字列①を格納する変数 char changeStr[200]; //入力した文字列②を格納する変数 char taisyoStr[200]; //検査対象となる定型文を格納する変数 char changeKanryoStr[200]; //文字列置換完了後の文字列を格納する変数 //-------------------------------------------- //配列の初期値に\0を代入する関数を呼び出す hairetuOkikaeSyutan(changeStr); hairetuOkikaeSyutan(taisyoStr); //---------------------------------------- //初期表示および変数入力----------------------- strcpy(taisyoStr, "abcaabbccaaabbbccc"); printf("置換前文字列 : %s\n", taisyoStr); printf("対象文字列 : "); fflush(stdout); scanf("%s", kensaStr); printf("置換文字列 : "); fflush(stdout); scanf("%s", changeStr); //------------------------------------------- //置換文字列の文字数を代入する処理-------- int cntChangeStr = hairetuCount(&changeStr[0]); //置換用文字列の長さを格納する変数の宣言 //---------------------------------------- //検査文字列の文字数を代入する処理-------- int cntKensaStr = hairetuCount(&kensaStr[0]); //置換用文字列の長さを格納する変数の宣言 //---------------------------------------- //文字列の置換---------------------------------------------------------------------------------- strcpy(changeKanryoStr, taisyoStr); kentiPlace = &changeKanryoStr[0]; while( (kentiPlace = strstr(kentiPlace, kensaStr)) != NULL){ //検査対象文字列の中に検査文字列が含まれている間以下の処理を繰り返す 1. //char testStr[200]; 1.  //strcpy(testStr, (kentiPlace + cntKensaStr)); 1.  //strcpy((kentiPlace + cntChangeStr), testStr); 2. strcpy((kentiPlace + cntChangeStr), (kentiPlace + cntKensaStr)); //...2 strncpy(kentiPlace, changeStr, cntChangeStr); kentiPlace = kentiPlace + cntChangeStr; } //------------------------------------------------------------------------------------------- //置換完了後文字列出力--------------------------- printf("置換後文字列 : %s", changeKanryoStr); //置換後の文字列を表示 //----------------------------------------------- return 0; }

実行結果としては以下のようになりました。

置換前文字列 : abcaabbccaaabbbccc 対象文字列 : ab 置換文字列 : ddd 置換後文字列 : dddcccbbccaaaabbccc

期待する結果は
dddcadddbccaadddbbccc
ですが、ソース中の2.の部分が期待する動作をしてくれずに困っています。
コメントアウトされた1.の3行で実行すると期待する結果になりますが、
一体何が違っているのかわかりません。

質問がアバウトになってしまいますが、1.の3行と、2.の1行で何が異なっているせいで期待する結果が得られないかを教えていただきたいです。
また必要な情報が足りなかった場合には追記させていただきます。

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

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

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

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

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

guest

回答3

0

strcpy関数のイメージです(動作未確認)。このとおり、コピー先と元が被っていると大変なことが起こります。

C

1char* strcpy(char* pdst, char* psrc) 2{ 3 char* p = pdst;//pdstは戻り値用に使うため 4 do{ 5 *(p++) = *psrc;//一文字コピー 6 }while('\0' != *(psrc++) );//終端文字もコピーするため比較後にインクリメント 7 8 return pdst; 9} 10コード

投稿2019/11/08 12:04

HogeAnimalLover

総合スコア4830

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

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

java_biginner

2019/11/08 12:34

分かりやすい解答ありがとうございます! 同じ配列を参照しているポインタを引数として渡した場合に無限ループしてしまうのは、 pdst(pに入れなおしている)の持つアドレスがpsrcより先を示しているということですか? たとえば、"abcd"が入っているtestString[5]という配列があったとして pdstに&testString[1]、psrcに&testString[0]を渡すと、本来終端であった場所を1つ前の値で上書きしてしまうことになるが、pdstとpsrcを逆にするか同じポインタを渡すと、意図した結果にはならないが、ループは終わるという認識を私は持っています. 先ほど回答してくださった回答者様にもコメントしましたが、私の書いたソースコードでは、changeKanryoStrの4文字目以降すべてがcで埋まるはずがそうならないためいまいち理解ができていません。 コンパイラの仕様によって動作が変わるというのはありえるのでしょうか?
guest

0

VisualStudioやEclipseを使ってるなら、任意の行で実行を止めて変数の中身を参照することができます
そうやって、実行を止め、各変数の中を見ながら1行づつ実行させていき、変数の中身が想定と違うところを突き止めましょう。

そうすれば、このように当てずっぽでコードを組まなくても済むようになります

投稿2019/11/08 09:46

y_waiwai

総合スコア88038

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

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

java_biginner

2019/11/08 10:11

解答ありがとうございます。 学校の授業でNetBeansを利用していますが、scanf()のコンパイルが正しく動作していないため、 テキストエディターでコーディングしております。 質問欄にあるコードにはありませんが、While文の中に手打ちでprintf()を使って調べていましたが、2.の処理が期待している動作にならないこと以上のことは分かりませんでした。 私の教師もブレイクポイントを有効に使うとよいと言っていたので、この機会にIDEを利用することを考えてみます
guest

0

ベストアンサー

strcopyは1文字ずつコピーするので、コピー先とコピー元の領域が重複してはいけません。

char a[100] = "abcd"; strcpy(a + 1, a);

これで、aabcdとなりそうですが、実際はこのようになります。

abcd
aacd a[0]をa[1]にコピー
aaad a[1]をa[2]にコピー
aaaa a[2]をa[3]にコピー
aaaaa a[3]をa[4]にコピー
aaaaaa a[4]をa[5]にコピー
...

と、いつまでも終端が訪れないので永遠にコピーが続きます

1の3行目は全く違うメモリに一度コピーしているのでメモリの重複が起こりません

余談ですが、hairetuCountはstrlenって関数を使えばいいと思います

投稿2019/11/08 09:40

izmktr

総合スコア2856

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

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

java_biginner

2019/11/08 10:29 編集

わかりやすい解答ありがとうございます。 次にコピーするアドレスの中身が、前回の処理で置き換わっていたため期待する値を上書きしてしまっていたということが問題だということは理解できました。 ではcが3つコピーされるにとどまったのは、コンパイラの仕様によって無限ループを回避したということでしょうか? 余談にあるstrlenはsize_t型で返ってくると本に書いてありましたが、 この型を整数型のように扱っていいものか分からなかったため、とりあえず要素数を数える関数を作っていました。
izmktr

2019/11/08 16:19

私の例は一例で、そちらのソースではないので、デバッガで動作を追ってみれば差異の原因はわかると思います strcpyの説明をよく読めば、コピー領域は重複してはならないと書いてあるので、それを示したまでです size_tはunsigned intです a[i]のiはintではなくsize_t型が望ましいというのが理由だと思います
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問