Jagdish
Jagdish

Reputation: 1922

malloc return typecasting confusion

I was going through here and found that malloc can cause unwanted behaviour if we don't include stdlib.h, cast the return value and if pointer and integer size differs on the system.

Below is the code snippet given in that SO question. This was tried on 64 bit machine where pointer and integer size are different.

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

If we don't include stdlib.h, compiler will assume malloc return type as int, and casting it and assigning to different size pointer can cause unwanted behaviour. But my question is why casting int to int* and assigning it to different size pointer can cause the problem.

Upvotes: -1

Views: 114

Answers (2)

Keith Thompson
Keith Thompson

Reputation: 263577

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

Under C99 and C2011 rules, the call to malloc with no visible declaration is a constraint violation, meaning that a conforming compiler must issue a diagnostic. (This is about as close as C comes to saying that something is "illegal".) If your compiler doesn't warn about the call, you should find out what options to use to make it do so.

Under C90 rules, calling a function with no visible declaration causes the compiler to assume that the function actually returns a result of type int. Since malloc is actually defined with a return type of void*, the behavior is undefined; the compiler is not required to diagnose it, but the standard says exactly nothing about what happens when the call is evaluated.

What typically happens in practice is that the compiler generates code as if malloc were defined to return an int result. For example, malloc might put its 64-bit void* result in some particular CPU register, and the calling code might assume that that register contains a 32-bit int. (This is not a type conversion; it's just bad code that incorrectly treats a value of one type as if it were of a different type.) That (possibly garbage) int value is then converted to int* and stored in p. You might lose the high-order or low-order 32 bits of the returned pointer -- but that's only one out of arbitrarily many ways it can go wrong.

Or malloc might push its 64-bit result onto the stack, and the caller might pop only 32 bits off the stack, resulting in a stack misalignment that will cause all subsequent execution to be incorrect. For historical reasons, C compiler typically don't use this kind of calling convention, but the standard permits it.

If int, void*, and int* all happen to be the same size (as they often are on 32-bit systems), the code is likely to work -- but even that's not guaranteed. For example, a calling convention might use one register to return int results and a different one to return pointer results. Again, most existing C calling conventions allow for old bad code that makes assumptions like this.

Calling malloc requires #include <stdlib.h>, even though some compilers might not enforce that requirement. It's much easier to add the #include (and drop the cast) than to spend time thinking about what might happen if you don't.

Upvotes: 5

Iharob Al Asimi
Iharob Al Asimi

Reputation: 53016

Almost, any function causes undefined behavior if no prototype is given before calling it, except for example if it's prototype is int function(int x);.

It's pretty obvious that if the size of a pointer is larger than the size of an int and malloc() returns an int because of implicit declaration, then the returned address might not be the real address because for example, it might not be possible to represent it with less bits.

Dereferencing it would be undefined behavior, which by the way, you can't test for, since it's undefined, what would you expect to happen? it's undefined!!!

So, nothing to test there?

Upvotes: 1

Related Questions