teratail header banner
teratail header banner
質問するログイン新規登録
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

2回答

701閲覧

For 文 ネスト 変数が範囲内に収まらない

退会済みユーザー

退会済みユーザー

総合スコア0

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2022/08/20 12:25

編集2022/08/21 15:08

0

0

前提

C++で以下のような問題に対してコードを書きましたが、アウトプットが思うように出ません。

For文で 1 <= s <= 13としているのですが14以降も出力されてしまいます。
どなたか、理由をご教授いただけないでしょうか。

【問題】

太郎が花子と一緒にトランプ遊びをしようとしたところ、52枚あるはずのカードが n 枚のカードしか手元にありません。これらの n 枚のカードを入力として、足りないカードを出力するプログラムを作成して下さい。

太郎が最初に持っていたトランプはジョーカーを除く52枚のカードです。

52枚のカードは、スペード、ハート、クラブ、ダイヤの4つの絵柄に分かれており、各絵柄には13のランクがあります。

Input
最初の行に太郎が持っているカードの枚数 n (n ≤ 52)が与えられます。

続いて n 組のカードがそれぞれ1行に与えられます。各組は1つの空白で区切られた文字と整数です。文字はカードの絵柄を表し、スペードが'S'、ハートが'H'、クラブが'C'、ダイヤが'D'で表されています。整数はそのカードのランク(1 〜 13)を表しています。

Output
足りないカードをそれぞれ1行に出力して下さい。各カードは入力と同様に1つの空白で区切られた文字と整数です。出力するカードの順番は以下のとおりとします:

絵柄がスペード、ハート、クラブ、ダイヤの順番で優先的に出力する。
絵柄が同じ場合は、ランクが小さい順に出力する。

【作成コード】

C++

1#include<iostream> 2using namespace std; 3 4int main(){ 5 int X[4][13] = {0}; 6 char mark; 7 int num; 8 int marknum; 9 int n; 10 cin >> n; 11 for (int i = 0; i < n; ++i){ 12 cin >> mark >> num; 13 if (mark == 'S'){ 14 marknum = 1; 15 } 16 else if (mark == 'H'){ 17 marknum = 2; 18 } 19 else if (mark == 'C'){ 20 marknum = 3; 21 } 22 else if (mark == 'D'){ 23 marknum = 4; 24 } 25 X[marknum][num] = 1; 26 } 27 28 for (int j = 1; j < 5; ++j){ 29 for (int s = 1; s < 14; ++s){// 1 <= s <13に制限 30 if (X[j][s] == 0){ //足りないカードのみ絵柄、番号を出力 31 if (j = 1){ 32 mark = 'S'; 33 34 } 35 else if (j == 2){ 36 mark = 'H'; 37 38 } 39 else if (j == 3){ 40 mark = 'C'; 41 42 } 43 else if (j == 4){ 44 mark = 'D'; 45 46 } 47 cout << mark << " " << s << endl; 48 } 49 } 50 } 51} 52

発生している問題・エラーメッセージ

【入力データ】

47 S 10 S 11 S 12 S 13 H 1 H 2 S 6 S 7 S 8 S 9 H 6 H 8 H 9 H 10 H 11 H 4 H 5 S 2 S 3 S 4 S 5 H 12 H 13 ...

【出力データ】

S 1 S 16 S 20 S 27 S 28 S 29 S 30 S 31 S 32 S 33 S 34 S 35 S 36 S 37 S 38 S 40 S 45 S 46 S 53 S 54 S 55 S 56 S 67 S 68 S...

回答いただいているお二方ありがとうございます。

以下のように変更した結果、正常な値を出力することができました
変更点
① if (j =1){ ⇨ if (j == 1){
②  j,sの範囲を0からに修正

ここで疑問なのはなぜ修正によって
sが表示されたりされなかったりしたのか、また数字だけ連続で永遠に出力されてしまったのかという点です。
値の範囲が正常に修正されたこととの関連がわからないです。
修正後コード

C++

1#include<iostream> 2using namespace std; 3 4int main(){ 5 int X[4][13] = {0}; 6 char mark; 7 int num; 8 int marknum; 9 int n; 10 cin >> n; 11 for (int i = 0; i < n; ++i){ 12 cin >> mark >> num; 13 if (mark == 'S'){ 14 marknum = 0; 15 } 16 else if (mark == 'H'){ 17 marknum = 1; 18 } 19 else if (mark == 'C'){ 20 marknum = 2; 21 } 22 else if (mark == 'D'){ 23 marknum = 3; 24 } 25 X[marknum][num-1] = 1;//数字の範囲を変更:1<=num<=13を0<=num-1<=12に変更  26 } 27 28 for (int j = 0; j < 4; ++j){ 29 for (int s = 0; s < 13; ++s){ 30 if (X[j][s] == 0){ 31 if (j == 0){ 32 mark = 'S'; 33 34 } 35 else if (j == 1){ 36 mark = 'H'; 37 38 } 39 else if (j == 2){ 40 mark = 'C'; 41 42 } 43 else if (j == 3){ 44 mark = 'D'; 45 46 } 47 cout << mark << " " << s+1 << endl;//s+1=num 48 } 49 } 50 } 51}

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

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

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

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

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

guest

回答2

0

配列の宣言は
型名 変数名[要素数]={初期化列};
です。で、要素数に整数Nを指定した場合、有効なインデックス(添字)は0~N-1のN個になります。決して、1~Nではありません。
さて、配列の範囲外にアクセスした結果は未定義で、つまり「何が起こってもおかしくない」です。そう言ってしまうと「何が起こってもおかしくない」ので質問のような現象になるかも...と言えてしまうのですが、しかしまぁ機械の動きですからある程度予測ができて、そうするとその結果にはならないはずなんだけどなぁ...と思ってしまうわけです。
すでに指摘があるif (j = 1){の働きによってそこまでjが何であろうとjが1になってしかも代入式(=演算の結果)も1つまり真になるのでmarkは常に'S'になるはず。
であるなら、cout << mark << " " << s << endl; によって必ず'S'が表示されるはず...なのに、質問にある実行結果にはSが表示されていない場所が多数ある...
そして、常にJ=1になってしまうということは、j<5は常に真で延々と表示が続いて止まらないという現象が起こるはず、また質問の主題のsの値が大きくなってしまうという現象も、質問のソースでは発生する要素がありません。

まずなによりも、質問に書かれた「結果」は本当にそこに書かれたソースをコンパイルしたものの実行結果かどうか、というのを確認していただけますでしょうか。
そこが違っていると質問そのものが成り立たなくなるので。


AtCoderのテスト環境で症状の再現を確認しました。でも、コンパイラをclangにすると症状が出ないですね。
手元のgcc 9.4.0(WSL/Ubuntu)で試すと、最適化オプション-O0では症状出ず、-O2で出る、ということで「仕組み」は最適化の闇の中ということになってしまいそうです。アセンブリレベルで解析するならもう一歩進めますが、所詮「未定義動作」なので突き詰めてもしかたないかなぁ。競技プログラミングな人には興味の対象外でしょうね。
一応、この辺が症状の出る最小コードかと思います。

C++

1#include <iostream> 2using namespace std; 3 4int main() { 5 int X[4][13] = {0}; 6 for (int j = 0; j < 1; ++j) { //j < 1は影響しないが++jは影響あり 7 for (int s = 12; s < 14; ++s) { //s=13では症状出ない 8 cout << j << "," << s << endl; 9 if (X[j][s] == 0) 10 ; 11 } 12 // break; //これを生かすと症状出ない 13 } 14}

内側for文の外側の状況が影響するので、かなりソースから変形された処理が行われているのでしょう。
私はここでギブアップ。

投稿2022/08/20 12:59

編集2022/08/21 23:04
thkana

総合スコア7736

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

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

退会済みユーザー

退会済みユーザー

2022/08/20 13:50

ありがとうございます。 出力された結果に間違いはありませんでした。 ご指摘いただいた点を修正した結果、正常な値を得ることができましたが、 修正前はなぜsの値が範囲外の値まで表示され続けたのか、理由がはっきりせずにいます
thkana

2022/08/20 14:10

> 修正した結果、正常な値を得ることができましたが では逆に、質問のプログラムをコンパイル/実行したらやはり質問に貼ってある結果がえられるでしょうか? ちなみに、コンパイラとできればバージョンなど教えていただけますか。
退会済みユーザー

退会済みユーザー

2022/08/21 15:11

ご返信いただきありがとうございます。 >では逆に、質問のプログラムをコンパイル/実行したらやはり質問に貼ってある結果がえられるでしょうか? はい。sの値が範囲外の値まで表示され続けています。 >>ちなみに、コンパイラとできればバージョンなど教えていただけますか。 バージョンはC++(9.2.1)でした。 atcoderというサイトのコードテストページで出力を確認しています。
guest

0

if (j = 1){

1を代入してますよ

もひとつ

int X[4][13] = {0};

この配列のとり得る範囲は、
X[0から3][0から12]
しかないので、提示のコードではアクセス違反となります

投稿2022/08/20 12:29

編集2022/08/20 12:34
y_waiwai

総合スコア88178

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

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

退会済みユーザー

退会済みユーザー

2022/08/20 13:52

ありがとうございます。 ご指摘いただいた点を修正した結果、正常な値を得ることができました。 アクセス違反となった場合、今回のように出力結果にsの値が出たり出なかったり、延々に数字が出力されるような現象は起こりえるものなのでしょうか
y_waiwai

2022/08/20 14:07

基本的に、C/C++では実行時のエラーチェックは行われません。 なので、アクセス違反をしても、よっぽど運が良くないと、間違った値のまま実行してしまうこととなります も一つ運が悪いと、全く正常に動いているようにみえる、ってことになったりします
退会済みユーザー

退会済みユーザー

2022/08/21 15:14

ご返信いただきありがとうございます。 単にエラーによって何らかの値が出力されているだけで、出力されている内容に意味はないということが理解できました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.30%

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

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

質問する

関連した質問