
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');
}
}
No.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]のまま、やり方を変えるのがいいのでは、と思います。
回答ありがとうございます!マニュアルをよく読んでみます。また(2)の点についても指摘していただきありがとうございました。助言していただいた通りに、a[201],b[201]のまま他のやり方を考えてみます。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
nullと""、\\0とEOFの違いにつ...
-
C++で入力した文字列から数字を...
-
C#でstringをポインタとして渡す
-
WSH(VBS)でJSONの文字列を読み...
-
char型配列の最大要素数
-
c#で他のアプリの文字入力フォ...
-
VBA-DLLの引数受け渡しについて
-
Cで二次元配列の読み込み
-
関数から配列を返すには?
-
C言語 配列の長さの上限
-
c言語
-
CStringからchar*への型変換に...
-
allocってなんですか?
-
System.IO.Directory.GetFiles...
-
c言語 構造体
-
VBA基本構文の作り方 2列の...
-
jQuery.TableSorterのカスタムの件
-
c言語でポインタ変数を用いた配...
-
C# Listを使わずに2次元配列の...
-
VB.NETでファイル名順にファイ...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
%dなどの違い
-
nullと""、\\0とEOFの違いにつ...
-
TCL言語で文字列検索方法を教え...
-
C#でstringをポインタとして渡す
-
c#で他のアプリの文字入力フォ...
-
16進数を2文字ずつ配列に格納し...
-
構造体→文字列→構造体 をする方法
-
C++で入力した文字列から数字を...
-
WSH(VBS)でJSONの文字列を読み...
-
バイナリファイル中の日本語文...
-
シリアル通信で0x00を送信した...
-
sprintf関数での文字列変換につ...
-
文字を反転させる。
-
数字の入った配列をファイルへ...
-
C言語のプログラムについて
-
_tcscpy_s(wcscpy_s)の第二引数...
-
char a[]="thank you for comin...
-
プログラムによく出てくるst...
-
VB6.0でのバイナリデータの扱い...
-
C#で質問があります。
おすすめ情報