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

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

ただいまの
回答率

90.32%

C言語でのメモリの解放が出来ません

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,028

ain4425

score 17

画像ファイルを読み込んで、いろいろなフィルタをかけて出力するプログラムを動かしていたのですが、画像枚数を4枚から5枚に増やしたところ以下のようなエラーが出てしまいました。

Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland
image.c:
Turbo Incremental Link 5.00 Copyright (c) 1997, 2000 Borland
Fatal: Error detected (LME1514)
Fatal: Error detected (LME1514)
Fatal: Error detected (LME1514)
Fatal: Error detected (LME1514)

[LME1514]を調べてみたところ、どうもメモリの溢れが問題となっているようでした。
どうか助けて頂けると幸いです。


画像にフィルタをかけるプログラム部分は長くなるので省略しますので、読込、出力等の重要部分だけのプログラムをimage.cとして記載します。
上記に付随するヘッダーファイルはppmlib.hとして記載します。

image.c

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include"ppmlib.h"

int Setting(void);
void ReadImage(int set);
void SaveImage(void);

int main(void){
    //標準設定パラメータ
    int set;

    //メモリ確保
    memory();

    //標準設定
    set=Setting();

    //画像読込
    ReadImage(set);

    //画像の出力
    SaveImage();

    //メモリの開放
    free(pimage);
    return 0;
}

//標準設定
int Setting(void){
    int set_flag;
    printf("標準設定→1\n");
    printf("詳細設定→0\n");
    scanf("%d",&set_flag);

    return set_flag;
}



//画像の読み込み
void ReadImage(set){

    load_color_image(0,1,set);
    load_color_image(1,2,set);
    load_color_image(2,3,set);
    load_color_image(3,4,set);
    load_color_image(4,5,set);

}

//画像の出力
void SaveImage(void){
    save_color_image(0,"");
    save_color_image(1,"");
    save_color_image(3,"");
    save_color_image(4,"");
}

ppmlib.h

#include<stdlib.h>
#include<string.h>

//height[],width[]のMAX画素数
#define MAX_IMAGESIZE   3000
//諧調値
#define MAX_BRIGHTNESS   255
//階調数(+1)
#define GRAYLEVEL        256
//FILEnameの最大長
#define MAX_FILENAME     256
//Buffer最大長
#define MAX_BUFFERSIZE   256
//画像枚数
#define MAX_NUM_OF_IMAGES  5

char pimage;

typedef unsigned char (*pimage_type)[MAX_IMAGESIZE][MAX_IMAGESIZE][3];
int width[MAX_NUM_OF_IMAGES],height[MAX_NUM_OF_IMAGES];


void memory(void);
void load_color_image( int n, int no, int set );
void save_color_image( int n, char name[] );
void copy_color_image( int n1, int n2 );
void init_color_image( int n, int red, int green, int blue );

void memory(void){
    pimage_type pimage = (pimage_type)malloc(MAX_NUM_OF_IMAGES * MAX_IMAGESIZE * MAX_IMAGESIZE * 3);
}

void load_color_image( int n, int no, int set ){

    char fname[MAX_FILENAME];
    char buffer[MAX_BUFFERSIZE];
    FILE *fp;
    int max_gray=0,x,y,col;

    if( no==1 && set==1){
    printf("INPUT FILE : white1.ppm\n");
        strcpy(fname,"white1.ppm");
    }
    else if( no==2 && set==1){
    printf("INPUT FILE : white2.ppm\n");
        strcpy(fname,"white2.ppm");
    }
    else if( no==3 && set==1){
    printf("INPUT FILE : white3.ppm\n");
        strcpy(fname,"white3.ppm");
    }
    else if( no==4 && set==1){
    printf("INPUT FILE : white4.ppm\n");
        strcpy(fname,"white4.ppm");
    }
    else if( no==5 && set==1){
    printf("INPUT FILE : white5.ppm\n");
        strcpy(fname,"white5.ppm");
    }
    else{
        printf("INPUT FILE : ");
        scanf("%s", fname);
    }
    if ( (fp = fopen( fname, "rb" ))==NULL ){
        printf("CAN'T OPEN THE FILE\n");
        exit(1);
    }
    fgets( buffer, MAX_BUFFERSIZE, fp );
    //check P6
    if ( buffer[0]!='P' || buffer[1]!='6' ){
    printf("FILE TIPE WRONG!\n");
        exit(1);
    }
    width[n] = 0;
    height[n] = 0;
    while ( width[n] == 0 || height[n] == 0 ){
        fgets( buffer, MAX_BUFFERSIZE, fp );
        if ( buffer[0] != '#' ) {
            sscanf( buffer, "%d %d", &width[n], &height[n] );
        }
    }

    while ( max_gray == 0 ){
        fgets( buffer, MAX_BUFFERSIZE, fp );
        if ( buffer[0] != '#' ){
            sscanf( buffer, "%d", &max_gray );
        }
    }
    printf("----------------------------\n");
    printf("WIDTH  :%d\n",width[n]);    
    printf("HEIGHT :%d\n",height[n]);
    printf("MAX RGB:%d\n",max_gray);
    printf("----------------------------\n");
    if ( width[n] > MAX_IMAGESIZE || height[n] >
        MAX_IMAGESIZE ||
        max_gray != MAX_BRIGHTNESS ){
        printf("SIZE or RGB WRONG!\n");
        exit(1);
    }
    for(y=0;y<height[n];y++){
        for(x=0;x<width[n];x++){
            for(col=0;col<3;col++){
              pimage[n][x][y][col] = (unsigned char)fgetc( fp );
            }
        }
    }
    printf("COMPLETE! ---> image[%d]\n",n);
    printf("----------------------------\n");    
    fclose(fp);
}


void save_color_image( int n, char name[] ){
    char fname[MAX_FILENAME];
    FILE *fp;
    int x,y,col;
    if ( name[0] == '\0' ){
        printf("OUTPUT FILE : ");
        scanf("%s",fname);
    } else strcpy( fname, name );
    if ( (fp = fopen(fname, "wb"))==NULL ){
        printf("CAN'T SAVE THE FILE\n");
        exit(1);
    }
    fputs( "P6\n", fp );
    fputs( "# Created by Image Processing\n", fp );
    fprintf( fp, "%d %d\n", width[n], height[n] );
    fprintf( fp, "%d\n", MAX_BRIGHTNESS );

    for(y=0;y<height[n];y++){
        for(x=0;x<width[n];x++){
            for(col=0;col<3;col++){
                fputc( pimage[n][x][y][col], fp );
            }
        }
    }
    fclose(fp);
    printf("COMPLETE!\n");
}


void copy_color_image( int n1, int n2 ){
    int x,y,col;

    width[n2] = width[n1];  
    height[n2] = height[n1];
    for(y=0;y<height[n1];y++){
        for(x=0;x<width[n1];x++){
            for(col=0;col<3;col++){
                pimage[n2][x][y][col] = pimage[n1][x][y][col];
            }
        }
    }
}


void init_color_image( int n, int red, int green, int blue ){
    int x, y;

    for(y=0;y<height[n];y++){
        for(x=0;x<width[n];x++){
            if (red   != -1) pimage[n][x][y][0] = red;
            if (green != -1) pimage[n][x][y][1] = green;
            if (blue  != -1) pimage[n][x][y][2] = blue;
        }
    }
}

以上よろしくお願いします

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • ttyp03

    2018/07/06 16:11

    質問タイトルと内容が一致してないので修正お願いします。

    キャンセル

回答 3

+5

こんにちは。

LME1415で検索するとグローバル変数とメモリについてがでてきました。
グローバル変数を取りすぎるとリンクエラーになるようです。

unsigned char image[MAX_NUM_OF_IMAGES][MAX_IMAGESIZE][MAX_IMAGESIZE][3];

のバイト数を計算すると5*3000*3000*3=135,000,000=0x80BEFC0となり、BCCがサポートしているグローバル領域のサイズを超えているっぽいです。

これくらい大きなメモリはヒープに獲得(malloc/free等を使う)した方がよいと思います。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/07/17 23:55

    サイトを参考にさせていただき、ポインタ型をどう扱うかを学ぶことが出来ました。
    おかげさまで一つのキーとなるエラー(E2342)を減らすことが出来ました。
    しかし、やはりまだよく残りのエラー、警告の理由が分かりません。
    警告 W8004 ppmlib.h 31: 'pimage' に代入した値は使われていない(関数 memory )
    エラー E2062 ppmlib.h 103: 無効な間接参照(関数 load_color_image )
    エラー E2062 ppmlib.h 133: 無効な間接参照(関数 save_color_image )
    エラー E2062 ppmlib.h 150: 無効な間接参照(関数 copy_color_image )
    エラー E2062 ppmlib.h 150: 無効な間接参照(関数 copy_color_image )
    エラー E2062 ppmlib.h 162: 無効な間接参照(関数 init_color_image )
    エラー E2062 ppmlib.h 163: 無効な間接参照(関数 init_color_image )
    エラー E2062 ppmlib.h 164: 無効な間接参照(関数 init_color_image )
    どこが問題でどうすればよいのか教えていただけると幸いです。

    キャンセル

  • 2018/07/18 00:31

    ああ、気がつくとずいぶん伸びてました。
    Ekiさん、折角ここまでサポートされているので回答を起こされた方が BA を適切につけることができて好ましいと思いますよ。

    キャンセル

  • 2018/07/18 01:25

    現在どのようなコードになっているのか少し分からないのですが、とりあえず、 E2062 の原因は pimages の型にあると思われます。今質問文に掲載されているコードだと、 pimages の型が char になっています。もしこのままなら、 char に対してインデックスアクセス pimages[...] はできませんから、それでエラーになっていると思われます。

    そうですね。今までのことも含めて回答のまとめようかと思います。

    キャンセル

checkベストアンサー

+2

Chironian さんのコメント欄でお話していましたが、あまりにも長くなってきたので、まとめる意味を込めてこちらに回答します。

しばらく、既にコメント欄で進めて解決した部分も含めてまとめていきます。
説明の都合上変更したところがあります。特に pimage_type を廃止したので注意です (別の型を導入しました) 。 pimage_type のことは一旦忘れてください。

 malloc() と free()

まず最初の問題は、グローバル変数として確保しようとしている多次元配列が非常に大きかったため、コンパイラの限界を越えてしまったことでした。そのような大量のメモリ確保を行うときは、残念ながら ヒープ領域 に頼る他ありません。ヒープ領域は malloc() 関数を使って確保するメモリ領域です。ここでは一般により多くのメモリを確保することができますが、若干速度が遅くなったり、使い終えた領域を忘れずに free() しなければいけなかったりと少し不便な領域です。

さて、例えば malloc() を使って int 型の要素数 10 の配列を確保するには、次のようにします。

// malloc() は取得したい領域のサイズをバイト単位で受け取ります。
// sizeof(int) は int 1 つ分のバイト数を表すので、それ×10 をしてやれば 10 個分になりますね。
int *p = (int *)malloc(sizeof(int) * 10);

// なんかいろいろ。 int のポインタは厳密には一次元配列ではありませんが、
// 似たような扱いができます。 p[0] のように添字でアクセスできます。
p を使う処理1;
p を使う処理2;
p を使う処理3;

// 終わったら、必ず解放しましょう。
free(p);

malloc() は言われた通りのサイズの領域をガバッと持ってくることしかしませんから、確保したメモリ領域がどういう型を格納するためのものかなどは気にしません。上の例では、わたしたちは確保した領域を int の配列として使いたかったのですよね?ということで、確保した領域を int のポインタにつっこんでいるのです。

 多次元配列の malloc()

さて、多次元配列のときはどうしましょうか。実は、C言語には多次元配列というものはなく 配列の配列 しかないのです。ということは配列の型が分かれば、上の例と同様に、その配列の型へのポインタにつっこめば配列の配列として使えるのではないでしょうか?

はい、実は配列にも型があります。ただし表記が複雑なので混乱します。これはC言語の悪いところの一つですが、興味があれば聞いてください。説明します。が、もう少し後でもよいかもしれません。

とりあえず次の一文で、 image_type という名前が、とある配列へのポインタ型になります。

typedef unsigned char image_type[MAX_IMAGESIZE][MAX_IMAGESIZE][3];

 さきほどまでコメント欄では pimage_type としていましたが、 名前も内容も 僅かに変わっています。混乱させてすみません。

この image_type がどういう型かですが、

unsigned char image[MAX_NUM_OF_IMAGES][MAX_IMAGESIZE][MAX_IMAGESIZE][3];
image_type image[MAX_NUM_OF_IMAGES];

上の二つの宣言が全く同じものになるような型です。よく見ると上の配列は最初にグローバル変数で配列を用意しようとしていたときのアレですね。
感覚的に言えば、上の配列の 3 次元配列部分 [MAX_IMAGESIZE][MAX_IMAGESIZE][3] をひとまとめにしたような型です。

欲しい型が判明してしまえば、やることは int の配列を用意したときと同様です。int だったところを image_type にすればよいです。

image_type *pimage = (image_type *)malloc(sizeof(image_type) * MAX_NUM_OF_IMAGES);

 実はコメントにおける pimage_type というのは、 image_type のポインタ型でした。つまり pimage_type == (image_type *) です。 image_type を定義したほうが説明しやすく、分かりやすいと思ったので変更しました。

その後、 malloc() を 直接グローバル変数の初期値としてつっこむことはできない という話をしましたね。
必ず何か関数を用意して (main() 関数でもよいですが) 実行時の処理 として malloc() しないといけません。同様に free() もどこかしらで行う必要があります。


(イマココ) ここまでコメント欄でお話しました。


 いろいろな場所から使えるようにする

そういうわけで実装してくださった memory() 関数ですが、今度は他の場所で pimage が見つからなくなったということでした。
memory() 関数内のローカル変数として宣言されたものは当然、他の関数からは見えません。
別の関数で malloc() するなら、確保したメモリ領域を memory() 関数の戻り値として返してあげるか、 pimage をグローバル変数にするか、どちらかの方法で外に出してやらないといけません。

グローバル変数にするということなので、単純に pimage をグローバルで宣言してやればよいです。 pimage はよくわからない image_type * などという型をしていますが、ただのポインタです。 int * とかと同じでいいんです。

image_type *pimage;

// (中略)

void memory(void) {
  // memory() 側ではローカル変数 pimage を宣言するのをやめ、グローバル変数につっこむようにする。
  pimage = malloc(...);
}

 まとめ

結局、次のような感じのコードになります。

typedef unsigned char image_type[MAX_IMAGESIZE][MAX_IMAGESIZE][3];
image_type *pimage;

// (中略)

void memory(void){
    pimage = (image_type *)malloc(sizeof(image_type) * MAX_NUM_OF_IMAGES);
}

 おまけ

本題とは逸れますが、

  1. printf() や FILE 構造体などは stdio.h というヘッダファイルに入っています。できれば #include <stdio.h> しておいたほうがよいと思います。
  2. ReadImage() 関数の定義のところで、その引数のところが

void ReadImage(set) {
// ...
}

となっていますが、きちんと set の型を明示するべきです。コンパイルエラーにはならないのですが、C言語では何も書かないと勝手に int と思われるという恐ろしいルールがあります。今回は set の型は使われ方から見ても int なので結果的に問題はなかったのですが、思わぬバグの温床になります。見た目にもわかりづらいので、例え int を受け取るのであろうと、きちんと書くべきです。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/07/18 12:46

    長く付き合って下さり、本当にありがとうございました。
    ヒープ領域について、またポインタなど数々お世話になりました。
    結果的にエラー無く通すことが出来ました。まだまだ学ばなければならないことが多いので、Ekiさんに教えてくださったサイトを参考に勉強していこうかと思います。
    この度はありがとうございました。

    キャンセル

+1

静的領域のサイズオーバーではないでしょうか。リンカのオプションでサイズを拡大できるかもしれません。あるいはimageをヒープ領域に確保しましょう。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

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

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