Why does this code segfault on 64-bit architecture but work fine on 32-bit?
Solution 1
The cast to int*
masks the fact that without the proper #include
the return type of malloc
is assumed to be int
. IA-64 happens to have sizeof(int) < sizeof(int*)
which makes this problem obvious.
(Note also that because of the undefined behaviour it could still fail even on a platform where sizeof(int)==sizeof(int*)
holds true, for example if the calling convention used different registers for returning pointers than integers)
The comp.lang.c FAQ has an entry discussing why casting the return from malloc
is never needed and potentially bad.
Solution 2
Most likely because you're not including the header file for malloc
and, while the compiler would normally warn you of this, the fact that you're explicitly casting the return value means you're telling it you know what you're doing.
That means the compiler expects an int
to be returned from malloc
which it then casts to a pointer. If they're different sizes, that's going to cause you grief.
This is why you never cast the malloc
return in C. The void*
that it returns will be implicitly converted to a pointer of the correct type (unless you haven't included the header in which case it probably would have warned you of the potentially unsafe int-to-pointer conversion).
Solution 3
This is why you never compile without warnings about missing prototypes.
This is why you never cast the malloc return in C.
The cast is needed for C++ compatibility. There is little reason (read: no reason here) to omit it.
C++ compatibility is not always needed, and in a few cases not possible at all, but in most cases it is very easily achieved.
user7
Updated on June 09, 2022Comments
-
user7 almost 2 years
I came across the following C puzzle:
Q: Why does the following program segfault on IA-64, but work fine on IA-32?
int main() { int* p; p = (int*)malloc(sizeof(int)); *p = 10; return 0; }
I know that the size of
int
on a 64 bit machine may not be the same as the size of a pointer (int
could be 32 bits and pointer could be 64 bits). But I am not sure how this relates to the above program. Any ideas? -
Admin over 12 yearsNote that the cast isn't needed in C, but it is needed in C++. Anyway, this question was about C so +1.
-
user7 over 12 yearswithout the proper #include, why is the return type of malloc assumed to be an int?
-
Flexo over 12 years@WTP - which is a good reason to always use
new
in C++ and always compile C with a C compiler and not a C++ compiler. -
Flexo over 12 years@user7 - that's the rules. Any return type is assumed to be
int
if it's not known -
Vlad over 12 years@user7: if it assumes for you the return type, is there a better idea?
int
is as arbitrary as everything other. -
chacham15 over 12 years@WTP depends on the standard though.
-
user7 over 12 yearssorry for sounding naive, but I always assumed that malloc returns a void pointer which can be cast to an appropriate type. I am not a C programmer and hence would appreciate a little more detail.
-
Flexo over 12 years@vlad - the better idea is to always declare functions rather than rely upon implicit declarations for exactly this reason. (And not cast the return from
malloc
) -
Vlad over 12 years@awoodland: I meant, a better idea for the compiler to determine the return type if it must guess.
-
Steve Jessop over 12 years@awoodland: C++ doesn't guess the function signature, so the cast in C++ doesn't cause the same harm as in C. I agree that C shouldn't be compiled with a C++ compiler, but this is not one of the reasons for that.
-
user7 over 12 years@awoodland : At the bit level, what happens when an int is cast to an int*? in this case, we have a pointer p (of size 64) which is pointing to 32 bits of memory (allocated by malloc). What happens when *p = 10 is executed?
-
sashang over 12 years@user7: without the #include <stdlib.h> the C compiler assumes the return value of malloc is an int.
-
Admin over 12 years@user7: The void pointer can be cast, but it's not needed in C as
void *
can be converted to any other pointer type implicitly.int *p = malloc(sizeof(int))
works if the proper prototype is in scope and fails if it isn't (because then the result is assumed to beint
). With the cast, both would compile and the latter would result in errors whensizeof(int) != sizeof(void *)
. -
Christian Rau over 12 years@user7 But if you not include
stdlib.h
, the compiler doesn't knowmalloc
and neither its return type. So it just assumesint
as default. -
Steve Jessop over 12 years@user7: "we have a pointer p (of size 64) which is pointing to 32 bits of memory" - wrong. The address of the block allocated by malloc was returned according to the calling convention for a
void*
. But the calling code thinks the function returnsint
(since you opted not to tell it otherwise), so it tries to read the return value according to the calling convention for anint
. Hencep
does not necessarily point to the allocated memory. It just so happened to work for IA32 because anint
and avoid*
are the same size, and returned in the same way. On IA64 you get the wrong value. -
Stephen Canon over 12 yearsWhy on earth would I care if my C code is "compatible" with C++? I don't care if it's compatible with perl or java or Eiffel or ...
-
Steven Lu over 12 yearsIf you guarantee somebody down the line isn't going to look at your C code and go, hey I'm going to compile it with a C++ compiler because that should work!
-
curiousguy over 12 yearsThat's cause most C code can be trivially made C++ compatible.
-
ViniciusArruda over 8 years@user7 For the return convention of type
int
, see here