最新閲覧日:

構造体を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によって確保してない場合はいくら使用できたとしても、空いているとコンピュータでは認識されるため、何かヒープ領域を使う場合に勝手に上書きされてしまう可能性がある。

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

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

このQ&Aに関連する最新のQ&A

A 回答 (4件)

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

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

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

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

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

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


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

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

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

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

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



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

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

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


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

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

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

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

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

このQ&Aに関連する人気のQ&A

このQ&Aと関連する良く見られている質問

Q[C]セグメンテーションエラー:malloc、ファイル、構造体

このプログラムで、gccコンパイル、実行は可能なのですが、ファイル名の入力が終了したところでセグメンテーション違反になります。
どうやったら解決できますか??

以下、Cプログラム>>
#include<stdio.h>
#include<stdlib.h>
#define NUM 256

struct GIFT {
char code[NUM];
char name[NUM];
int price;
};

int main(void)
{
FILE *fp1, *fp2;
char filename1[NUM];
char filename2[NUM];
int max, i;
struct GIFT *gifts[NUM];

printf("データ数を入力してください:"); scanf("%d",&max);
printf("入力ファイル名を指定してください:"); scanf("%s", filename1);
printf("出力ファイル名を指定してください:"); scanf("%s", filename2);

for( i=0 ; i<=max ; i++ ){
gifts[i] = (struct GIFT*)malloc(sizeof(struct GIFT) *(max+1));
if(gifts[i] == NULL) {
printf( "error\n" );
return (-1); }
}

if( (fp1 = fopen( filename1, "r")) == NULL ) {
printf("ファイルをオープンできません.\n");
} else if( (fp2 = fopen( filename2, "w")) == NULL ) {
printf("ファイルをオープンできません.\n");
} else {
for( i=0 ; i<max ; i++ ){
fscanf( fp1, "%s %s %d", gifts[i]->code, gifts[i]->name, gifts[i]->price);
fprintf( fp2, "code: %s\n" , gifts[i]->code);
fprintf( fp2, "name: %s\n" , gifts[i]->name);
fprintf( fp2, "price: %d\n", gifts[i]->price);
printf("\n");
free(gifts[i]);
}
for( i=0 ; i<max ; i++ ){
free(gifts[i]);
}
fclose(fp1);
fclose(fp2);
printf("指定ファイルへ出力を完了しました。\n");
}

return 0;
}

<<<<

実行結果>>>>
./ a
データ数を入力してください:4
入力ファイル名を指定してください:file_1.dat
出力ファイル名を指定してください:file_2.dat
セグメンテーション違反です
<<<<

ちなみにプログラムと同じフォルダ内にfile_1.datは存在します。
かなり粘って検索してみたりしてみたんですがどうも解決しません。
ご回答よろしくお願いいたします。

このプログラムで、gccコンパイル、実行は可能なのですが、ファイル名の入力が終了したところでセグメンテーション違反になります。
どうやったら解決できますか??

以下、Cプログラム>>
#include<stdio.h>
#include<stdlib.h>
#define NUM 256

struct GIFT {
char code[NUM];
char name[NUM];
int price;
};

int main(void)
{
FILE *fp1, *fp2;
char filename1[NUM];
char filename2[NUM];
int max, i;
struct GIFT *gifts[NUM];

printf("データ数を入力してくださ...続きを読む

Aベストアンサー

gccのオプション指定で恐らく指摘されるでしょうけど……。

>fscanf( fp1, "%s %s %d", gifts[i]->code, gifts[i]->name, gifts[i]->price);

gifts[i]->priceには何の値が入っているんでしょうかね?
で、そこは書き込みしていい「アドレス」ですかね?

Qint kosuu; とstruct tanka_kosuu kosuu[10]; の関係は

同プログラムの内容で現在3個の質問をしておりますが!
 その質問を解決する上で4つ目の質問をさせていただきます。
 悪しからず・・・
 さて
以下のサイトのプログラムで 些細な疑問がございます。
https://oshiete.goo.ne.jp/qa/9062058.html
 で
 struct tanka_kosuu {
int tanka; 
int kosuu; 
int kingaku; /
以上の中にあるkosuuと
 struct tanka_kosuu kosuu[10];のkosuu[10]とは直接関係がありますか?
 馬鹿な質問ばかりで申し訳ございませんがよろしくお願いいたします。

Aベストアンサー

#No.1です。

>kosuu[10];をakb[10];変えたところ 以下の errorでてコンパイルできません!?
> example10.c(15) : error C2065: 'kosuu' : 定義されていない識別子です。


宣言している変数名を変更したら、その変数を利用している場所(エラーメッセージで15行目と書かれています)の変数名も変更する必要があると思いませんか?

下の例で、1行目も変数をaからbに変えたら、2行目,3行目のaも、bに変える必要があのはご理解いただけますよね?
01: int a;
02: a = 10;
03: printf("a=%d\n", a);

Qstruct tanka_kosuu kosuu[10];に付いているアンダーバー"_"の意味

以下のプログラムで質問がございます。
C言語の構造体であるstructの変数であるtanka_kosuuですが
アンダーバー"_"を付けてあります。
(※struct tanka_kosuu kosuu[10];)
 これは、なんでつける必要があるのですか!?
  ご教授ができるお方がおられましたらよろしくお願いいたします。

#include <stdio.h>
struct tanka_kosuu {
int tanka;
int kosuu;
int kingaku;
};
int goukei_kingaku(struct tanka_kosuu kosuu[], int nyuuryoku_kosuu);
int main()
{
struct tanka_kosuu kosuu[10];
struct tanka_kosuu kari_nyuuryoku = {-1, 0, 0};
int nyuuryoku_kosuu = 0;
int i;
while(kari_nyuuryoku.tanka != 0){
scanf("%d %d", &kari_nyuuryoku.tanka,
&kari_nyuuryoku.kosuu);
kosuu[nyuuryoku_kosuu] = kari_nyuuryoku;
nyuuryoku_kosuu++;
}
for ( i = 0; i < nyuuryoku_kosuu-1; i++ ){
kosuu[i].kingaku = kosuu[i].tanka * kosuu[i].kosuu;
printf("%d\t%d\t%d\n", kosuu[i].tanka,
kosuu[i].kosuu, kosuu[i].kingaku);
}
printf("goukei=%d\n",goukei_kingaku(kosuu, nyuuryoku_kosuu-1));
return 0;
}
int goukei_kingaku(struct tanka_kosuu kosuu[], int nyuuryoku_kosuu)
{
int i;
int goukei = 0;
for ( i = 0; i < nyuuryoku_kosuu; i++ ){
goukei += kosuu[i].kingaku;
}
return goukei;
}

以下のプログラムで質問がございます。
C言語の構造体であるstructの変数であるtanka_kosuuですが
アンダーバー"_"を付けてあります。
(※struct tanka_kosuu kosuu[10];)
 これは、なんでつける必要があるのですか!?
  ご教授ができるお方がおられましたらよろしくお願いいたします。

#include <stdio.h>
struct tanka_kosuu {
int tanka;
int kosuu;
int kingaku;
};
int goukei_kingaku(struct tanka_kosuu kosuu[], int nyuuryoku_kosuu);
int main()
{
struct tanka_kosuu kosuu[10...続きを読む

Aベストアンサー

変数や型などの識別名は、プログラム言語仕様により使用できる文字が制限されています。

多くの言語で空白文字は認められないので、複数の英単語からなる名前をつづる場合に
tankakosuu
では読みづらいので、以下の様な命名規則で単語間の区切りを表します。
tanka_kosuu // 下線で区切る "スネークケース"
tanka-kosuu // ハイフンで区切る "チェインケース"
tankaKosuu // 単語頭を大文字にする "キャメルケース"
TankaKosuu // 全ての単語頭を大文字にする "パスカルケース"
TANKA_KOSUU // 全て大文字で下線で区切る "アッパーケース"

C言語の構造体の場合ですが、
慣習として スネークケース または キャメルケース あたりが一般的な様です。

というわけで質問の回答としては、
そのプログラムを作った人の命名規則が、構造体名はスネークケースだった。

ちなみに、
言語仕様的に使えないチェインケースを除いて、どの命名規則でもコンパイルエラーになることはありません。
ただし、コンパイラーとは別に静的コード解析をしている場合は、変な命名だと警告されるかもしれません。


以下参考です。

慣習の由来はたぶんこれ
http://www.amazon.co.jp/dp/4320026926

命名規則が パスカルケース または アッパーケース の例
https://msdn.microsoft.com/ja-jp/library/kbt00480.aspx

言語によっては、この手の命名規則についての公式ガイドラインがあります
http://www.oracle.com/technetwork/java/codeconventions-135099.html
https://msdn.microsoft.com/ja-jp/library/ms229043.aspx

変数や型などの識別名は、プログラム言語仕様により使用できる文字が制限されています。

多くの言語で空白文字は認められないので、複数の英単語からなる名前をつづる場合に
tankakosuu
では読みづらいので、以下の様な命名規則で単語間の区切りを表します。
tanka_kosuu // 下線で区切る "スネークケース"
tanka-kosuu // ハイフンで区切る "チェインケース"
tankaKosuu // 単語頭を大文字にする "キャメルケース"
TankaKosuu // 全ての単語頭を大文字にする "パスカルケース"
TANKA_KOSUU // 全て大文字で下線...続きを読む

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

このQ&Aを見た人が検索しているワード


人気Q&Aランキング

おすすめ情報