Why does this code segfault on 64-bit architecture but work fine on 32-bit?

15,244

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.

Share:
15,244
user7
Author by

user7

Updated on June 09, 2022

Comments

  • user7
    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
    Admin over 12 years
    Note that the cast isn't needed in C, but it is needed in C++. Anyway, this question was about C so +1.
  • user7
    user7 over 12 years
    without the proper #include, why is the return type of malloc assumed to be an int?
  • Flexo
    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
    Flexo over 12 years
    @user7 - that's the rules. Any return type is assumed to be int if it's not known
  • Vlad
    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
    chacham15 over 12 years
    @WTP depends on the standard though.
  • user7
    user7 over 12 years
    sorry 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
    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
    Vlad over 12 years
    @awoodland: I meant, a better idea for the compiler to determine the return type if it must guess.
  • Steve Jessop
    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
    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
    sashang over 12 years
    @user7: without the #include <stdlib.h> the C compiler assumes the return value of malloc is an int.
  • Admin
    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 be int). With the cast, both would compile and the latter would result in errors when sizeof(int) != sizeof(void *).
  • Christian Rau
    Christian Rau over 12 years
    @user7 But if you not include stdlib.h, the compiler doesn't know malloc and neither its return type. So it just assumes int as default.
  • Steve Jessop
    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 returns int (since you opted not to tell it otherwise), so it tries to read the return value according to the calling convention for an int. Hence p does not necessarily point to the allocated memory. It just so happened to work for IA32 because an int and a void* are the same size, and returned in the same way. On IA64 you get the wrong value.
  • Stephen Canon
    Stephen Canon over 12 years
    Why 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
    Steven Lu over 12 years
    If 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
    curiousguy over 12 years
    That's cause most C code can be trivially made C++ compatible.
  • ViniciusArruda
    ViniciusArruda over 8 years
    @user7 For the return convention of type int, see here