このコードが64ビットアーキテクチャでsegfaultになるのに、32ビットでは正常に機能するのはなぜですか?


112

次のCパズルを見つけました。

Q:IA-64では次のプログラムがsegfaultするのに、IA-32では正常に動作するのはなぜですか?

  int main()
  {
      int* p;
      p = (int*)malloc(sizeof(int));
      *p = 10;
      return 0;
  }

int64ビットマシンののサイズは、ポインタのサイズと同じではない可能性があることを知っています(int32ビットであり、ポインタは64ビットである可能性があります)。しかし、これが上記のプログラムとどのように関連しているかはわかりません。何か案は?


50
stdlib.h含まれていないような愚かなことですか?
user786653 '09 / 09/25

3
このコードは、私の64ビットマシンで正常に動作します。次の場合、警告なしでコンパイルされます#include stdlib.h(mallocの場合)
mpenkov

1
ああ!@ user786653は重要な部分を釘付けにしました。では#include <stdlib.h>、完全に見つかりますが、それは問題ではありません。

8
@delnan-そのように動作する必要はありませんがsizeof(int) == sizeof(int*)、たとえば、int使用されている呼び出し規約でs への異なるレジスターを通じてポインターが返された場合、プラットフォームで合法的に失敗する可能性があります。
フレキソ

7
C99環境では、コンパイラはの暗黙の宣言について少なくとも警告を表示しているはずですmalloc()。GCCは言う:warning: incompatible implicit declaration of built-in function 'malloc'あまりにも。
ジョナサンレフラー、2011

回答:


130

へのキャストint*は、適切ななし#includeの戻り型がであるmallocと見なされるという事実をマスクしますint。IA-64はたまたまsizeof(int) < sizeof(int*)この問題を明らかにしています。

(未定義の動作のsizeof(int)==sizeof(int*)ため、たとえば、呼び出し規約が整数以外のポインターを返すために異なるレジスターを使用した場合など、trueを保持するプラットフォームでも失敗する可能性があることに注意してください)

comp.lang.cよくあるご質問は、議論のエントリがあるから復帰キャスティング理由をmalloc必要としない、潜在的に悪いんが


5
適切な#includeがなければ、mallocの戻り値の型がintであると想定されるのはなぜですか?
user7 '09 / 09/25

11
@WTP-これは、常にnewC ++で使用し、常にC ++コンパイラではなくCコンパイラでCをコンパイルするのに適した理由です。
フレキソ

6
@ user7-それがルールです。戻り値の型は、int不明な場合とみなされます
Flexo

2
@vlad- この理由から、暗黙の宣言に依存するのではなく、常に関数宣言することをお勧めします。(そしてからのリターンをキャストしないmalloc
フレキソ

16
@ user7:「32ビットのメモリを指すポインタp(サイズ64)があります」-間違っています。mallocによって割り当てられたブロックのアドレスは、の呼び出し規約に従って返されましたvoid*。しかし、呼び出し元のコードは、関数が戻ると考えているためint(別の方法で指示しないように選択したため)、の呼び出し規約に従って戻り値を読み取ろうとしますint。したがって必ずしも割り当てられたメモリを指すとpは限りませ。an intとa void*は同じサイズであり、同じように返されるため、IA32でもたまたま動作しました。IA64では、誤った値を取得します。
Steve Jessop

33

最も可能性が高いのは、ヘッダーファイルを含めないためmallocであり、コンパイラーは通常これを警告しますが、戻り値を明示的にキャストしているという事実は、何をしているのかを知っていることを意味します。

つまり、コンパイラはintが返されることを期待し、そこからmallocポインタにキャストします。サイズが異なると、悲しみの原因になります。

これが、Cで戻り値をキャストしない理由ですmallocvoid*戻り値は、暗黙的に正しい型のポインターに変換されます(ヘッダーを含めていない場合は、安全でない可能性のあるint-について警告している可能性があります)ポインターへの変換)。


素朴に聞こえて申し訳ありませんが、私は常にmallocが適切な型にキャストできるvoidポインターを返すと想定していました。私はCプログラマではないので、もう少し詳しく教えてください。
user7 '09 / 09/25

5
@ user7:#include <stdlib.h>がない場合、Cコンパイラはmallocの戻り値がintであると想定します。
sashang 2011

4
@ user7:voidポインターキャストできますが、void *暗黙的に他のポインター型に変換できるため、Cでは必要ありません。int *p = malloc(sizeof(int))適切なプロトタイプがスコープ内にある場合は機能し、そうでない場合は失敗します(結果はと見なされるためint)。キャストを使用すると、両方がコンパイルされ、後者の場合はエラーになりsizeof(int) != sizeof(void *)ます。

2
@ user7ただし、を含めない場合stdlib.h、コンパイラはmallocその戻り値の型も認識しません。そのためint、デフォルトとして想定されています。
クリスチャンラウ

10

これが、欠落したプロトタイプに関する警告なしにコンパイルしない理由です。

これが、Cでmallocの戻り値をキャストしない理由です。

キャストはC ++互換性のために必要です。それを省略する理由はほとんどありません(ここでは理由はありません)。

C ++互換性は必ずしも必要ではなく、場合によってはまったく不可能である場合もありますが、ほとんどの場合、非常に簡単に実現できます。


22
私のCコードがC ++と「互換性がある」かどうか、いったいどうして気になるのですか?perl、java、またはEiffelと互換性があるかどうかは気にしません...
Stephen Canon

4
誰かがあなたのCコードを見て行かないことを保証するなら、それはうまくいくはずなので、私はC ++コンパイラーでコンパイルするつもりです!
Steven Lu

3
これは、ほとんどのCコードを簡単にC ++互換にすることができるためです。
curiousguy
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.