ページ 11

コンパイラのバグ?

Posted: 2013年3月03日(日) 12:26
by みけCAT
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」というダイアログが表示されました。
どうしてこのような現象が起きるのでしょうか?
コンパイラを変えたほうがいいのでしょうか?

Re: コンパイラのバグ?

Posted: 2013年3月03日(日) 12:36
by softya(ソフト屋)
こちらではヘッダがないのでコンパイルして確認できませんが、他のコンパイラで試したら結果はどうでしょうか?

Re: コンパイラのバグ?

Posted: 2013年3月03日(日) 13:22
by みけCAT
プロジェクト一式を上げます。
gcc version 3.2.3 (mingw special 20030504-1)で試したところ正常のようでした。

追記
もう一度試そうとしたらコンパイル(リンク)が通りません。誤報かもしれません。

Re: コンパイラのバグ?

Posted: 2013年3月03日(日) 13:50
by みけCAT
gcc 4.7.2では正常のようです。

Re: コンパイラのバグ?

Posted: 2013年3月03日(日) 14:28
by softya(ソフト屋)
みけCAT さんが書きました:gcc 4.7.2では正常のようです。
それだとバグでしょうね。
【補足】アセンブルコードを出してみて確認するのが一番なんですが出来ますか?

【さらに補足】
VC++でコンパイルしてみたところ、通知領域にアイコンが表示されるだけで何も起きないですね。
これはどう動作すれば?

Re: コンパイラのバグ?

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

Re: コンパイラのバグ?

Posted: 2013年3月03日(日) 15:29
by みけCAT
提示したコードのコンパイル結果を貼ります。

gcc4.7.0
► スポイラーを表示
gcc4.7.2
► スポイラーを表示

Re: コンパイラのバグ?

Posted: 2013年3月03日(日) 15:35
by みけCAT
gcc4.7.0の285行目と286行目から、wsprintfのstart<iとnumが両方0に固定されている、ということでしょうか?

Re: コンパイラのバグ?

Posted: 2013年3月03日(日) 15:49
by softya(ソフト屋)
どういうわけか最適化でiとstartは固定値であると判断されたみたいですね。まぁ、最適化のバグだと思います。

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

CRTでのアサート条件は
_ASSERTE((unsigned)(c + 1) <= 256);
c = 27770
って事ですね。256を超えるとダメみたいです。

Re: コンパイラのバグ?

Posted: 2013年3月03日(日) 16:02
by みけCAT
ありがとうございました。

Re: コンパイラのバグ?

Posted: 2013年3月03日(日) 22:58
by YuO
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へのキャストが必要になる,ということになります。