重要なお知らせ

「教えて! goo」は2025年9月17日(水)をもちまして、サービスを終了いたします。詳細はこちら>

【GOLF me!】初月無料お試し

構造体をmallocにより動的確保を行っていたのですが、例えば
typedef struct _point{
int x, y;
} point;

point *pelem_point;

pelem_point = (point *)malloc(sizeof(point)*5);

このように、point型の構造体を5つ確保するとします。
しかし、
(pelem_point+100)->x = 1;
(pelem_point+100)->y = 2;

printf("%d\n", (pelem_point+100)->x);
printf("%d\n", (pelem_point+100)->y);

とやったら、確保していない100個先のところも構造体として利用できました。
なぜなのでしょうか。

自分の考えではこのようになりました。
mallocによりヒープ領域から適当な空いているメモリのアドレスが渡されるため、そこからはヒープ領域より先に、限りがあるまで進めてしまうために確保外のサイズにアクセスしても使えてしまっている。
また、mallocにより確保した場合は使用中のラベルがはられるため他に侵されることはないが、先の例のようにmallocによって確保してない場合はいくら使用できたとしても、空いているとコンピュータでは認識されるため、何かヒープ領域を使う場合に勝手に上書きされてしまう可能性がある。

しかし、この考えでも、なぜ確保外の領域が構造体のサイズ分ずつ区切られているのか納得いきません。

わかる方いましたらよろしくお願いします。

A 回答 (4件)

> しかし、この考えでも、なぜ確保外の領域が構造体のサイズ分ずつ区切られているのか納得いきません。



#1の回答にに補足します。
int x[10];
とあったとき、x + 3と(char*)(x) + 3は同じ値でしょうか違う値でしょうか?

*(x + 3)とあったら、コンパイラーはxはint型の配列だからということで、xが100だったとして、x + 3は100 + sizeof(int) * 3の値になります。(char*)(x) + 3は100 + sizeof(char)*3の値になります。

確保しているわけではなく、コンパイラーがそう扱うというだけです。確保されているかどうかをコンパイラーは気にしませんし、実行してみるまでそれはわかりません。(配列として確保している場合、コンパイラーによっては警告くらい出しそうですが。)


「何かヒープ領域を使う場合に勝手に上書きされてしまう可能性がある」どころか、ライブラリーが管理のために作っている構造を破壊して、予測不能な動きをさせることもありそうですが。
mallocの動きはmallocの実装によって異なります。
様々なmallocの実装の解説はすでに多数公開されていますからそれらを読んでみてはいかがでしょうか。
http://ja.wikipedia.org/wiki/Malloc

glibc
http://www.slideshare.net/kosaki55tea/glibc-malloc
http://www.valinux.co.jp/technologylibrary/docum …
ソースコード: https://sourceware.org/git/?p=glibc.git;a=tree;f …

tcmalloc
http://goog-perftools.sourceforge.net/doc/tcmall …
ソースコード: https://code.google.com/p/gperftools/source/brow …

jemalloc
https://www.facebook.com/note.php?note_id=480222 …
和訳: http://d.hatena.ne.jp/repeatedly/20110110/129463 …
ソースコード: http://fxr.watson.org/fxr/source/stdlib/malloc.c …
    • good
    • 0
この回答へのお礼

回答ありがとうございます。

具体的な例を挙げてくださったおかげで、理解しやすかったです。ありがとうございます。
もちろんこのようなコードを多分に利用するとかそういったわけではないですが、気になったので質問させていただきました。
まだより深い、内部的な知識は全くないので、どういった実装になっているかなどしっかりと勉強していきたいと思います。
URLまで載せていただき感謝です。

お礼日時:2014/12/07 23:19

malloc じゃなくって, 単純に配列を使っても同じでしょ?

    • good
    • 0
この回答へのお礼

回答ありがとうございます。

確かに配列でも似たようなことが言えました。ただ、配列では要素数を越えた場合エラーが出てくれると思っているのですが、mallocにより確保したポインタではそのようなことが起こらなかったため疑問を持ってしまいました。

お礼日時:2014/12/07 23:26

C/C++ でのポインタについて、少し調べられた方が良いと思います。


ポインタ型は、配列として使えますが、全く別物です。
mallocは、指定のバイト数の領域を確保し、その先頭アドレスを返します。
(構造体 何個分のデータかどうかは、mallocの関与するところではない)
そのアドレスを構造体へのポインタとしてキャストする事で、構造体としての利用が可能となりますが、構造体何個分の領域が確保され、使えるかを管理するのは、完全にプログラマの責任で、セキュリティ上のバッファオーバーフロー問題とかに関係してくる事となります。
    • good
    • 0
この回答へのお礼

回答ありがとうございます。

現在大学の課題でプログラムを作っているときにふと生じた疑問でしたが、構造体へのポインタとしてのキャストをしていることによりそういったことが起こっていたんですね。
久しぶりにC言語を扱ったので、再度ポインタについて学び直したいと思います。

お礼日時:2014/12/07 23:23

>なぜ確保外の領域が構造体のサイズ分ずつ区切られているのか納得いきません。



区切られているワケではなく、構造体のサイズからコンパイラがアドレスを「算出」しているだけです。

>mallocによりヒープ領域から適当な空いているメモリのアドレスが渡されるため、そこからはヒープ領域より先に、限りがあるまで進めてしまうために確保外のサイズにアクセスしても使えてしまっている。

管理状態によります。
実際に読み書きする時に仮想アドレスにメモリを割り当てる。
とかの場合は、例外発生して死ぬ可能性もあります。
# OSが例外を受け取って、ライブラリに転送してごにょごにょ…とか、そういう場合もあるかも知れませんが。

いずれにしろ、正しい使い方ではないので何が起こっても文句は言えません。
# 確保された領域外の変数に値を設定したらハードディスクがフォーマットされてしまった。とかなっても。


バッファオーバーランを引き起こす正しいコードではありますけどね。
    • good
    • 0
この回答へのお礼

回答ありがとうございます。

Cの勉強はlinuxでやるため、VMwareの仮想環境上で行っているので、ハードが必要以上にフォーマットされることはとりあえず大丈夫だと思っています。

おっしゃるとおり、正しい使い方ではないのでもちろんこれを通すつもりではないですが、たまたま気になってしまって質問してしまいました。
当たり前ですが、実際ではmallocにより確保した分だけを使いたいと思います。

お礼日時:2014/12/07 23:11

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!