ページ 11

static で値を0以外に初期化するとバグが起きます

Posted: 2012年5月22日(火) 15:39
by あsdf
OpenNI(キネクト)、OpenCV、OpenGL、OpenALを使っています。

staticで変数を0以外に初期化すると、光源の設定が無効になります。0にすると、有効となります。

その変数を定義した場所は、あるサブルーチンの中で、プログラム中ではその関数を使っていません。
しかし、その使っていない関数の中のstatic変数を0以外で初期化すると、無効になるのです。また、グローバル変数を0以外で初期化しても同様です。

普通、関数を実行していなければ、プログラムには影響しないと思うのですが、今回はstaticでプログラム開始前に静的領域に格納しているため、このようなバグが起きたのだと思います。ただ、理由が全くわかりません。

静的領域の容量をオーバーしてしまったためにこのようなバグが起きたのでしょうか?

staticではなく、普通にプログラム領域に変数を格納(ローカル変数)すると、大丈夫です。

Re: static で値を0以外に初期化するとバグが起きます

Posted: 2012年5月22日(火) 15:49
by softya(ソフト屋)
配列で添字範囲外をアクセスしているか、ポインタがとんでもないところを指している可能性があります。
VC++などのデバッガの機能でその変数を参照した時にデバッグブレークする機能がありますので試してみると良いと思います。

Re: static で値を0以外に初期化するとバグが起きます

Posted: 2012年5月22日(火) 17:02
by あsdf
おっしゃるとおりだと思い、いろいろ探してみたのですが、見つかりませんでした。

なぜ、0で初期化するとうまくいくのに、0以外だとダメなのでしょうか。
ここが物凄く気になります。

おそらく、格納される場所が違うのでしょうが。。。

Re: static で値を0以外に初期化するとバグが起きます

Posted: 2012年5月22日(火) 17:05
by softya(ソフト屋)
あsdf さんが書きました:おっしゃるとおりだと思い、いろいろ探してみたのですが、見つかりませんでした。

なぜ、0で初期化するとうまくいくのに、0以外だとダメなのでしょうか。
ここが物凄く気になります。

おそらく、格納される場所が違うのでしょうが。。。
それはデバッガの機能でデータブレークされたと言うことでしょうか?
目視で探しても限界があり探し出すことは困難です。

Re: static で値を0以外に初期化するとバグが起きます

Posted: 2012年5月23日(水) 17:30
by あsdf
すいません。実はこれは友達のプログラムで、質問されただけなんです。

友達は、現状でも動くからいいと言っていて・・・

自分としては、なぜこのようなことが起きるのかということが知りたいだけなんです。

解決法ではなく、理由を教えていただきたいです。。。

Re: static で値を0以外に初期化するとバグが起きます

Posted: 2012年5月23日(水) 17:58
by softya(ソフト屋)
あsdf さんが書きました:すいません。実はこれは友達のプログラムで、質問されただけなんです。

友達は、現状でも動くからいいと言っていて・・・

自分としては、なぜこのようなことが起きるのかということが知りたいだけなんです。

解決法ではなく、理由を教えていただきたいです。。。
staticな変数はメモリ上の静的領域に配置されますが、ここには色々なファイルや関数のstaticな変数が集められています。
ここには当然ながらstaticな配列なども近いメモリ空間に配置されていますので、もし配列アクセスで添字範囲外アクセスをすると予想もしない変数の値を見てしまう可能性があります。
あるいは、static変数をポインタ参照している場合にポインタ値が誤操作されれば何処の変数を参照するかは予想できません。偶然ポインタ変数がstatic変数のアドレスを指した可能性もあります。
更に配列の添字やポインタ値の異常は連鎖する可能性もあるので引き金となった物はもっと別の配列操作やポインタ処理かも知れません。

参考プログラム。すべての環境での再現性はありません。 cygwin gcc 4.3.4 gcc main.cでコンパイル・リンク。

コード:

#include <stdio.h>

void func(void)
{
	static int a = 99;
}

int main()
{
	static int b[3] = { 3,5,7 };//やべっintを書き忘れ。
	int i;
	for( i=-1 ; i<3 ; i++ ) {
		printf( "b[%d]=%d\n", i, b[i] );
	}
}
上記のプログラムの例だと-1の添字でaの変数が参照されています。

コード:

$ ./a
b[-1]=99
b[0]=3
b[1]=5
b[2]=7
ちなみに、条件次第で何が起こるかわからないのでバグを放置するのは賢明な行為とはいえません。
ましてや再現性が確実なら後々のためにバグを取っておくほうが技術力アップにもつながります。
興味があるなら、その友人のプログラムのバグの原因を調べさせてもらってはどうでしょうか? 良い教材だと思うんですけどね。

Re: static で値を0以外に初期化するとバグが起きます

Posted: 2012年5月23日(水) 18:17
by softya(ソフト屋)
ポインタも織り交ぜたバージョン。

コード:

#include <stdio.h>

void func(void)
{
	static int a = 99;
}

int main()
{
	static int b[3] = { 3,5,7 };
	int *px = &b[1];
	int i;
	for( i=-1 ; i<4 ; i++ ) {
		printf( "b[%d]=%d\n", i, b[i] );
	}
	printf( "*(px)=(%p)=%d\n", px, *(px) );
	printf( "*(px-2)=(%p)=%d\n", px-2, *(px-2) );
	printf( "*(px+2)=(%p)=%d\n", px+2, *(px+2) );
	
}

void func2(void)
{
	static int c = 1111;
}
実行結果

コード:

$ ./a
b[-1]=99
b[0]=3
b[1]=5
b[2]=7
b[3]=1111
*(px)=(0x402014)=5
*(px-2)=(0x40200c)=99
*(px+2)=(0x40201c)=1111

Re: static で値を0以外に初期化するとバグが起きます

Posted: 2012年5月25日(金) 00:54
by あsdf
VisualStudio2010で同様のプログラムを実行してみると、同じ結果が得られました。

static変数は定義された順に静的領域に連続的に格納されるということでしょうか?

Re: static で値を0以外に初期化するとバグが起きます

Posted: 2012年5月25日(金) 01:00
by h2so5
そうとは限らないですね。

例えばgccでこのコードを実行すると

コード:

#include <stdio.h>
 
int main(void) {
        static int a;
        static int b;
        static int c;
        static int d;
        printf("%d\n", (int)&a);
        printf("%d\n", (int)&b);
        printf("%d\n", (int)&c);
        printf("%d\n", (int)&d);
        return 0;
}
結果はこうなります。

コード:

134520872
134520868
134520864
134520860

Re: static で値を0以外に初期化するとバグが起きます

Posted: 2012年5月25日(金) 01:03
by softya(ソフト屋)
あsdf さんが書きました:VisualStudio2010で同様のプログラムを実行してみると、同じ結果が得られました。

static変数は定義された順に静的領域に連続的に格納されるということでしょうか?
その保証はありません。
C/C++の規格に書かれた事ではありませんので、どうなるかはコンパイラ・リンカを開発したスタッフの趣味と言って良いと思います。
定義順やa,b,c順かも知れませんし、何らかの内部処理の都合(ハッシュ等)で順番が決まるかも知れません。
あと最適化をかけると、この条件は成立しなくなるので同じ動きをしません。リリースビルドをしてみると分かります。
なので、そんな風に思い込むのは危険です。

【補足】
ためにしVC++2008でリリースビルドしたもの。

コード:

b[-1]=1
b[0]=3
b[1]=5
b[2]=7
b[3]=0
*(px)=(000C301C)=5
*(px-2)=(000C3014)=1
*(px+2)=(000C3024)=0
宿題です。なぜ、こうなるかを推測してみてください。

Re: static で値を0以外に初期化するとバグが起きます

Posted: 2012年5月25日(金) 20:57
by あsdf
ためしに、以下の様なプログラムを書いてみました。

コード:

void func(void)
{
    static int a = 99;
		printf("&a = %p = %d\n", &a, a);
}
 
 void func2(void)
{
    static int c = 1111;
		printf("&c = %p = %d\n", &c, c);
}

int main()
{
    static int b[3] = { 3,5,7 };
    int *px = &b[1];
    int i;
    for( i=-2 ; i<4 ; i++ ) {
        printf( "b[%d]=%d\n", i, b[i] );
    }
		for( i=-2 ; i<4 ; i++ ) {
        printf( "&b[%d]=%p\n", i, &b[i] );
    }

		func();
		func2();

    printf( "*(px)=(%p)=%d\n", px, *(px) );
    printf( "*(px-2)=(%p)=%d\n", px-2, *(px-2) );
    printf( "*(px+2)=(%p)=%d\n", px+2, *(px+2) );
    
}
これをリリースビルドすると、結果は以下のようになりました。
b[-2]=99
b[-1]=1111
b[0]=3
b[1]=5
b[2]=7
b[3]=0
&b[-2]=011B3018
&b[-1]=011B301C
&b[0]=011B3020
&b[1]=011B3024
&b[2]=011B3028
&b[3]=011B302C
&a = 011B3018 = 99
&c = 011B301C = 1111
*(px)=(011B3024)=5
*(px-2)=(011B301C)=111
*(px+2)=(011B302C)=0

さらに、main内のfunc2をコメントアウトすると、
b[-2]=1
b[-1]=99
b[0]=3
b[1]=5
b[2]=7
b[3]=1
となりました。つまり、リリースビルドをすると、使用していない関数のstatic変数は領域に格納されないということでしょうか?

ただ、値がb[-2]=1のように1になる理由はわかりませんでした。同様に、補足のところに挙げていただいたコードのb[3]=0と0になる理由もわかりませんでした。

(静的領域は、0に初期化されるものだから、b[3]も静的領域に存在し、そのために0となったと思ったのですが、上のようにb[3]=1と1が出てきたしまったためにこれは間違っていると考えました。)

Re: static で値を0以外に初期化するとバグが起きます

Posted: 2012年5月25日(金) 22:45
by softya(ソフト屋)
あsdf さんが書きました:となりました。つまり、リリースビルドをすると、使用していない関数のstatic変数は領域に格納されないということでしょうか?
不要な関数はコンパイラが抹消します。なので実行ファイル中には変数どころかfunc2のプログラムコードも残りません。
あsdf さんが書きました:ただ、値がb[-2]=1のように1になる理由はわかりませんでした。同様に、補足のところに挙げていただいたコードのb[3]=0と0になる理由もわかりませんでした。
たまたま、何らかの1に見える値が格納されていたのでしょう。
あるいは、標準ライブラリ関数で使われるstatic変数の値かも知れません。
あsdf さんが書きました:(静的領域は、0に初期化されるものだから、b[3]も静的領域に存在し、そのために0となったと思ったのですが、上のようにb[3]=1と1が出てきたしまったためにこれは間違っていると考えました。
上に書いたのが原因です。自分で書いたものだけでなく標準ライブラリ関数の使う変数も静的領域には含まれています。

Re: static で値を0以外に初期化するとバグが起きます

Posted: 2012年5月27日(日) 01:24
by あsdf
ありがとうございます。

標準ライブラリ関数でstatic変数を使っているものなんてあるのでしょうか?
例えばどんなものがあるのでしょうか?

Re: static で値を0以外に初期化するとバグが起きます

Posted: 2012年5月27日(日) 08:14
by beatle
例えばstrtokはstatic変数を用いて実装されることが主なのではないかと思います。