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

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

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

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Visual Studio 2013

Microsoft Visual Studio 2013は、Microsoftによる統合開発環境(IDE)であり、多種多様なプログラミング言語に対応しています。 Visual Studio 2012の次のバージョンです

Q&A

3回答

10506閲覧

ヒープの崩壊について

matchdas3333

総合スコア12

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Visual Studio 2013

Microsoft Visual Studio 2013は、Microsoftによる統合開発環境(IDE)であり、多種多様なプログラミング言語に対応しています。 Visual Studio 2012の次のバージョンです

0グッド

0クリップ

投稿2016/12/19 16:34

編集2016/12/19 16:36

###前提・実現したいこと
unsigned short型の画像を読み込み、検出フィルター、大津の2値化、白黒反転、ラベリング、ラベルの面積・円形度の計算を経て2値化した画像とラベルを色付けしたものを出力するプログラムを書いているのですが、
roundnessという関数の一つ目のfor文の途中で強制終了してしまいます。
エラー文は以下の通りです。
また、ステップオーバーで1文づつ確認していったところ、そこでエラーは出ず、
2値化した画像を書き込もうとしたときに、
image_write_unchar(outname, w, h, binary);
の中で
「ヒープが壊れています」
とエラーが出て、また*fpを参照すると
「メモリが読み取れません」
と表示されます。
また、freeで配列を解放した時にも同じエラーが出ます。
どこかで配列の扱い方が間違っているのだとは思いますが、どこが間違っているのか私にはわかりません。
お力を貸していただけないでしょうか。
よろしくお願いします。
###発生している問題・エラーメッセージ
①コマンドプロンプトで実行した場合
②ステップオーバー(デバッグモード)で実行した場合

①Unhandled exception at 0x0F5B205E (msvcr120d.dll) in fjt8.exe: 0xC0000005: Access violation reading location 0x40319E42. ②ハンドルされない例外が 0x77769D11 (ntdll.dll) で発生しました(fjt8.exe 内): 0xC0000374: ヒープは壊れています。 (パラメーター: 0x7779D8D0)。

###該当のソースコード

c++

1void removefilter(unsigned char *a, int w, int h){ 2 int i, j, k, l; 3 int *s; //ラベルの面積 4 s = (int *)calloc(labelcnt, sizeof(int *)); 5 6 printf("labelcnt = %d\n", labelcnt); 7 8 for (k = 0; k < labelcnt; k++){ //面積を計算 9 for (i = 0; i < h; i++){ 10 for (j = 0; j < w; j++){ 11 if (a[i*w + j] == k){ //もし注目座標のラベルがkなら 12 s[k]++; 13 } 14 15 } 16 } 17 } 18 s[0] = 0; 19 for (k = 0; k < labelcnt; k++){ //面積の出力 20 printf("ラベル%dの面積 = %d\n", k, s[k]); 21 } 22 printf("面積が極端に小さい、また極端に大きいラベルを削除します(100以下または3000以上)\n"); 23 for (k = 0; k < labelcnt; k++){ 24 if (s[k] < 100 || 3000 < s[k]){ //もし面積が極端に小さい、また極端に大きいなら 25 for (i = 0; i < h; i++){ 26 for (j = 0; j < w; j++){ 27 if (a[i*w + j] == k){ //もし該当するラベルの座標なら 28 a[i*w + j] = 0; //ラベルを取り消す 29 } 30 31 } 32 } 33 } 34 } 35 printf("削除工程を経て残ったものを表示します\n"); 36 initarray(s, labelcnt); 37 for (k = 1; k < labelcnt; k++){ //もう一度面積を計算(残ったもの) 38 for (i = 0; i < h; i++){ 39 for (j = 0; j < w; j++){ 40 if (a[i*w + j] == k){ 41 s[k]++; 42 } 43 44 } 45 } 46 } 47 s[0] = 0; 48 for (k = 0; k < labelcnt; k++){ //改めて面積の出力 49 if (s[k] != 0){ 50 printf("ラベル%dの面積 = %d\n", k, s[k]); 51 } 52 } 53 54 printf("これで面積の値が極端なものを削除することができました\n"); 55 printf("次に、円形度による判定を行います。\n"); 56 57 roundness(a, w, h, s, labelcnt); 58 printf("面積の初期化\n"); 59 initarray(s, labelcnt); 60 printf("面積の再計算\n"); 61 for (k = 0; k < labelcnt; k++){ //もう一度面積を計算(残ったもの) 62 for (i = 0; i < h; i++){ 63 for (j = 0; j < w; j++){ 64 if (a[i*w + j] == k){ 65 s[k]++; 66 } 67 68 } 69 } 70 } 71 s[0] = 0; 72 for (k = 0; k < labelcnt; k++){ //面積の出力 73 if (s[k] != 0){ 74 printf("ラベル%dの面積 = %d\n", k, s[k]); 75 } 76 77 } 78 printf("残ったラベルは以上です。\n"); 79 80 free(s); 81 82} 83void roundness(unsigned char *a, int w, int h, int *s, int labelcnt){ 84 int i, j, k, l; 85 86 double *r; //ラベルの仮想の円の半径 87 r = (double *)calloc(labelcnt, sizeof(double *)); 88 89 printf("円形度を求めるために仮想的な半径を求めます。\n"); 90 for (k = 0; k < labelcnt; k++){ 91 if (s[k] != 0){ 92 r[k] = sqrt((double)s[k] / pi); //r*r*pi = sより 93 printf("%d:r = %lf, s = %d\n", k, r[k],s[k]); 94 } 95 } 96 printf("次に、図形の中心となる座標を求めます。\n"); 97 int max_x = 0; 98 int min_x = 1024; 99 int max_y = 0; 100 int min_y = 1024; 101 int x[100],y[100]; //ラベルの中心の座標 102 initarray(x, 100); 103 initarray(y, 100); 104 105 printf("探索開始\n"); 106 for (k = 0; k < labelcnt; k++){ 107 if (s[k] != 0){ 108 max_x = 0; 109 min_x = 1024; 110 max_y = 0; 111 min_y = 1024; 112 //printf("ラベル%d:面積あり,%d\n", k,s[k]); 113 for (i = 0; i < h; i++){ 114 for (j = 0; j < w; j++){ 115 if (a[i*w + j] == k){ //もし注目座標のラベルがkなら 116 if (j > max_x){ 117 max_x = j; 118 } 119 if (j < min_x){ 120 min_x = j; 121 } 122 123 if (i > max_y){ 124 max_y = i; 125 } 126 if (i < min_y){ 127 min_y = i; 128 } 129 } 130 } 131 } 132 //printf("ラベル%d:探索終了\n", k); 133 x[k] = min_x + (max_x - min_x) / 2; 134 y[k] = min_y + (max_y - min_y) / 2; 135 printf("label %d: (x,y) = (%d,%d)\n", k, x[k], y[k]); //中心座標の出力 136 //終わったら次のラベルへ 137 } 138 } 139 printf("探索終了\n"); 140 141 printf("仮想の円と実際のラベルが一致している面積を求め、円形度を求める\n"); 142 143 int match_s[100]; 144 initarray(match_s, 100); 145 146 for (k = 0; k < labelcnt; k++){ 147 if (s[k] != 0){ 148 for (i = 0; i < h; i++){ 149 for (j = 0; j < w; j++){ 150 if ((double)(abs(j - x[k])*abs(j - x[k]) + abs(i - y[k])*abs(i - y[k])) < r[k] * r[k]){ //もし注目画素の座標が仮想の円の中なら 151 if (a[i*w + j] == k){ //注目画素の座標がラベルを持っている(=一致しているなら) 152 match_s[k]++; 153 } 154 } 155 } 156 } 157 printf("label %dの一致した面積: %d\n 元の面積: %d\n", k, match_s[k], s[k]); 158 } 159 } 160 printf("各ラベルの円形度を表示します。\n"); 161 for (k = 0; k < labelcnt; k++){ 162 if (s[k] != 0){ 163 printf("label %dの円形度: %lf\n", k, (double)match_s[k] / (double)s[k]); 164 } 165 } 166 printf("円形度が0.8以下のものを削除します\n"); 167 for (k = 0; k < labelcnt; k++){ 168 if (((double)match_s[k] / (double)s[k]) < 0.8){ //もし円形度が0.8以下なら 169 for (i = 0; i < h; i++){ 170 for (j = 0; j < w; j++){ 171 if (a[i*w + j] == k){ //もし該当するラベルの座標なら 172 a[i*w + j] = 0; //ラベルを取り消す 173 } 174 175 } 176 } 177 } 178 } 179 printf("円形度の値によるラベルの削除終了\n"); 180 181 free(r); 182 183 printf("free\n"); 184 185 186} 187void image_write_unchar(char *filename, int w, int h, unsigned char *im) 188{ 189 printf("output %s\n", filename); 190 191 FILE *fp; 192 if ((fp = fopen(filename, "wb")) == NULL){ 193 printf("File Open Error Occured!\n"); 194 exit(-1); 195 } 196 fwrite((unsigned char *)im, sizeof(unsigned char), w * h, fp); fclose(fp); 197 198 printf("output completed\n"); 199}

###試したこと
配列を宣言するときにmallocとcalloc、静的確保と動的確保、両方試してみました。

###補足情報(言語/FW/ツール等のバージョンなど)
そこのプログラムを実行すると変数:labelcntは88になります。
また上に乗せたソースはエラーが発生した関数のみとなります。

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

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

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

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

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

guest

回答3

0

VSは不勉強でよく分かってないのですが、デバッガでスタックトレースできないですか?落ちたところからスタックを見ればエラー箇所が分かると思います。
Chironianさんも書かれていますが、sizeof(~*)はポインタのサイズ(32ビット環境ではたいてい4バイト)です。従ってsizeof(int*)はたまたま同じサイズで問題は起こらないと思いますが、修正をした方がいいと思います。また、重箱の隅をつつくようですが100以下といった場合≦100となります。(提示されたコードでは100より小さいが正解と思います、)3000も同様に3000より大きいが正解です。

投稿2016/12/22 12:59

cateye

総合スコア6851

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

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

0

labelcnt の値はいくつでしょう?
ざっと眺めた感じでは

c

1int x[100],y[100]; //ラベルの中心の座標 2int match_s[100]; 3 4```としているので labelcnt が 100 以上の値になると破壊されませんか?

投稿2016/12/20 00:29

HiroshiWatanabe

総合スコア2160

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

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

0

こんにちは。

r = (double *)calloc(labelcnt, sizeof(double *));

もし、32bitビルドしているのであれば、sizeof(double*)は4です。
しかし、doubleは通常は8バイトですので、領域が足りていないと思います。
double型の領域をlabelcnt分獲得するのですから、sizeof(double)が正しいと思います。

ところで、コードを見る限りC言語ですが、C++のタグで正しいのでしょうか?
C++を使うのでしたら、callocよりはnewを使った方がこのようなミスは発生しにくいと思いますよ。

投稿2016/12/19 18:55

Chironian

総合スコア23272

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

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

matchdas3333

2016/12/19 19:21

回答ありがとうございます。 おっしゃる通りだとは思いますが、変更して実行してみたところまた強制終了してしまいました… C++であっています。newですね、アドバイス、ありがとうございます。
Chironian

2016/12/19 19:32

なるほど、他にもまだ不明メモリ・アクセスしているところがあるということですね。 配列に値を設定しているところを、暫定的にコメント・アウトしてみて落ちるかどうか確認してみてはどうでしょうか? 異常動作しない程度の初期化はしておかないといけませんので、初期化部は要注意です。 その結果、落ちなくなったら、コメントアウトしたどれかが原因ですので、前の方から1つ1つもどして行けば原因に辿り着くと思います。
yohhoy

2016/12/22 07:58 編集

C++を使うなら std::vector や std::unique_ptr でメモリリソース管理をすべきかと思います。cmalloc/freeをnew/deleteに置き換えただけでは何も解決しませんし... 追記:言われてみれば malloc -> newでsizeof(型名)のミスは減りますね。とはいえ最大限newは避けた方がいいですけど。
Chironian

2016/12/22 07:54

ああ、これはsizeof(double*)でdouble用メモリ獲得するミスをし難くなることを狙った提案でした。 でも、折角C++を使うのであれば、new/deleteを使わないで済む時は使わない方が良いですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問