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

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

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

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

Q&A

解決済

1回答

1117閲覧

C言語で質問です

taiga

総合スコア22

C

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

3グッド

1クリップ

投稿2015/12/20 14:52

編集2015/12/20 14:59

c

1int main() 2{ 3 char name[32]; 4 5 while (1){ 6 printf("入力---"); 7 fgets(name, sizeof(name), stdin); 8 name[strlen(name) - 1] = '\0'; 9 if (strcmp(name, "") == 0) 10 break; 11 } 12 return 0; 13}

fgetsで文字を入力して文字を入れずにエンターを押した場合はwhileから抜けるというのを作りました
デバッグビルドだと普通に文字を入れたりした後に何も入れずにエンターを押せば抜けれるんですが
リリースビルドだと2回目以降に何もいれずにエンターを押してもループから抜けれません
1回目で何もいれずにエンターを押せば抜けれます
1回でも何かを入力するとどうやっても抜けれなくなります
なぜでしょうか?
コンソールアプリで基礎の勉強中なのでまだ気にしなくてもいいのでしょうか?
VisualStudio2013のC++で作ってます

Chironian, catsforepaw, kozuchi👍を押しています

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

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

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

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

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

guest

回答1

0

ベストアンサー

こんにちは。

ソース・コードは正しいように見えます。デバッグ・モードでの動作が正常と思います。
しかし、リリース・モードですとtaigaさんの言う通り、一度何か入力してEnterした後は、Enterだけ入力しても終了しませんでした。
かなりびっくりですが、どうもMSVC 2013の最適化の不具合っぽく見えます。

name[0]をprintfしてみたところ、どうもname[strlen(name) - 1] = '\0';が最適化で消されていようです。どうみても消してはいけないと思うのですが。

1つだけ少し危険なコードがあるため、下記のように修正したことろ、リリースモードでも正常動作しました。(fgetsからの戻りではないはずですが、もしname[0]が0で戻ってくると危険。)

しかし、if (0 < len)の行をコメントアウトとすると、また元に戻ります。
後、コマンドラインでビルドしても同様でしたし、msvc 2015でも同じでした。
なお、最適化オプションはデフォルトでは/O2のようですが、これを/Otに変えると不具合はなくなりました。
(プロジェクトのプロパティ→C/C++→最適化の最適化をカスタムとし、コマンドラインの追加のオプションに/Otを入力)

C++

1#include "stdafx.h" 2 3int main() 4{ 5 char name[32]; 6 7 while (1){ 8 printf("入力---"); 9 fgets(name, sizeof(name), stdin); 10 int len = strlen(name); 11 if (0 < len) 12 len--; 13 name[len] = '\0'; 14 if (strcmp(name, "") == 0) 15 break; 16 } 17 return 0; 18}

う~~ん、かなりの驚きです。
そこそこMSVCは使って来たのですが、こんな不具合を見たのは初めてです。
未だに私の何かの勘違いではないかと感じてます。

最後の'\n'を0に置き換える処理はそこそこやる処理なので何故にこんな現象が残っているのか?本当に不思議です。

投稿2015/12/20 15:58

Chironian

総合スコア23272

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

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

T.Kanno

2015/12/20 16:48

stdin 特有の動作の gets のあおりを食らっていませんかね? アッセンブラレベルでトレースしてみたいなぁ。
catsforepaw

2015/12/21 06:14

再現しました。明らかにDebugとReleaseで挙動が変わりますね。なかなか興味深いです。 fgetsの手前で`name[0] = '\0'`とやっても回避できます。 Chironianさんのコードのように不正アクセス対策はちゃんとやりましょうということなのでしょうか。
catsforepaw

2015/12/21 08:29

ちょっと思い当たる節があって試してみたところ、どうやら「/Og(グローバルの最適化)オプション」が影響していることが判りました。 /O2には/Ogも含まれているので、コマンドラインの追加オプションに「/Og-」を入れてやると、正しく動くようになりました。また、その逆に、最適化を「無効 (/Od)」にすると正しく動くはずですが、コマンドラインの追加オプションに「/Og」を指定すると、問題が発生するようになります。 アセンブリソースを出力して確認してみましたが、最初の1回だけstrlenを実行し、それ以降はその結果を使い回しているようです。試しに2回目で1回目より長い文字列を入力すると、確かに1回目と同じ長さに切られています。 バグっぽいですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問