限定しりとり

aizu online judgeというサイトの9Bの問題に関する質問です。scanfで文字数を読み取り、strncpyでその文字数分コピーするということをしているのですが、三回目の読み取りで1を入力するとなぜか二文字コピーされてしまいます。いろいろ検証してみましたが原因がよくわかりません。どなたか教えてください。よろしくお願いします。

問題文 (https://onlinejudge.u-aizu.ac.jp/courses/lesson/ …
シャッフル

1つのアルファベットが描かれた n 枚のカードの山をシャッフルします。

1回のシャッフルでは、下から h 枚のカードをまとめて取り出し、それを残ったカードの山の上に積み上げます。

カードの山は以下のように1つの文字列で与えられます。

abcdeefab
最初の文字が一番下にあるカード、最後の文字が一番上にあるカードを示しています。

例えば、これを h が 4 でシャッフルすると、最初の4文字 abcd が、残りの文字 eefab の末尾へ連結されるので以下のようになります:

eefababcd
このシャッフルを何回か繰り返します。

カードの山の最初の並び(文字列)と h の列を読み込み、最後の並び(文字列)を出力するプログラムを作成して下さい。

自分の書いたコード
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main()
{
char a[201], b[201];
int m;
int h;
int n=0;
while(1) {
scanf("%s", a);
if(a[0]=='-') break;

scanf("%d", &m);

for(int i=0; i<m; i++) {
scanf("%d", &h);
strncpy(b, a+n, h);
printf("%s\n", b); //配列bの状況を確認するために入れました。 
strcat(a, b);
n+=h;
}
printf("%s", a+n);
putchar('\n');
}
}

A 回答 (1件)

(1) strncpy のマニュアルをよく読んでください。


おそらく「指定した長さの文字列のコピー」という点を勘違いしています。

(2)そもそも、現在のプログラムには致命的な問題があります。


(1)について。
例)
https://linuxjm.osdn.jp/html/LDP_man-pages/man3/ …
> 警告: src の最初の n バイトの中にヌルバイトがない場合、 dest に格納される文字列はヌルで終端されないことになる。


strncpy(b, a+n, h);

a={'a','b','c','d','e','\0'}
n=0
h=3
とすると、これを実行後
b={'a','b','c'}
となります。
「a の最初の 3 バイトの中にヌルバイトがない」ので「b に格納される文字列はヌルで終端されないこと」になります。
b[3]以降はstrncpy実行前のままです。'\0' である保証はありません。

strcat(a, b);
上記に続けて実行すると、bの「終わり」がはっきりしないので
a={'a','b','c','d','e','a','b','c'}
までは確実ですが、その後がどこまで続くかわかりません。


おそらく「配列bが0でクリアされた状態で始まる」ような設定(例えばデバッグモード)でコンパイルしたため、以下のように動作しているのでしょう。
a="aabc"
h=1→ b={'a' ,0,0,...} /* たまたま b[1]=0だったので "a"と解釈される */→ a+n="abca"
h=2→ b={'a' ,'b' , 0,0,...} /* たまたま b[2]=0だったので "ab"と解釈される */ → a+n="caab"
h=1→ b={'c' ,'b' ,0,0,...} /* b[0]に'c'がコピーされたが、前の b[1]='b'が残ってるので "cb"と解釈される */→ a+n="aabcb"

本来は(初期化しなければ)中身がどうなっているか決まってないのがC言語です。
最初のh=1の段階で
a+n="abcakopasj-0gajpadj-afspf2^09=UJAH0HFAS"
などとなってもおかしくありません。

文字列として扱いたいのなら、strmcpyの後に'\0'を明示的に設定する必要があります。


(2) strncpyなどどうでもよくなるような欠陥があります。
シャフルするときに、配列aの後にstrcatで付けたすようになっていますが、
strcatは配列の容量を増やしたりはしません。
付けたしていって200文字を越えたら、a[201]での範囲を越えていきます(バッファオーバーラン)
・a:200文字
・h=199
とすると、strcatによって 399文字の文字列が作られます。
完全にa[201]をオーバーしてます。
仕様によると、最大100回操作するので
このままでは aは200+199*100文字の文字列になる可能性があります。


それだけの長さの配列を用意する、という手段もありますが、
今のa[201],b[201]のまま、やり方を変えるのがいいのでは、と思います。
    • good
    • 0
この回答へのお礼

回答ありがとうございます!マニュアルをよく読んでみます。また(2)の点についても指摘していただきありがとうございました。助言していただいた通りに、a[201],b[201]のまま他のやり方を考えてみます。

お礼日時:2020/08/29 17:40

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!