C言語関係掲示板

過去ログ

No.955 malloc関数が返すポインタは、4BYTE境界か?

[戻る] [ホームページ]
No.12224

メモリ取得に関して
投稿者---Gina(2004/01/28 10:02:55)


malloc関数に関して質問があります。
malloc関数が成功したとして、その時返すポインタは、4BYTE境界に
有るのでしょうか?

int* ptr = malloc(sizeof(int));
ptr = 1;

malloc関数が例えば0x00000001等の4BYTE境界にないアドレスを
返した場合には、ptrに値を代入するとおかしな事になりそうです。
コンパイラ、ライブラリ、OS、CPUによるのでしょうか?
それとも、C言語のmalloc関数は必ず4BYTE境界のアドレスを
返すのでしょうか?

malloc関数の説明を見てもこのことに関しては記されているのを見た
ことがありません。


No.12225

Re:メモリ取得に関して
投稿者---Gina(2004/01/28 10:04:28)



>int* ptr = malloc(sizeof(int));
>ptr = 1;

*ptr = 1;
でした。

No.12227

Re:メモリ取得に関して
投稿者---たか(2004/01/28 10:38:54)


4バイト境界になるかどうかすらも、ご使用のマシンとOSとコンパイラに
強く依存すると思いますが、プログラミング言語C第2版(日本語版につい
てはP225、英語版はP154)の8.7-記憶割当てにあるように、malloc()で返
されるメモリ境界は、最も制限の強い型に合わせられます。

構造体についても同様で、2個目以降がバス・エラーを引き起こすような
ポインタ演算はしません。

但しユーザーの側でこのルールを勝手に無視するような事をすれば、多分
バス・エラーが出る事もあるでしょう。



int *ptr = (int *)malloc(sizeof(int) * 10);
(int *)((char *)ptr + 1) = 1;

No.12235

Re:メモリ取得に関して
投稿者---たか(2004/01/28 12:50:17)


>(int *)((char *)ptr + 1) = 1;

うわっこれ
*(int *)((char *)ptr + 1) = 1; の間違いですね。

関係ないですけどx86はアライメントに関してはかなり甘めで、多分過去
との互換性を重視した結果だと思いますが、FPU及びSSE命令でアライメン
ト・チェックを有効にしていないと保護モード例外は発生しません。当然
奇数バウンダリからのデータアクセスは時によっては低速化を招きますの
で、コンパイラオプションでデータ整列が出来るようになっているのが
普通です。コード領域に関してはx86の命令プリフェッチ機構との兼ね合い
もあって64ビットバウンダリになっている事がほとんどです。

しかし一般のCPU(特にRISC)はメモリアクセスを効率的にするために奇数
アドレスやワード境界からのアクセスも強く制限しているのが普通です。
(その方がキャッシュコントローラやメモリ管理ユニットなどのハードウェ
アを単純化できるという理由もあります。)

普通にプログラミングしていればバス・エラーなど滅多にお目にかかるこ
とはないはずですが、ポインタの参照外しを行うとしばしばそんな現象に
も出くわす事があるでしょう。

No.12244

Re:メモリ取得に関して
投稿者---YuO(2004/01/28 15:28:45)


>malloc関数に関して質問があります。
>malloc関数が成功したとして、その時返すポインタは、4BYTE境界に
>有るのでしょうか?

4Byte境界になるとは限りません。


>malloc関数が例えば0x00000001等の4BYTE境界にないアドレスを
>返した場合には、ptrに値を代入するとおかしな事になりそうです。
>コンパイラ、ライブラリ、OS、CPUによるのでしょうか?

なりません。

JIS X 3010-1993 7.10.3 記憶域管理関数
割付けが成功したときに返されるポインタは,適切に境界調整されているため, いかなる型のオブジェクトへのポインタに代入してもよいし, (領域が明示的に開放されるか,又は再割付けされるまで) その割り付けられた領域のオブジェクト又はオブジェクトの配列へのアクセスに使用してもよい。


故に,全てのオブジェクトで確実な境界調整が4Byte単位であれば最低でも4Byte単位の境界に境界調整されるでしょうし,
1Byte単位であるなら境界調整はされていないかもしれません。


No.12256

Re:メモリ取得に関して
投稿者---Gina(2004/01/28 17:16:58)


たか様、YuO様、回答を頂きありがとうございます。
おっしゃっていることが、多少難しいですが、
次のように理解しました。間違いないでしょうか?

malloc関数が返すアドレスは、もっとも強い型に合わせてある。
つまり、どんな型の変数でも代入ができるようになっている。
これは、C言語の規格でありコンパイラ、ライブラリ等の開発
環境で違いがあるわけではない。

最も強い型というのは、CPU、OS、コンパイラ等の環境で
異なる。例えばPC上のWindowsOSでVisualC++、BorlandC++の
環境では、最も強い型はintで、これは4バイトである。
したがって、この場合、malloc関数は成功した場合には4バイト
境界にあるアドレスを必ず返す。


No.12263

Re:メモリ取得に関して
投稿者---たか(2004/01/28 18:13:46)


>malloc関数が返すアドレスは、もっとも強い型に合わせてある。
>つまり、どんな型の変数でも代入ができるようになっている。
>これは、C言語の規格でありコンパイラ、ライブラリ等の開発
>環境で違いがあるわけではない。
>
>最も強い型というのは、CPU、OS、コンパイラ等の環境で
>異なる。例えばPC上のWindowsOSでVisualC++、BorlandC++の
>環境では、最も強い型はintで、これは4バイトである。
>したがって、この場合、malloc関数は成功した場合には4バイト
>境界にあるアドレスを必ず返す。

最も強い型はdoubleです。BCCの場合は8バイトアラインが標準ですが、
-aオプションにより変更可能です。(1バイトアラインでも)
VC7.1も8バイトアライン(/Zp8)が規定値です。やはりBCC同様アラインの
単位を変更できます。これはx86である事による自由度です。

BCCは本当は最も強い型はlong doubleで、10バイトです。しかしこれは
滅多に使われない上、Pentiumのバス幅64ビットとも合致しないので、
事実上doubleが最も強い型として扱われます。VC7.1はsizeof(long double) == sizeof(double) == 8
です。

また、いくつかのデータメンバを持った構造体のsizeof()を取ってみれば、
ある規則性が見えてくると思います。これもx86コンパイラではあやふや
になりがちですので、できればPowerPC、SH4などのバス制限の強いCPU
用のコンパイラで試してみられる事をお勧めします。

No.12265

Re:メモリ取得に関して
投稿者---YuO(2004/01/28 18:14:10)


>最も強い型というのは、CPU、OS、コンパイラ等の環境で
>異なる。例えばPC上のWindowsOSでVisualC++、BorlandC++の
>環境では、最も強い型はintで、これは4バイトである。

一番境界に関する条件が厳しいのは一般にlong doubleまたはlong long intになります。
#上記コンパイラでは真の組み込みlong long int型は存在しない。
32bit版Visual C++において,long doubleはdoubleと等しく8Byteの大きさをもつ型です。
32bit版Borland C++ではlong doubleは10Byteの型になります。

そして,IA-32におけるこれらのデータへの境界要求は8Byteです。
#アライメント・チェック例外が起きるシステムでは,この要求通りに設定する必要がある。
ただし,Windowsにおいてはアライメント・チェック機能を使っていないので,
どのバイトからオブジェクトをおいても,CPUが例外#ACを発生させることはありません。


>したがって、この場合、malloc関数は成功した場合には4バイト
>境界にあるアドレスを必ず返す。

VC++(.Net Framework 1.0 SDK付属ライブラリ)では,16Byte境界に調整されたポインタを返します。
BCC 5.5.1では4Byte境界に調整されたポインタを返します。

Windowsにおいてシステムが要求する境界調整は1Byteですから,どちらにしろ問題はおきません。


No.12267

Re:メモリ取得に関して
投稿者---Gina(2004/01/28 18:32:07)


詳しく教えていただきありがとうございました。
メモリの確保は良く使用しますが、このような
事まで考えていなかったです。

というよりも、malloc関数がきちんと考慮して
いてくれた事で、プログラマが考えなくても良
いようになっているのでしょう。

日常的に使用していたmalloc関数に対して、
ふと、もし戻り値が奇数番地だったりしたら?
って思って質問させていただきました。

どうもありがとうございました。

No.12268

Re:メモリ取得に関して
投稿者---たか(2004/01/28 19:14:24)


>VC++(.Net Framework 1.0 SDK付属ライブラリ)では,16Byte境界に調整されたポインタを返します。
>BCC 5.5.1では4Byte境界に調整されたポインタを返します。

--- BCC5.6.4 ---
(コマンドラインオプション: -a8 または -a)

このオプションを設定すると,文字以外のデータが 64 ビットのワード(8 バイト)境界に配置されます。データが 4 バイトより小さな型サイズの場合には,その型サイズで配置されます。

だそうですから、正確にはdoubleなどの4バイトを超えるデータは8バイト
アライン、4バイト以下のデータは4バイトアラインですね。実験してみた
所すべてのデータは実際には16バイトアラインでした。

--- VC7.1 ---
8バイトアラインでした。

よってVC SDK、BCC5.5.1とは違う結果が得られました。(VC SDK、BCC5.5.1
は試していません)

No.12269

Re:メモリ取得に関して
投稿者---YuO(2004/01/28 20:37:16)


>(コマンドラインオプション: -a8 または -a)
>このオプションを設定すると,文字以外のデータが 64 ビットのワード(8 バイト)境界に配置されます。データが 4 バイトより小さな型サイズの場合には,その型サイズで配置されます。

このコマンドラインオプションはmallocの動作に対して影響を全く与えません。
識別子を持つオブジェクトと,構造体のメンバの配置のみに関して影響を与えます。


No.12270

Re:メモリ取得に関して
投稿者---たか(2004/01/28 21:18:51)


>このコマンドラインオプションはmallocの動作に対して影響を全く与えません。
>識別子を持つオブジェクトと,構造体のメンバの配置のみに関して影響を与えます。

malloc()の動作については影響を与えませんが、sizeof()演算子の返す値
に対して影響を及ぼします。

struct Test {
int i[2]; // 8 bytes
char c; // 1 byte
};

-a1 : 9bytes
-a2 : 10bytes
-a4 : 12bytes