コンパイラのバグ?

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
アバター
みけCAT
記事: 6247
登録日時: 9年前
住所: 千葉県
連絡を取る:

コンパイラのバグ?

#1

投稿記事 by みけCAT » 7年前

Windows Vista SP2 32ビット
Dev-C++ 5.4.0
gcc 4.7.0

次のコードが不可解な挙動をして、困りました。

コード:

#include <windows.h>
#include <ctype.h>
#include <limits.h>
#include "cpb_main.h"
#include "isprime.h"

static unsigned long long str2ull(const char* buf) {
	unsigned long long result=0;
	while(*buf!=0 && *buf>='0' && *buf<='9') {
		result=result*10+(*buf-'0');
		buf++;
	}
	return result;
}

enum {
	STATUS_NONUMBER,
	STATUS_NUMBER,
	STATUS_INVALIDNUMBER
};

int primeJudge(HWND hWnd,char* prime,const config_t* config) {
	HGLOBAL hMem;
	WCHAR* str;
	int error=0;
	int i,length,start,nowlength,status;
	if(IsClipboardFormatAvailable(CF_UNICODETEXT)) {
		if(OpenClipboard(hWnd)) {
			if(hMem=(HGLOBAL)GetClipboardData(CF_UNICODETEXT)) {
				WCHAR* strtemp;
				if(strtemp=(WCHAR*)GlobalLock(hMem)) {
					length=lstrlenW(strtemp);
					str=HeapAlloc(GetProcessHeap(),0,(length+1)*sizeof(WCHAR));
					if(str)lstrcpyW(str,strtemp); else error=1;
					GlobalUnlock(hMem);
				} else error=1;
			} else error=1;
			CloseClipboard();
		} else error=1;
	} else error=1;
	if(error)return 0;

	if(config->readMax>0 && config->readMax<2097152) { /* readmax<2^21 */
		int kyoyo_length=config->readMax*1024/sizeof(WCHAR);
		if(length>kyoyo_length) {
			if(config->noReadOverMax) {
				HeapFree(GetProcessHeap(),0,str);
				return 0;
			}
			length=kyoyo_length;
		}
	}

	status=STATUS_NONUMBER;
	for(i=0;i<length;i++) {
		switch(status) {
			case STATUS_NONUMBER:
				if(isdigit((int)str[i])) {
					nowlength=1;
					status=STATUS_NUMBER;
					start=i;
				} else if((int)str[i]=='.' && !config->stopOnPeriod) {
					nowlength=0;
					status=STATUS_INVALIDNUMBER;
				}
				break;
			case STATUS_NUMBER:
			case STATUS_INVALIDNUMBER:
				if(isdigit((int)str[i])) {
					nowlength++;
					if(nowlength>19)status=STATUS_INVALIDNUMBER;
				} else if((int)str[i]=='.' && !config->stopOnPeriod) {
					status=STATUS_INVALIDNUMBER;
				} else if(!config->ignoreComma|| (int)str[i]!=',') {
					if(status==STATUS_NUMBER) {
						int j,num=0;
						char numberBuffer[32];
						unsigned long long target;
						for(j=start;j<i;j++) {
							MessageBox(hWnd,"aaa","test",MB_OK);
							if(isdigit((int)str[j]))numberBuffer[num++]=(int)str[j];
						}
						numberBuffer[num]=0;
						wsprintf(numberBuffer,"%d %d %d %d",start,i,start<i,num);
						MessageBox(hWnd,numberBuffer,"test",MB_OK);
						target=str2ull(numberBuffer);
						if(target<=LLONG_MAX && isprime(target)) {
							lstrcpy(prime,numberBuffer);
							HeapFree(GetProcessHeap(),0,str);
							return 1;
						}
					}
					status=STATUS_NONUMBER;
				}
				break;
		}
	}
	if(status==STATUS_NUMBER && (int)str[i]==0) {
		int j,num=0;
		char numberBuffer[32];
		unsigned long long target;
		for(j=start;j<i;j++) {
			if(isdigit((int)str[j]))numberBuffer[num++]=(int)str[j];
		}
		numberBuffer[num]=0;
		target=str2ull(numberBuffer);
		if(target<=LLONG_MAX && isprime(target)) {
			lstrcpy(prime,numberBuffer);
			HeapFree(GetProcessHeap(),0,str);
			return 1;
		}
	}

	HeapFree(GetProcessHeap(),0,str);
	return 0;
}
クリップボードに「0;」という文字列をかぎかっこなしでコピーした状態で上のプログラムを実行すると、
「0 1 0 0」というダイアログが表示されます。
これは、start==0,i==1のとき、start<iが偽であることを示しています。
これは意味不明です。
さらに、wsprintf(numberBuffer,"%d %d %d %d",start,i,start<i,num);のあとにstart=1;という行を加えると、
正しく「0 1 1 1」というダイアログが表示されました。
どうしてこのような現象が起きるのでしょうか?
コンパイラを変えたほうがいいのでしょうか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: コンパイラのバグ?

#2

投稿記事 by softya(ソフト屋) » 7年前

こちらではヘッダがないのでコンパイルして確認できませんが、他のコンパイラで試したら結果はどうでしょうか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
みけCAT
記事: 6247
登録日時: 9年前
住所: 千葉県
連絡を取る:

Re: コンパイラのバグ?

#3

投稿記事 by みけCAT » 7年前

プロジェクト一式を上げます。
gcc version 3.2.3 (mingw special 20030504-1)で試したところ正常のようでした。

追記
もう一度試そうとしたらコンパイル(リンク)が通りません。誤報かもしれません。
添付ファイル
sokudokensei_beta.zip
プロジェクト
(546.42 KiB) ダウンロード数: 26 回
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6247
登録日時: 9年前
住所: 千葉県
連絡を取る:

Re: コンパイラのバグ?

#4

投稿記事 by みけCAT » 7年前

gcc 4.7.2では正常のようです。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: コンパイラのバグ?

#5

投稿記事 by softya(ソフト屋) » 7年前

みけCAT さんが書きました:gcc 4.7.2では正常のようです。
それだとバグでしょうね。
【補足】アセンブルコードを出してみて確認するのが一番なんですが出来ますか?

【さらに補足】
VC++でコンパイルしてみたところ、通知領域にアイコンが表示されるだけで何も起きないですね。
これはどう動作すれば?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
みけCAT
記事: 6247
登録日時: 9年前
住所: 千葉県
連絡を取る:

Re: コンパイラのバグ?

#6

投稿記事 by みけCAT » 7年前

softya(ソフト屋) さんが書きました:【補足】アセンブルコードを出してみて確認するのが一番なんですが出来ますか?
アセンブルコードは出せますが、読めません。
► スポイラーを表示
【編集】これは新しい、書き換えたコードから出力したものです。
softya(ソフト屋) さんが書きました:【さらに補足】
VC++でコンパイルしてみたところ、通知領域にアイコンが表示されるだけで何も起きないですね。
これはどう動作すれば?
起動し、クリップボードに素数を含む文字列をコピーすると「うおおおおおおお!素数だあああああああ!」と言います。
アイコンを右クリックでメニューが出ます。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6247
登録日時: 9年前
住所: 千葉県
連絡を取る:

Re: コンパイラのバグ?

#7

投稿記事 by みけCAT » 7年前

提示したコードのコンパイル結果を貼ります。

gcc4.7.0
► スポイラーを表示
gcc4.7.2
► スポイラーを表示
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6247
登録日時: 9年前
住所: 千葉県
連絡を取る:

Re: コンパイラのバグ?

#8

投稿記事 by みけCAT » 7年前

gcc4.7.0の285行目と286行目から、wsprintfのstart<iとnumが両方0に固定されている、ということでしょうか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: コンパイラのバグ?

#9

投稿記事 by softya(ソフト屋) » 7年前

どういうわけか最適化でiとstartは固定値であると判断されたみたいですね。まぁ、最適化のバグだと思います。

【補足】 VC++2008でコンパイル実行している状態で、クリップボードに「決定の」と言う文字列が入ったら落ちました。
場所は
if(isdigit((int)str)) {
str = 0x007a8c00 "決定の"
i = 0

CRTでのアサート条件は
_ASSERTE((unsigned)(c + 1) <= 256);
c = 27770
って事ですね。256を超えるとダメみたいです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
みけCAT
記事: 6247
登録日時: 9年前
住所: 千葉県
連絡を取る:

Re: コンパイラのバグ?

#10

投稿記事 by みけCAT » 7年前

ありがとうございました。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

YuO
記事: 941
登録日時: 9年前
住所: 東京都世田谷区

Re: コンパイラのバグ?

#11

投稿記事 by YuO » 7年前

softya(ソフト屋) さんが書きました:CRTでのアサート条件は
_ASSERTE((unsigned)(c + 1) <= 256);
c = 27770
って事ですね。256を超えるとダメみたいです。
in ISO/IEC 9899:1999 7.4 Character handling <ctype.h> ¶1
In all cases the argument is an int, the value of which shall be representable as an unsigned char or shall equal the value of the macro EOF. If the argument has any other value, the behavior is undefined.
なので,未定義の振る舞いになる場合にassertしている,ということだと思います。
charがsingned 8bitな環境で8bit系の文字コードを使っている場合,intへのキャストではなくunsigned charへのキャストが必要になる,ということになります。

閉鎖

“C言語何でも質問掲示板” へ戻る