Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Needless pointer-casts in C

Tags:

c++

c

pointers

I got a comment to my answer on this thread:

Malloc inside a function call appears to be getting freed on return?

In short I had code like this:

int * somefunc (void)
{
  int * temp = (int*) malloc (sizeof (int));
  temp[0] = 0;
  return temp;
}

I got this comment:

Can I just say, please don't cast the return value of malloc? It is not required and can hide errors.

I agree that the cast is not required in C. It is mandatory in C++, so I usually add them just in case I have to port the code in C++ one day.

However, I wonder how casts like this can hide errors. Any ideas?

Edit:

Seems like there are very good and valid arguments on both sides. Thanks for posting, folks.

like image 294
Nils Pipenbrinck Avatar asked Sep 20 '08 17:09

Nils Pipenbrinck


People also ask

Can you cast a pointer in C?

In the C language, casting is a construct to view a data object temporarily as another data type. When you cast pointers, especially for non-data object pointers, consider the following characteristics and constraints: You can cast a pointer to another pointer of the same IBM® i pointer type.

What happens if you don't cast malloc?

In C, you don't need to cast the return value of malloc . The pointer to void returned by malloc is automagically converted to the correct type. However, if you want your code to compile with a C++ compiler, a cast is needed.

Why is typecasting required in malloc in C?

malloc return a generic type pointer. Because generic pointer is of type void (void *p,p is generic pointer). Void pointer can hold any type of data so it is needed to typecast it.

Can you cast a pointer to an int?

The most general answer is – in no way. In 64-bit programs, the size of the pointer is 64 bits, and cannot be put into the int type, which remains 32-bit in almost all systems. The only exception is exotic systems with the SILP64 data model, where the size of int is also 64 bits.


3 Answers

It seems fitting I post an answer, since I left the comment :P

Basically, if you forget to include stdlib.h the compiler will assume malloc returns an int. Without casting, you will get a warning. With casting you won't.

So by casting you get nothing, and run the risk of suppressing legitimate warnings.

Much is written about this, a quick google search will turn up more detailed explanations.

edit

It has been argued that

TYPE * p;
p = (TYPE *)malloc(n*sizeof(TYPE));

makes it obvious when you accidentally don't allocate enough memory because say, you thought p was TYPe not TYPE, and thus we should cast malloc because the advantage of this method overrides the smaller cost of accidentally suppressing compiler warnings.

I would like to point out 2 things:

  1. you should write p = malloc(sizeof(*p)*n); to always ensure you malloc the right amount of space
  2. with the above approach, you need to make changes in 3 places if you ever change the type of p: once in the declaration, once in the malloc, and once in the cast.

In short, I still personally believe there is no need for casting the return value of malloc and it is certainly not best practice.

like image 154
freespace Avatar answered Oct 17 '22 12:10

freespace


This question is tagged both for C and C++, so it has at least two answers, IMHO:

C

Ahem... Do whatever you want.

I believe the reason given above "If you don't include "stdlib" then you won't get a warning" is not a valid one because one should not rely on this kind of hacks to not forget to include an header.

The real reason that could make you not write the cast is that the C compiler already silently cast a void * into whatever pointer type you want, and so, doing it yourself is overkill and useless.

If you want to have type safety, you can either switch to C++ or write your own wrapper function, like:

int * malloc_Int(size_t p_iSize) /* number of ints wanted */
{
   return malloc(sizeof(int) * p_iSize) ;
}

C++

Sometimes, even in C++, you have to make profit of the malloc/realloc/free utils. Then you'll have to cast. But you already knew that. Using static_cast<>() will be better, as always, than C-style cast.

And in C, you could override malloc (and realloc, etc.) through templates to achieve type-safety:

template <typename T>
T * myMalloc(const size_t p_iSize)
{
 return static_cast<T *>(malloc(sizeof(T) * p_iSize)) ;
}

Which would be used like:

int * p = myMalloc<int>(25) ;
free(p) ;

MyStruct * p2 = myMalloc<MyStruct>(12) ;
free(p2) ;

and the following code:

// error: cannot convert ‘int*’ to ‘short int*’ in initialization
short * p = myMalloc<int>(25) ;
free(p) ;

won't compile, so, no problemo.

All in all, in pure C++, you now have no excuse if someone finds more than one C malloc inside your code... :-)

C + C++ crossover

Sometimes, you want to produce code that will compile both in C and in C++ (for whatever reasons... Isn't it the point of the C++ extern "C" {} block?). In this case, C++ demands the cast, but C won't understand the static_cast keyword, so the solution is the C-style cast (which is still legal in C++ for exactly this kind of reasons).

Note that even with writing pure C code, compiling it with a C++ compiler will get you a lot more warnings and errors (for example attempting to use a function without declaring it first won't compile, unlike the error mentioned above).

So, to be on the safe side, write code that will compile cleanly in C++, study and correct the warnings, and then use the C compiler to produce the final binary. This means, again, write the cast, in a C-style cast.

like image 27
paercebal Avatar answered Oct 17 '22 13:10

paercebal


One possible error it can introduce is if you are compiling on a 64-bit system using C (not C++).

Basically, if you forget to include stdlib.h, the default int rule will apply. Thus the compiler will happily assume that malloc has the prototype of int malloc(); On Many 64-bit systems an int is 32-bits and a pointer is 64-bits.

Uh oh, the value gets truncated and you only get the lower 32-bits of the pointer! Now if you cast the return value of malloc, this error is hidden by the cast. But if you don't you will get an error (something to the nature of "cannot convert int to T *").

This does not apply to C++ of course for 2 reasons. Firstly, it has no default int rule, secondly it requires the cast.

All in all though, you should just new in c++ code anyway :-P.

like image 7
Evan Teran Avatar answered Oct 17 '22 12:10

Evan Teran