C言語について質問です。
画像を読み込む時、その画像の幅、高さを入力し、そのサイズに見合った配列を確保します。その後ファイル名を入力し、配列に読み込むプログラムを作成しました。
メインの部分のみ記述します。
int xsize, ysize, i;
unsigned char **src;
char filename[30];
FILE *fp;
printf("ファイル名を入力してください:"); scanf("%s", filename);
printf("画像の幅:");scanf("%d", &xsize);
printf("画像の高さ:");scanf("%d", &ysize);
src = (unsigned char **)malloc(sizeof(unsigned char *) * ysize);
for(i=0; i<ysize; i++)
src[i] = (unsigned char *)malloc(sizeof(unsigned char) * xsize);
fp = fopen(filename, "rb");
fread(src[0], sizeof(unsigned char), xsize * ysize, fp);
このように記述し、エラーもなく実行できたのですが、srcをこのまま出力すると変?な画像となって出力されてしまいました。
上のように記述した場合、矛盾する場所はあるでしょうか?
そして、この方法以外に配列を確保する方法はあるでしょうか?
No.6ベストアンサー
- 回答日時:
★最初に ysize 分のポインタを管理領域と呼んでいません。
・プログラマが malloc(1000) として 1000 バイトを確保した場合、
実際のメモリ上には
(1)malloc() 関数がメモリ確保の管理情報を数バイト保持(8~16バイトほど)
(2)実際にプログラマが使用できる 1000 バイトの領域
となります。つまり、使用できる 1000 バイトの領域の先頭に数バイトのメモリ管理情報が
存在するのです。
・詳しい内部の仕組みは分かりませんが、確保したメモリ領域をリストデータで管理していると
思います。リストデータはポインタで次々にデータをチェインしますよね。
そのとき、次のデータ位置を表すポインタや確保サイズなどの数バイトが malloc() 関数などの
メモリ管理情報(領域)として存在するわけです。
・以上を踏まえて回答 No.4 を読んでみて下さい。
もう少し分かりやすく説明すると
char *a = malloc(1000);
char *b = malloc(2000);
char *c = malloc(3000);
char *d = malloc(4000);
の4つのメモリは合計で 10000 バイト確保され 10000 バイトを使用できます。
でも、実際のメモリ上には、メモリ管理情報(領域)=ヘッダが存在しますので
a…8 + 1000
b…8 + 2000
c…8 + 3000
d…8 + 4000
で合計 10032 バイトのメモリを消費します。なお、8 バイトが管理情報ですが、昔使っていた
コンパイラを調べたときのサイズです。今はどれくらいか分かりませんが数バイトはあります。
・メモリを節約したい場合は、回答者 No.5 さんのマクロ関数が一番節約できると思います。
前回も同じような感じでアドバイスしたときにマクロ関数を紹介しようと思いましたが、
それを使うのがスッキリしなくて unsigned char **src を使って src[y][x] とアクセスしたいのかと
思いましたのでマクロ使用の紹介は控えました。が、他の回答者さんのお礼にマクロ関数のことが
よく分からないとなっていましたね。今回はマクロ関数を利用する方がメモリを節約できます。
・マクロ関数を利用すれば、画像の横×高さのサイズ分だけ確保すればよいので src[] のポインタ分の
メモリは節約できます。
・以上。malloc() などは『実際のメモリ使用量=メモリ管理情報(領域)+確保サイズ』です。
返答ありがとうございました。
丁寧に説明いただきありがとうございます。
いろいろと質問してしまいましたが、やっと理解できました。
mallocは『実際のメモリ使用量=メモリ管理情報(領域)+確保サイズ』つまりmallocを行わなければ、いくらポインタをmallocで確保したとしても管理領域は発生しないということですね。
自分が質問に書いた”ysize分のポインタを確保”これはポインタを確保しただけであり、この時点では管理領域は発生しておらず、その後自分が書いたソースのように記述すると、各行ごとに管理領域が発生してしまうが、Oh-Orangeさんが提示してくださったプログラムではそれが発生しない。
自分の勘違いの基は「ポインタ変数=管理領域」という思い込みに原因がありましたねorz
このような理解でよいでしょうか?
そして最後にもう1つ気がかりが・・・・
この質問で初めに答えてくださったプログラムについてです。
そのプログラムではbuffのアドレスをsrcへ格納しています。
もしその後free(src)を行った場合、buffへの影響はないでしょうか?
No.5
- 回答日時:
どうしても使うメモリを xsize * ysize (くらい: malloc の管理領域はどうしても必要だから) にするのであれば, src を unsigned char * で定義してマクロで逃げる.
つまり
#define src(i, j) src[(i)+xsize*(j)]
というマクロを用意してメモリは
src = malloc(xsize * ysize);
のように確保する. そうすれば size(x, y) という形でアクセスできる, はず.
姑息だけど.
返答遅れて申し訳ありませんでした。
確かにマクロで #define src(i, j) src[(i)+xsize*(j)] このように記述すれば、2次元配列として扱うことができますね。
アドバイスをしていただき、ありがとうございました。
No.4
- 回答日時:
★メモリ確保量を考えるのならば、一度に大きい配列を確保した方が良い。
>src = (unsigned char **)malloc(sizeof(unsigned char *) * ysize);
>for(i=0; i<ysize; i++)
> src[i] = (unsigned char *)malloc(sizeof(unsigned char) * xsize);
↑の方法では、ysize 分のメモリ管理領域(数バイト)のサイズ分だけ無駄になります。
つまり、メモリ管理領域を 8 バイトと仮定して、ysize=1000px とすると
8バイト×1000px=8000バイト分の管理領域が必要になります。
でも、
>buff = (unsigned char *)malloc( sizeof(unsigned char) * xsize * ysize );
>src = (unsigned char **)malloc( sizeof(unsigned char *) * ysize );
↑の方法ならメモリ管理領域は2つ分で済みます。→8バイト×2個=16バイト
8000バイトと16バイトでは、どちらがメモリ量を節約できるか分かりますよね。
・malloc 関数などのメモリ確保では、数バイトの管理領域+確保容量の合計が実際のメモリ上に
確保されるサイズ(バイト)です。このような内部的な仕組みを考えていないと確保容量を本当に
節約するプログラムは記述できません。→知っていればの話ですけど。
・以上。補足情報でした。
返答が遅れてしまい、申し訳ありませんでした。
ちょっと込み入ってて・・・
確かに自分の方法では管理領域としてysize分を必要とする記述方法になりますね。
Oh-Orangeさんが記述された
src = (unsigned char **)malloc( sizeof(unsigned char *) * ysize );
このプログラムもysize分のポインタを確保して、そのポインタの先頭アドレスをsrcに代入するという意味では 管理領域としてysize分を必要とする記述になり、自分が書いたプログラムと同じ管理領域にならないのでしょうか?
No.3
- 回答日時:
★アドバイス
・src[0] のメモリ領域は xsize 分しか確保されていません。
>fread(src[0], sizeof(unsigned char), xsize * ysize, fp);
とするとメモリ領域が足りないため、正しく読み込めませんよ。
src[0]、src[1]、src[2]…src[ysize - 1]のメモリ領域は連続して確保されることは
ないと思います。通常、malloc() 関数ではメモリ管理の為に数バイトのヘッダ情報が
あるためメモリ領域が連続して確保されません。無理!
解決法:
・一度に大きい配列領域を確保してから以前に紹介した FuncSetArray() 関数のようなやり方で
unsigned char **src のポインタにセットして下さい。
・下にサンプルを載せます。
サンプル:
int xsize, ysize, i;
unsigned char **src;
unsigned char *buff;
FILE *fp;
// メモリの確保
buff = (unsigned char *)malloc( sizeof(unsigned char) * xsize * ysize );
src = (unsigned char **)malloc( sizeof(unsigned char *) * ysize );
// ファイルの読み込み
fp = fopen( filename, "rb" );
fread( buff, sizeof(unsigned char), (xsize * ysize), fp );
fclose( fp );
// 縦軸の配列セット
for ( i = 0 ; i < ysize; i++ ){
src[ i ] = &buff[ xsize * i ];
}
:
処理
:
その他:
・上記のサンプルではエラーチェックなしです。チェックして下さい。
連続したメモリ領域 buff を確保して、その領域に fread で読み込めばデータが必ず
連続して読み込まれます。ラインごとに分割してメモリを確保した場合は、すべての
データが連続しません。malloc 以外のメモリ確保関数でも同じです。
・以上。参考に!
返答ありがとうございました。
自分もこの方法を考えましたが、確かにうまく格納できると思います。しかしメモリ確保の量を考えると、sizeof(unsigned char) * xsize * ysize + sizeof(unsigned char *) * ysizeになると思います。
できるだけxsize*ysizeにしようと思っていたのですがちょっと難しそうです。
しかしこの方法を用いれば確実に2次元配列で扱うことができるし、画像の保存時もbuffをfwrite関数の引数に与えればいいだけなので、ある意味一番効率が良い方法かもしれません。
アドバイスありがとうございました。
No.2
- 回答日時:
srcが連続領域である保障がありません。
fread(src[0], sizeof(unsigned char), xsize * ysize, fp);
が何処に読み込まれるか不確実です。
fp = fopen(filename, "rb");
for(i=0; i<ysize; i++) {
src[i] = (unsigned char *)malloc(sizeof(unsigned char) * xsize);
fread(src[i], sizeof(unsigned char), xsize , fp);
または
src = (unsigned char *)malloc(xsize * ysize);
fp = fopen(filename, "rb");
fread(src[0], xsize , ysize, fp);
なのでは?
src = (unsigned char *)malloc(xsize * ysize);
fp = fopen(filename, "rb");
fread(src[0], xsize , ysize, fp);
確かにこのプログラムなら正常に読み込めると思います。
しかし・・・私が説明不足でしたね。
わざわざ**srcを使用した理由は通常の2次元配列同様に亜通ことができるからです。 src[][]のように。
この画像を読んでから複雑な処理を行うため、二次元配列同様に扱えたらとても理解しやすくなるので、**srcを用いました。
アドバイスありがとうございました^^
No.1
- 回答日時:
> srcをこのまま出力すると
ここのコードを省略せずに書いてほしかったです。
ヘッダーファイルのインクルードや、関数の定義など、
どうせなら書かれたコードを全部出してみませんか?
返答ありがとうございました。
と、同時に申し訳ありませんでした。
確かにこの疑問がsrcを表示させる部分に間違いがある可能性も十分にあるため、全てのコードを乗せるのが定石だとは思ったのですが、自分が不安を持っている部分について主に質問したかったためこのように記述してしまいました。
申し訳ないです。
やはり全てのコードを乗せたほうが良いですよね。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・人生のプチ美学を教えてください!!
- ・10秒目をつむったら…
- ・あなたの習慣について教えてください!!
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・【大喜利】【投稿~9/18】 おとぎ話『桃太郎』の知られざるエピソード
- ・街中で見かけて「グッときた人」の思い出
- ・「一気に最後まで読んだ」本、教えて下さい!
- ・幼稚園時代「何組」でしたか?
- ・激凹みから立ち直る方法
- ・1つだけ過去を変えられるとしたら?
- ・【あるあるbot連動企画】あるあるbotに投稿したけど採用されなかったあるある募集
- ・【あるあるbot連動企画】フォロワー20万人のアカウントであなたのあるあるを披露してみませんか?
- ・映画のエンドロール観る派?観ない派?
- ・海外旅行から帰ってきたら、まず何を食べる?
- ・誕生日にもらった意外なもの
- ・天使と悪魔選手権
- ・ちょっと先の未来クイズ第2問
- ・【大喜利】【投稿~9/7】 ロボットの住む世界で流行ってる罰ゲームとは?
- ・推しミネラルウォーターはありますか?
- ・都道府県穴埋めゲーム
- ・この人頭いいなと思ったエピソード
- ・準・究極の選択
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
c言語のポインタへの文字列入力...
-
void*型のデータサイズ
-
構造体を使ったファイルの読み込み
-
newしないオブジェクトについて
-
malloc でのメモリ取得状況の可...
-
MSDNがgethostbynameではなくge...
-
stringの最大サイズ
-
座標値を読み込んである領域に...
-
allocってなんですか?
-
メモリ管理
-
スタック破壊の上手な見つけ方...
-
128ビット変数の符合表現について
-
new と malloc によるメモリの...
-
文字列を格納する配列を、動的...
-
VBからMFC-DLL呼び出し
-
メモリ不足になってしまう。
-
仮に&str[i]のアドレスを上げな...
-
newと配列?
-
x64環境で連続4GB以上のメモリ...
-
16進ダンプのプログラム
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
allocってなんですか?
-
newしないオブジェクトについて
-
c言語のポインタへの文字列入力...
-
ヒープメモリの解放について
-
配列の添え字の最大数とは?
-
stringの最大サイズ
-
C++で、メンバもヒープに確保さ...
-
プログラムが途中で強制終了し...
-
void*型のデータサイズ
-
malloc呼び出し時のセグメンテ...
-
スタック破壊の上手な見つけ方...
-
ビットをローテートするプログ...
-
構造体でchar name[]と*nameの...
-
mallocについて
-
GDI+におけるメモリの開放について
-
ポインタのポインタの初期化法
-
構造体を使ったファイルの読み込み
-
C言語 mallocとfreeについて
-
HEAP に関すること
-
VBからMFC-DLL呼び出し
おすすめ情報