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

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

ただいまの
回答率

87.34%

ヒープは壊れていますと表示される

受付中

回答 4

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,265

score -8

下記プログラムにてヒープは壊れていますとエラーが出ます。
(修正:現在は「読み取り中にアクセス違反が発生」と表示されます)
配列xをS_fftのakに渡すときにサイズを超えている(?)のが原因だと思うのですが、
対処法が分かりません。ヒントをください。
エラー箇所はmain関数内の三番目のfor文の中です。

エラー内容

ハンドルされない例外が 0x77BF0F01 (ntdll.dll) で発生しました(WAVFFT02.exe 内): 0xC0000005: 場所 0x000010A5 の読み取り中にアクセス違反が発生しました。

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#pragma warning(disable:4996)

typedef struct

{

    int fs; //サンプリング周波数

    int bits; //量子化bit数

    int L; //データ長

} WAV_PRM;

#define   PI  4.0*atan(1.0)  // 円周率

void S_fft(double* x, double* y, int, int);
#define N 4096  // N=データ総数(2,4,8,16,32,64,128,256,512,1024 のどれか)

double* audio_read(WAV_PRM* prm, char* filename);

double* audiodatasize;

int main(void) {

    double* x, * y;
    x = (double*)malloc(sizeof(double) * N);
    y = (double*)malloc(sizeof(double) * N);

    FILE* fp = fopen("fft.txt", "w");              // ファイル入出力

    WAV_PRM prm_in;
    double* data;
    //FILE* audio = fopen("sinwave.wav", "rb");
    FILE* txt = fopen("mansample.txt", "w");
    char fn[] = "mansample.wav";

    int i, Fr = 4096;
    const double dt = 0.00005;
    double sc;


    double fq;
    int pw = 0;
    printf("STG 01\n");
    sc = 2.0 * PI * dt;
    for (i = 0; i < N; i++) {
        if (x != NULL && y != NULL) {
            // x = audio_read(&prm_in, fn);
            y[i] = 0.0;
        }
    }
    x = audio_read(&prm_in, fn);
    printf("STG 02\n");
    //  Fourier変換        

    printf("Size of Data : %d\n", sizeof *x);

    S_fft(x, y, N, -1);

    printf("STG 03\n");

    //for (i = 0; i < Fr; i++) printf(" i=%d %f %f \n", i, x[i], y[i]);

    //    計算結果をファイルに格納

    for (i = 0; i < N / 2; i++) {
        if (x != NULL && y != NULL) {
            pw = sqrt(x[i] * x[i] + y[i] * y[i]) * 100; // パワースペクトル
        }
        fq = i / (dt * Fr) * 2.2;   //  周波数
        //printf("%f = %d / (%f * %d)\n", fq, i, dt, N);
        fprintf(txt, "%fHz  %d\n", fq, pw); // エラー箇所
    }
    printf("STG 04\n");
    //fclose(fp);
    //fclose(audio);
    fclose(txt);
    free(x);
    free(y);
    return 0;
}

/*********  Fourier変換および逆Fourier変換を行う副関数  ***************/
/*   入力:  x[n]: 被解析波形(振幅)  n; 離散データの数               */
/*          ff:   Fourier変換の場合は -1, 逆Fourier変換の場合は 1     */
/*   出力: ak[n]:cos関数の係数、 bk[n]:sin関数の係数                 */
/**********************************************************************/
void S_fft(double ak[], double bk[], int n, int ff) {
    int i, j, k, k1, num, nhalf, phi, phi0;
    static int rot[N];
    double s, sc, c, a0, b0, tmp;

    for (i = 0; i < n; i++) rot[i] = 0;

    //ak = (double*)malloc(sizeof(double) * N);
    //bk = (double*)malloc(sizeof(double) * N);

    nhalf = n / 2; num = n / 2; sc = 2.0 * PI / n;
    while (num >= 1) {
        for (j = 0; j < n; j += 2 * num) {
            phi = rot[j] / 2;    phi0 = phi + nhalf;
            c = cos(sc * phi);   s = sin(sc * phi * ff);
            for (k = j; k < j + num; k++) {
                k1 = k + num;
                if (ak != NULL && bk != NULL) {
                    a0 = ak[k1] * c - bk[k1] * s;
                    b0 = ak[k1] * s + bk[k1] * c;
                    ak[k1] = ak[k] - a0;      bk[k1] = bk[k] - b0;
                    ak[k] = ak[k] + a0;       bk[k] = bk[k] + b0;
                }
                rot[k] = phi;           rot[k1] = phi0;
            }
        }
        num = num / 2;
    }
    if (ff < 0 && ak != NULL && bk != NULL) {
        for (i = 0; i < n; i++) {
            ak[i] /= n;  bk[i] /= n;
        }
    }

    for (i = 0; i < n - 1; i++) {
        if ((j = rot[i]) > i && ak != NULL && bk != NULL) {
            tmp = ak[i]; ak[i] = ak[j]; ak[j] = tmp;
            tmp = bk[i]; bk[i] = bk[j]; bk[j] = tmp;
        }
    }
}



double* audio_read(WAV_PRM* prm, char* filename)

{

    //変数宣言

    FILE* fp;

    int n;

    double* data;

    char header_ID[4];

    long header_size;

    char header_type[4];

    char fmt_ID[4];

    long fmt_size;

    short fmt_format;

    short fmt_channel;

    long fmt_samples_per_sec;

    long fmt_bytes_per_sec;

    short fmt_block_size;

    short fmt_bits_per_sample;

    char data_ID[4];

    long data_size;

    short data_data;



    //wavファイルオープン

    fp = fopen(filename, "rb");



    //wavデータ読み込み

    fread(header_ID, 1, 4, fp);

    fread(&header_size, 4, 1, fp);

    fread(header_type, 1, 4, fp);

    fread(fmt_ID, 1, 4, fp);

    fread(&fmt_size, 4, 1, fp);

    fread(&fmt_format, 2, 1, fp);

    fread(&fmt_channel, 2, 1, fp);

    fread(&fmt_samples_per_sec, 4, 1, fp);

    fread(&fmt_bytes_per_sec, 4, 1, fp);

    fread(&fmt_block_size, 2, 1, fp);

    fread(&fmt_bits_per_sample, 2, 1, fp);

    fread(data_ID, 1, 4, fp);

    //fread(&data_size, 4, 1, fp);
    fread(&data_size, sizeof(unsigned char), 4, fp);



    //パラメータ代入

    prm->fs = fmt_samples_per_sec;

    prm->bits = fmt_bits_per_sample;

    prm->L = data_size / 2;



    //音声データ代入

    data = (double*)calloc(prm->L, sizeof(double));
    audiodatasize = (double*)malloc(sizeof(double) * prm->L);

    for (n = 0; n < prm->L; n++) {

        fread(&data_data, 2, 1, fp);

        if (data != NULL) {
            data[n] = (double)data_data / 32768.0;
        }

    }



    fclose(fp);

    return data;

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

0

どこかでアクセス違反を起こして、ヒープメモリ領域を破壊しています。
デバッグ環境を整え、どこでアクセス違反を起こしてるかを探してみてください


C言語のコードを書くなら、デバッグできる環境を整えましょう。
Eclipseや、WindowsならVisualStudioなど。
コードの任意の場所で実行を止め、変数のナカミを見ることができます。そこから1行づつ実行して、コードの流れを見れるようになります
そうすれば、アテズッポでコードを書かなくて済むようになります。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2021/02/28 21:27

    ざっとみただけですが、
    > x = audio_read(&prm_in, fn);

    prm_inは初期化されてませんね

    キャンセル

  • 2021/02/28 22:37

    prm_inの初期化は必要でしょうか…?
    とあるサイトを参考に(というか関数をお借りして)作ったコードなのですが、
    そのサイトでは特に初期化等はしていなかったようなのでそのまま使用しています。

    キャンセル

  • 2021/02/28 22:44

    そこらへん、ステップ実行でどうなるか見てみれば

    まあ、ワケワカラン赤の他人としてはこれ以上はやめておきますw

    キャンセル

0

メモリを動的確保したことが失敗の原因だと思う

違います。
動的確保が直接の原因ではありません。動的であれ静的であれ、確保した領域を超えて書き込みを行っているのでしょう。ちゃんとわからないでメモリをめちゃくちゃにいじくり回していることこそが「原因」といえるでしょう。C言語は、いろいろなことを「プログラマの責任」のひとことで済ませてしまう怖い言語です。「初心者」をいいわけにちゃんと理解しないでプログラムを作るなら、その結果はすべて自分に跳ね返ってきますので覚悟してください。

さて。
main関数の
    x = (double*)malloc(sizeof(double) * N);
ここで確保したメモリ領域は、使われないまま
    x = audio_read(&prm_in, fn);
としてxが上書きされてしまうので、二度とアクセスできなくなります。いわゆる「メモリリーク」と呼ばれる不良です。(ヒープ破壊にはつながりませんが)

このとき、audio_read関数が返すのは
    fread(&data_size, 4, 1, fp);
    prm->L = data_size / 2;
    data = (double*)calloc(prm->L, sizeof(double));
として確保した領域です。このあとにこの領域は
    S_fft(x, y, N, -1);
としてS_fftに渡されます。S_fft(double ak[], double bk[], int n, int ff)の中では

        for (i = 0; i < n; i++) {
            ak[i] /= n;  bk[i] /= n;
        }


などとしているところがあります。つまり、data_size<2*N以上でなければ、確保したエリアをはみ出してアクセスを行う(メモリの破壊を行う)ということになります(他に危ないところがあるかどうかのチェックはしていません)。ここが一番怪しいところです。しかし、入力ファイルの詳細が不明なため、ここでエラーが埋め込まれたのかどうかはしかとは確認できません。

また、audio_read関数中での
    audiodatasize = (double*)malloc(sizeof(double) * prm->L);
は全く使われていないようですが、何か意図があるのですか? (これも直接のエラーにはなりませんが)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2021/03/01 07:11

    そこまでプログラムを読んでいませんが、多分違う原因でしょう。

    Cでは、動的に確保されるメモリ(ローカル変数とかmallocで確保した領域)は勝手には初期化されないので、生成直後は内容は不定(プログラムを実行してみて初めて値が決まること。特定の値が期待できないこと)となります。なにか値を設定する前にその値を使用して計算していたりして、その値が破綻を招いているのでは。

    キャンセル

  • 2021/03/01 19:04

    言われた通りakのメモリを確保しようとしてS_fft関数内にak = (double*)malloc(sizeof(double) * N)でメモリ領域を確保してみたら正常に動作はするのですが、出力結果が出鱈目になってしまいます。
    ブレークポイントを設置してxの中身を覗いてみても原因が分かりません。
    もう少しだけヒントをお願いします。

    キャンセル

  • 2021/03/01 20:38

    プログラムは、首尾一貫していなければいけません。局所的にちょこちょこといじくり回してもダメなときはダメです。

    まず、音声データを入力する領域とデータを出力する領域を兼用するのをやめて、それぞれに独立に必要なサイズで領域を確保するようにしてください。

    キャンセル

0

malloc,callocで確保したメモリ以上に書き込んでいるのでは。確保したメモリのサイズと、メモリにループで書き込んでる所をデバッグして突き合わせていくしかないですね。

x = audio_read(&prm_in, fn);


ここで戻ってきたxのメモリ長は不明ですが、

    for (i = 0; i < N; i++) {
        if (x != NULL && y != NULL) {
            pw = sqrt(x[i] * x[i] + y[i] * y[i]) * 100; // パワースペクトル
        }
        fq = i / (dt * Fr) * 2.2;   //  周波数
        //printf("%f = %d / (%f * %d)\n", fq, i, dt, N);
        fprintf(txt, "%fHz  %d\n", fq, pw);    /* エラー箇所 */
    }


N固定でループ回してると、xのメモリ長<(N*sizeof(double)) の場合にバッファオーバーランすると思います。

VisualStudioなら、デバッグ中にメニューのデバッグ⇒ウィンドウ⇒メモリで、ポインタのアドレスを打ち込んで直接メモリの中を覗けます。電卓で16進数表示でアドレス計算しながら、どこまで書き込まれたか実際に確認してみるとよいでしょう。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

mallocやcallocが必要なメモリを指定して呼び出すのに対して、freeは解放するメモリの大きさを指定していないことを不思議に思ったことはありませんか。

freeが解放するメモリの大きさを指定しなくてよいのは、mallocが返すメモリへのポインタの手前の管理領域に割り当てたメモリの大きさを持っているからです。

多くの場合、メモリ破壊は領域外アクセス(SIGSEGV)になることが多いのですが、それ以外のエラーになる場合もあります。
それは、続けてmalloc等で取った領域、xとyがあり、xを配列などとして使って取ったメモリを超えて書き込んで、yの管理領域を上書きしてしまいうエラーです。このあと、yをfreeしようとすると、free関数がチェックをして管理領域が壊れていることを知らせます。
あるいは、x += 1 とか y++ をやってmallocしたときの先頭アドレスでない状態にしてからfreeを行った場合も同じエラーが出ます。
今回起こっているのはそういう現象です。
xの指す領域でサイズを超えて書き込んでいるか、xやyを更新していないかをを調べてください。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 87.34%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る