C言語関係掲示板

過去ログ

No.1242 動的配列がfreeされない

[戻る] [ホームページ]
No.2388

関数内での動的配列について
投稿者---初心者(2004/07/27 01:22:33)


初心者です。
科学技術計算プログラムをCで作成中なのですが、反復アルゴリズム中何度も呼び出す必要がある関数内で、動的配列を定義しています。例えば下のようなプログラムでは、配列aはauto宣言しているので、関数終了時にメモリ解放が行われて、無限ループのプログラムになるだろうと思いきや、コメントのfreeを有効にしても配列は破棄されないようで、メモリオーバーとなり異常終了してしまいます。何が問題なのかよくわかりません。どなたかご教授いただけないでしょうか。なにとぞよろしくお願い申し上げます。ちなみに環境は、Windows上でBorland C++ Compiler 5.5を用いています。

#include <stdio.h>
#include <stdlib.h>

void alloc(int i)
{
  auto double *a;
  int n;

  printf("%dMB\n", i);

  a = (double *) malloc(1048575);
  if(a == NULL){
    printf("allocation error\n");
    exit(0);
  }

//  free(a);

}

void main()
{
  int i;

  for(i = 0;;i++){
    alloc(i);
  }

}




No.2389

Re:関数内での動的配列について
投稿者---円零(2004/07/27 03:07:49)


おかしいですね。私の環境で実行したところ、freeが有効になってれば
オーバーフローする気配は全然なかったですよ("105685MB"まで実行)。

それと、"auto"はこの際あまり関係ないような。
mallocで確保した領域を示すポインタ変数aが自動変数になるだけですから、
freeせずに関数を抜けたら、そこにアクセスする手段がなくなるだけで
割り当てられたメモリブロック自体はそのまま残ってしまいます。
また、ローカル変数は特に宣言しない限り暗黙に自動変数になりますから、
基本的に"auto"は書く必要がないものであると思われます。


No.2392

Re:関数内での動的配列について
投稿者---shu(2004/07/27 15:16:34)


MB(メガバイト)のメモリーの確保なら、
105685ではなくて、105686(1024 * 1024)じゃないでしょうか?
105686にしたところ、うまく動作しました。


No.2395

Re:関数内での動的配列について
投稿者---初心者(2004/07/27 18:15:05)


shu様

ご助言ありがとうございます。
ご指摘の通り、1024*1024=1048576としてメモリー確保を行うと問題なく動作いたしました。
しかしながら、1048575でも1048576でも、メモリー確保を行うという意味では
大して変わらない操作をやっているように思えるのですが、
なぜ1048575だとうまくいかないのですか?ご教授いただけたら幸いです。



No.2396

Re:関数内での動的配列について
投稿者---shu(2004/07/27 19:17:07)


>なぜ1048575だとうまくいかないのですか?ご教授いただけたら幸いです。

(double *)でキャストされているからでしょう。
sizeof(double)が、8(バイト)なので、1048575だと割りきれません。
確保したいサイズとと実際に確保されたサイズに、
少しずつ誤差が生じていると思われます。


No.2394

Re:関数内での動的配列について
投稿者---初心者(2004/07/27 18:10:52)


円零様

早速のご助言ありがとうございました。

>mallocで確保した領域を示すポインタ変数aが自動変数になるだけですから、
>freeせずに関数を抜けたら、そこにアクセスする手段がなくなるだけで
>割り当てられたメモリブロック自体はそのまま残ってしまいます。

ポインタ変数aが自動変数であれば、関数が終了した時点でmallocで確保した
領域全てが自動的に解放されるものだと考えておりましたが、
そうではなくてfreeを用いる必要があったのですね。

ちなみにauto宣言は、質問内容を明確にしたかったために、あえて使ったものです。



No.2397

Re:関数内での動的配列について
投稿者---RAPT(2004/07/27 21:32:13)


>ポインタ変数aが自動変数であれば、関数が終了した時点でmallocで確保した
>領域全てが自動的に解放されるものだと考えておりましたが、
>そうではなくてfreeを用いる必要があったのですね。

「ポインタ変数」そのものは、開放されますが、その指し示す先が
開放される訳ではありません。
# そうでないと fopen() 等、軒並み使えない。


No.2399

Re:関数内での動的配列について
投稿者---かずま(2004/07/28 02:48:52)


    size_t n;  char *p, *q;

    for (n = 1048000; n <= 1050000; n++) {
        p = malloc(n);
        if (p == NULL) break;
        free(p);
        q = malloc(n);
        free(q);
        if (p != q) printf("p=%p, q=%p, n=%u (%#x)\n", p, q, n, n);
    }

このプログラムを走らせると、n = 1048573, 1048574, 1048575 のときのみ、通
常とは異なる結果になることがわかります。たいていの場合、free すると、その
領域がもう一度 malloc で使用されるため、同じアドレスが返されるはずです。

次に、
    for (n = 1024; ; n <<= 1) {
        p = malloc(n);
        if (p == NULL) break;
        printf("%p: %u\n", p, n);
        free(p);
    }

を試してみると、n が 1048576未満と、1048576以上とで返って来るアドレスが
異なることがわかります。

つまり、BCC の malloc は 1MBを境にメモリの割り付け方を変えているようです。
また、返されるアドレスは常に 4の倍数です。

ここからは推論ですが、1048573〜1048575 の領域を malloc するとき、これは
1MB未満なので、その領域に割り付けられます。ところが、実際にはサイズを
4の倍数にするため、1048576バイト割り付けたと記録されます。

free するとき、サイズが 1MBなので、1MB以上の領域を解放しようとして、
失敗しているものと思われます。

では、なぜこのバグが今まで問題にならなかったかというと、小さいサイズなら
ともかく、1MB ぐらいの領域を奇数バイト割り付けようとすることは普通しない
からではないでしょうか。



No.2400

Re:関数内での動的配列について
投稿者---shu(2004/07/28 08:08:08)


過去ログにこんな内容がありました。

http://f1.aaa.livedoor.jp/~pointc/log955.html


No.2403

Re:関数内での動的配列について
投稿者---シャノン(2004/07/28 10:48:04)


> また、返されるアドレスは常に 4の倍数です。
> ここからは推論ですが、1048573〜1048575 の領域を malloc するとき、
> これは1MB未満なので、その領域に割り付けられます。ところが、実際に
> はサイズを4の倍数にするため、1048576バイト割り付けたと記録されま
> す。
>
> free するとき、サイズが 1MBなので、1MB以上の領域を解放しようとし
> て、失敗しているものと思われます。

ひょっとしたら BCC にはそのようなバグがある可能性も否定できませんが、そうではないと思われます。

まず、malloc は要求したサイズぴったりのメモリを確保するとは限りません。
確保に成功すれば、最低でも要求したサイズは確保されますが、ひょっとしたらそれより大きいサイズを確保しているかもしれません(規格には定められていないようですが、少なくとも VC++ はそのような実装をしています。BCC はきっちり確保しますと BCC のドキュメントに明記されていたらごめんなさい)。
また、返されるアドレスが4バイト境界だからといって、確保されるサイズも4の倍数とは限りません。
仮に4の倍数バイトが確保されるとしても、前述の理由により、しっかり4の倍数バイト確保されたと記録されるはずです。
「滅多に無いだろうから」でバグを見逃すようなことは許されません。


No.2406

Re:関数内での動的配列について
投稿者---かずま(2004/07/28 11:55:03)


>> free するとき、サイズが 1MBなので、1MB以上の領域を解放しようとし
>> て、失敗しているものと思われます。

> ひょっとしたら BCC にはそのようなバグがある可能性も否定できませんが、
> そうではないと思われます。

今、読み返したら、誤解を与える文章なのに気づきました。次のように
読み替えてください。

  free するとき、サイズが 1MBなので、「1MB以上を確保する領域」から
  1MBを解放しようとして、失敗しているものと思われます。


もっと誤解のないように説明してみます。

・1MB未満のメモリは領域Aから確保される。
・1MB以上のメモリは領域Bから確保される。
・指定されたサイズは、4の倍数に切り上げられる。

1048573〜1048575バイトの場合、1MB未満なので領域Aから確保される。
ただし、確保されたサイズは 1048576(1MB)と記録される。

確保した領域を free しようとしたとき、サイズが 1MB なので、領域B の
解放処理が行われようとする。

しかし、free に渡されたポインタは領域Aを指しているので失敗する。


> まず、malloc は要求したサイズぴったりのメモリを確保するとは限りません。
> 確保に成功すれば、最低でも要求したサイズは確保されますが、ひょっとし
> たらそれより大きいサイズを確保しているかもしれません(規格には定めら
> れていないようですが、少なくとも VC++ はそのような実装をしています。
> BCC はきっちり確保しますと BCC のドキュメントに明記されていたらごめ
> んなさい)。

私は、BCC の malloc が要求したサイズぴったりのメモリを確保するとは
言っていないので、シャノンさんの上の意見の真意が理解できません。


> また、返されるアドレスが4バイト境界だからといって、確保されるサイズ
> も4の倍数とは限りません。
> 仮に4の倍数バイトが確保されるとしても、前述の理由により、しっかり4
> の倍数バイト確保されたと記録されるはずです。

実は、書かなかったので申し訳ないのですが、確保された領域のひとつ前の
4バイトにそのサイズが書き込まれるようです。その値は、4の倍数です。
ただし、1〜11バイトは 12バイトになります。

    size_t n;  long *p;

    for (n = 1; n < 32; n++) {
        p = malloc(n);
        printf("p[-1]=%d, n=%d\n", p[-1], n);
        free(p);
    }


> 「滅多に無いだろうから」でバグを見逃すようなことは許されません。
 
私は、バグを見逃すことを許しているのではありません。
見逃された原因を推論しているだけです。



No.2407

Re:関数内での動的配列について
投稿者---シャノン(2004/07/28 13:29:48)


うーん…
ごめんなさい。

> まず、malloc は要求したサイズぴったりのメモリを確保するとは限りません。
> 確保に成功すれば、最低でも要求したサイズは確保されますが、ひょっとし
> たらそれより大きいサイズを確保しているかもしれません(規格には定めら
> れていないようですが、少なくとも VC++ はそのような実装をしています。
> BCC はきっちり確保しますと BCC のドキュメントに明記されていたらごめ
> んなさい)。
> また、返されるアドレスが4バイト境界だからといって、確保されるサイズ
> も4の倍数とは限りません。
> 仮に4の倍数バイトが確保されるとしても、前述の理由により、しっかり4
> の倍数バイト確保されたと記録されるはずです。

何を考えてこんなことを書いたのか、読み返しても思い出せません(汗
なんだか、「要求したバイト数よりも多く確保されたけど、要求したサイズしか解放してないなんてケースはないから大丈夫」って言ってるように聞こえてきました。
問題を履き違えてたんでしょうか…


No.2408

Re:関数内での動的配列について
投稿者---シャノン(2004/07/28 13:36:03)


>・1MB未満のメモリは領域Aから確保される。
>・1MB以上のメモリは領域Bから確保される。
>・指定されたサイズは、4の倍数に切り上げられる。
>
> 1048573〜1048575バイトの場合、1MB未満なので領域Aから確保される。
> ただし、確保されたサイズは 1048576(1MB)と記録される。
>
> 確保した領域を free しようとしたとき、サイズが 1MB なので、領域B
> の解放処理が行われようとする。
>
> しかし、free に渡されたポインタは領域Aを指しているので失敗する。

これを読み取れていなかったために、
単に 1048573〜1048575 バイトしか確保していないのに 1048576 バイト解放しようとして落ちている、と解釈していました。
#まぁ、大差ない話ではありますが。

もしもおっしゃられたような処理が行われているとすると、おかしな話になります。
実装の詳細を知らないので、何も断言はできないのですが…
free に渡されたポインタは領域 A を指しているのに、領域 B の解放処理をしようとするなんて、そんな馬鹿な話があるでしょうか?
確保する際に、サイズに応じて領域を決めているのなら、ライブラリは領域 A および B の開始位置・終了位置くらいは把握していなければなりません。
ならば、free に渡されたポインタがどちらの領域を指しているかくらい、サイズを見る前に確かめると思います。

また、そのようなケースは十分に想定できますから、何らかの対応策を行っていて当然です。
この話で言えば、1048573 バイトしか確保していないのに 1048576 バイト確保したと記録するなどということは、まずありえないと思います。
1048576 バイトと記録するからには 1048576 バイト確保しているはずです。
でなければ、実際に何バイト解放したらいいのかわかりません。

>> 「滅多に無いだろうから」でバグを見逃すようなことは許されません。

> 私は、バグを見逃すことを許しているのではありません。
> 見逃された原因を推論しているだけです。

サイズに応じて領域を分けるような処理を行っているのであれば、本当に分けられているかどうかテストを行っているはずです。
ちゃんと、別の領域から確保されているかどうか。解放の際は正しい領域に返しているかどうか。領域を分ける境界値の前後の値でテストをしています。
大きなサイズで奇数値のサイズを確保するようなことは滅多にないから見逃されたのではありません。見逃さないように、大きなサイズの奇数値でのテストもしています。

何が言いたいかといえば、原因はそんなバグじゃないだろうさ、ということです。
断言はできませんが、断言してもいいくらいの自信はあります。


No.2409

Re:関数内での動的配列について
投稿者---円零(2004/07/28 16:29:21)


>大きなサイズで奇数値のサイズを確保するようなことは滅多にないから見逃されたのではありません。
>見逃さないように、大きなサイズの奇数値でのテストもしています。

それははバグがあるかどうかわからない時に言うセリフでしょう。
境界値付近でテストをするのが重要なのは事実でしょうけど、
現に境界値付近での振る舞いに異常が出てる以上、境界値に関わるバグが見逃された結果だと考えるのが普通じゃありませんか?

自分の環境がbccじゃないのがもどかしい。
因みに、何周目くらいでメモリオーバーするんですか?


No.2410

Re:関数内での動的配列について
投稿者---ニタチ(2004/07/28 16:46:56)


>自分の環境がbccじゃないのがもどかしい。
>因みに、何周目くらいでメモリオーバーするんですか?

 Borland C++Builder 6です。

 0MB
  :
  :
  :
 128MB
 と、128MBまで表示してエラーになります。


No.2412

Re:関数内での動的配列について
投稿者---円零(2004/07/28 17:59:51)


>128MBまで表示してエラーになります。
ありがとうございます。
としますと、1048573〜1048575バイトの場合、切り上げて1MBだから、ということで
領域Aではなく領域Bの中に確保されているのではないかと思います。
しかし解放する時は、領域Aを参照するのか領域Bを参照するのかは単純にmallocで要求されたバイト数で判断するので、
領域Aの中の同じ相対位置の場所を解放しようとして失敗すると。

それとも、容量を間違えてるんですかね?
仮に、freeする時のために確保した容量を記録しておく実装だとして、領域Aに確保した時とBに確保した時で記録の仕方が違う。
Aの場合1048575バイトまでしか記録できないので、オーバーフローして「0バイト確保した」と記録される、
みたいな。


No.2414

Re:関数内での動的配列について
投稿者---REE(2004/07/28 18:37:47)


>仮に、freeする時のために確保した容量を記録しておく実装だとして、領域Aに確保した時とBに確保した時で記録の仕方が違う。

これが怪しいですね
以下が、直前の4wordをlongで見た時の値です。

p[-1]=ffffc, n=1048570
p[-1]=ffffc, n=1048571
p[-1]=ffffc, n=1048572
p[-1]=100000, n=1048573
p[-1]=100000, n=1048574
p[-1]=100000, n=1048575
p[-1]=100ffc, n=1048576
p[-1]=100ffc, n=1048577
p[-1]=100ffc, n=1048578
・・・
p[-1]=100ffc, n=1052667
p[-1]=100ffc, n=1052668
p[-1]=101ffc, n=1052669
p[-1]=101ffc, n=1052670


No.2425

Re:関数内での動的配列について
投稿者---REE(2004/07/29 15:50:44)


C++Builderに添付されていたRTLのソースを解読してみました。
大方の推測は当たっていたようです。
※細かい部分は端折っています。

メモリ確保時、サイズが1Mを超えているかをチェック
 1Mを超えている場合・・
   サイズに4を足した後4kbyte単位にアライメントして、直接VirtualAllocを呼び出す。
 1M未満の場合・・・
   サイズが管理構造体サイズ未満の場合は管理構造体サイズ(=12)にする。
   それ以外の場合は、4byte単位にアライメントする。
   そのサイズ+4以上をVirtualAllocで確保した大きなブロックから割り当てる。
 いずれの場合も確保したサイズ-4を確保した領域の先頭に格納する。(mallocの戻り値は+4されている)

メモリ開放時、サイズが1Mを超えているかをチェック
 1Mを超えている場合・・
   直接VirtualFreeを呼び出す。
 1M未満の場合・・・
   管理構造体として、空きリストに登録する。

ということで、1048573〜1048575の場合は、確保と開放の手順不一致のため、
メモリの開放に失敗するという裏づけが取れました。

試しに、以下の様に記述しても例外が発生しませんでした。
void *p=malloc(1048575);
free(p);
free(p);
free(p);



No.2415

Re:関数内での動的配列について
投稿者---シャノン(2004/07/28 18:57:48)


>境界値付近でテストをするのが重要なのは事実でしょうけど、
>現に境界値付近での振る舞いに異常が出てる以上、境界値に関わるバグが見逃された結果だと考えるのが普通じゃありませんか?

普通なんですか。
そんな処理系、怖くてとても使えません。
こんな誰でも思いつくようなテストをすっぽかした結果のバグが見逃されていると考えるくらいなら、何かはわからないけど何か他のバグがあると考えます。

現にバグが出ているとかのレスは結構です。
現に出ているなら、それはその通りで、俺が BCC 開発陣を買いかぶっていただけの話なのでしょうから。

「偉そうなことを言っているが、じゃあお前は一切バグを出さないのか!?」
いいえ、絶対にバグを出さないのがほぼ不可能といわれているのは周知の通りでしょう。
ただ、境界値チェックのような当然すべきテストを見逃すのがいけないというだけの話です。


No.2417

Re:関数内での動的配列について
投稿者---nm(2004/07/29 01:12:39)


>こんな誰でも思いつくようなテストをすっぽかした結果のバグが見逃されていると考えるくらいなら、何かはわからないけど何か他のバグがあると考えます。

誰でも思いつくと言っておられますが、そうでもないと思いますよ。
ただの境界値ではありません。
境界値+128Mまで実行しなければならないわけです。
# 真の再現条件はわからないけど、今のところは上記条件か?

はっきり言って、処理系だって他のソフトと変わりません。
下手したら、入ったばかりの新人さんがいじった箇所がないとも限りませんし。
OSだって処理系だって、しょっちゅうパッチが出てバグフィックスされてます。
つまり、しょーもないバグを作り込む可能性はいくらでもあるかと。

>現にバグが出ているとかのレスは結構です。
(中略)
>いいえ、絶対にバグを出さないのがほぼ不可能といわれているのは周知の通りでしょう。
>ただ、境界値チェックのような当然すべきテストを見逃すのがいけないというだけの話です。

で、この部分で言いたいこと(今回の議論に関連すること)がよくわかりませんね。
少なくとも、REEさんの示した結果により、可能性があることが示されたのに
そこに目をつぶって可能性を捨て去るのは、建設的な不具合調査方法とは思えません。


No.2419

Re:関数内での動的配列について
投稿者---シャノン(2004/07/29 02:07:14)


>>こんな誰でも思いつくようなテストをすっぽかした結果のバグが見逃されていると考えるくらいなら、何かはわからないけど何か他のバグがあると考えます。
>
>誰でも思いつくと言っておられますが、そうでもないと思いますよ。
>ただの境界値ではありません。
>境界値+128Mまで実行しなければならないわけです。
># 真の再現条件はわからないけど、今のところは上記条件か?

よくわかりません。何ですか 128MB って。
ニタチさんが実験して、128MB 確保したところで落ちた、ですか?
今回の 1MB 境界問題とは関係ない話じゃないですか?

>はっきり言って、処理系だって他のソフトと変わりません。
>下手したら、入ったばかりの新人さんがいじった箇所がないとも限りませんし。
>OSだって処理系だって、しょっちゅうパッチが出てバグフィックスされてます。
>つまり、しょーもないバグを作り込む可能性はいくらでもあるかと。

入ったばかりの新人さんが作るのは別に構いませんよ。
俺だって入社4ヶ月目、実務2ヶ月目ですが、納品するソフト作ってますし。
しょーもないバグ作りこんでるところだってあるでしょう。
でも、作って終わりじゃないでしょう?
作った担当者がデバッグするのはもちろんのこと、その後で他の人がテストするでしょう。本人のデバッグよりずっと厳密に。

ましてや、言語処理系です。
バグがあったらバグってるソフトだけが落ちるんじゃ済まないんです。
なればこそ、テストは厳密なものが要求されます。
そこでも境界値チェックを見逃しているなんて信じられません。

>少なくとも、REEさんの示した結果により、可能性があることが示されたのに
>そこに目をつぶって可能性を捨て去るのは、建設的な不具合調査方法とは思えません。

いいです、非建設的で。
俺は BCC 使いませんから。

#あぁ、フレーミング楽しー
#でも、掲示板にもスレ主にも、真面目に原因探ってる方にも迷惑になるので(俺がな)、これ以上この横枝を伸ばさないようにお願い申し上げます。



No.2420

Re:関数内での動的配列について
投稿者---塗り壁(2004/07/29 02:58:21)


シャノンさん、頑張って。私に言わせれば、
シャノンさんが一番まともです。

ところで、バグ云々と行っていますが、何がバグなんですか。
よく分かるように教えて下さい。  > all


No.2421

Re:関数内での動的配列について
投稿者---isshi(2004/07/29 09:36:18)


>ところで、バグ云々と行っていますが、何がバグなんですか。
最初に書いてあります。
ソースを見る限り何も問題ないのに、現実にエラーが起きるのです。
コンパイラのバグとしか思えないのです。

BCB6で試したところ、free を入れた場合、"4MB"と表示したところで
アクセス違反で終了しました。
私のところでは、malloc でアクセス違反となりました。
1048576 にしたら問題ありませんでした。
ちなみに char* にキャストしても同じでした。
さらに、new, delete に置き換えても同じでした。

VC6でも試しましたがこちらは 1048575 でも無問題です。

実験に使ったソースと実行結果です。最初のソースとほとんど同じです。
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>

void alloc(int i)
{
  double *a;

  printf("%dMB ", i);

  a = (double *) malloc(1048575);
  if(a == NULL){
    printf("allocation error\n");
    exit(0);
  }
  printf("malloc ");
  printf("a = %p ", a);

  free(a);
  printf("free\n");
}

int main(int argc, char* argv[])
{
  int i;

  for(i = 0; i < 10; i++){
    alloc(i);
  }
  getch();
  return 0;
}

実行結果
0MB malloc a = 00C74DC4 free
1MB malloc a = 00D74DC8 free
2MB malloc a = 00E74DCC free
3MB malloc a = 0117009C free
4MB
ここでアクセス違反

残念ながら時間がなくてこれ以上調査できません。


No.2423

Re:関数内での動的配列について
投稿者---あかま(2004/07/29 10:32:12)


>ところで、バグ云々と行っていますが、何がバグなんですか。
>よく分かるように教えて下さい。  > all
freeするだけで落ちる。

ここからシャノンさんへレス
>作った担当者がデバッグするのはもちろんのこと、その後で他の人がテストするでしょう。本人のデバッグよりずっと厳密に。
これで全てのバグが取れれば問題ないのですけどね。なかなかそうもいかないわけで。

>ましてや、言語処理系です。
>バグがあったらバグってるソフトだけが落ちるんじゃ済まないんです。
>なればこそ、テストは厳密なものが要求されます。
>そこでも境界値チェックを見逃しているなんて信じられません。
信じる信じないは問題とは関係のない話で。言語処理系のバグなんてよく聞きますよ。
「コンパイラのバグ」でググってみてはいかがでしょう。

プログラムに問題がなく、BCCで落ちて、VC6で落ちないというのならコンパイラに問題があるのは明らかです。
もちろんプログラムに問題がないなんて言い切れないので、そちらにバグが潜んでいるかもしれませんが、
No.2421 isshiさんのプログラムはバグの入れようがないほど簡潔でしょう。
ちなみにワタシの環境では128Mのところで落ちました。
freeを削ると1000M確保しても落ちない。面白いなこれ。

んー、前にscanfでコンパイラによって挙動が変わったことがあったんだけど記事が出てこない。無念。




No.2424

Re:関数内での動的配列について
投稿者---かずま(2004/07/29 11:57:15)


> んー、前にscanfでコンパイラによって挙動が変わったことがあったんだけど
> 記事が出てこない。無念。
私が知っている BCC の scanf のバグ。

	int n, i = 2, j = 3;

	n = sscanf("123 456 789", "%*[a-z] %d %d", &i, &j);
	printf("n=%d, i=%d, j=%d\n", n, i, j);

%*[a-z] は、1文字以上の英字列を読み飛ばすという書式です。
先頭の "1" は英字ではありませんから、sscanf はそこでスキャンを打ち切り
n には 0 が返るはずです。実際、VC++、gcc、LSI C-86 では、出力が、

    n=0, i=2, j=3

となりますが、BCC だけは、

    n=2, i=123, j=456

となります。



No.2438

Re:関数内での動的配列について
投稿者---Sciggepy(2004/08/03 14:11:25)


>プログラムに問題がなく、BCCで落ちて、VC6で落ちないというのならコンパイラに問題があるのは明らかです。
> んー、前にscanfでコンパイラによって挙動が変わったことがあったんだけど
一応つけ足しておきますが、ここで語られているのはコンパイラのバグに関してではなく、RTLのバグに関してです。Windows上のVC++とgccでは、RTLが同じなので、ライブラリ関数の振る舞いは同じになります。

ところで、ここまで話を引き伸ばす必要があったのでしょうか。少なくとも、Windows上で目的の動作を実行するには、HeapAllocで十分だと思います。怪しいものは使わなければよい、ということで。(最近のVCのRTLでは、HeapAllocを使っているらしい。)


No.2439

Re:関数内での動的配列について
投稿者---円零(2004/08/03 14:36:47)


>ところで、ここまで話を引き伸ばす必要があったのでしょうか。
そう言いながら自ら話を引き伸ばしてるようですが…

RTLってなんですか?


No.2440

Re:関数内での動的配列について
投稿者---Sciggepy(2004/08/03 15:19:16)


>そう言いながら自ら話を引き伸ばしてるようですが…
尤も。

>RTLってなんですか?
RunTime Library