93

Now before people start marking this a dup, I've read all the following, none of which provide the answer I'm looking for:

  1. C FAQ: What's wrong with casting malloc's return value?
  2. SO: Should I explicitly cast malloc()’s return value?
  3. SO: Needless pointer-casts in C
  4. SO: Do I cast the result of malloc?

Both the C FAQ and many answers to the above questions cite a mysterious error that casting malloc's return value can hide; however, none of them give a specific example of such an error in practice. Now pay attention that I said error, not warning.

Now given the following code:

#include <string.h>
#include <stdio.h>
// #include <stdlib.h>

int main(int argc, char** argv) {

    char * p = /*(char*)*/malloc(10);
    strcpy(p, "hello");
    printf("%s\n", p);

    return 0;
}

Compiling the above code with gcc 4.2, with and without the cast gives the same warnings, and the program executes properly and provides the same results in both cases.

anon@anon:~/$ gcc -Wextra nostdlib_malloc.c -o nostdlib_malloc
nostdlib_malloc.c: In function ‘main’:
nostdlib_malloc.c:7: warning: incompatible implicit declaration of built-in function ‘malloc’
anon@anon:~/$ ./nostdlib_malloc 
hello

So can anyone give a specific code example of a compile or runtime error that could occur because of casting malloc's return value, or is this just an urban legend?

Edit I've come across two well written arguments regarding this issue:

  1. In Favor of Casting: CERT Advisory: Immediately cast the result of a memory allocation function call into a pointer to the allocated type
  2. Against Casting (404 error as of 2012-02-14: use the Internet Archive Wayback Machine copy from 2010-01-27.{2016-03-18:"Page cannot be crawled or displayed due to robots.txt."})
Community
  • 1
  • 1
Robert S. Barnes
  • 39,711
  • 30
  • 131
  • 179
  • 7
    casting `void` pointers allows to compile the code as C++; some people say that's a feature, I'd say it's a bug ;) – Christoph Oct 14 '09 at 17:29
  • 1
    also, read the comments to the first of your links as it describes what you should do instead of casting: https://www.securecoding.cert.org/confluence/display/seccode/MEM02-C.+Immediately+cast+the+result+of+a+memory+allocation+function+call+into+a+pointer+to+the+allocated+type?focusedCommentId=14811415#comment-14811415 – Christoph Oct 14 '09 at 17:41
  • 3
    I'll take CERTs advice for including the cast. Also, I wont forget to include stdlib.h, ever. :) – Abhinav May 20 '12 at 18:29
  • [This is wrong, specifically](http://stackoverflow.com/a/605858/2628863) – 0xF1 Apr 28 '14 at 10:01
  • The CERT advisory is absolutely correct. – JonS Mar 17 '15 at 02:20
  • I would ask for examples which produce dangerous code in spite of compiling with strict settings. Anyone who compiles without flagging undefined functions (etc.) is begging for all sorts of trouble. I'm a bit agnostic on this question, but I've sometimes cast just to shut up MSVS's IntelliNonSense. – PJTraill May 05 '15 at 22:17
  • I've a clarified [my answer](http://stackoverflow.com/a/605858/28169) to item 4. – unwind Sep 30 '15 at 14:12
  • 1
    [Here is a SO-example](http://stackoverflow.com/q/7545365) of a compile runtime error because of casting `malloc`'s return value: casting to `int*` on 64-bit arch. – John_West Dec 28 '15 at 18:02
  • 1
    this question is tagged `C` not `C++` (they are two different languages) So any discussion (as in some of the answers) is not relevant to this question. – user3629249 Mar 09 '17 at 18:15
  • `casting` of the heap allocation functions returned value just clutters the code, making it more difficult to understand, debug, maintain. – user3629249 Mar 09 '17 at 18:17

7 Answers7

67

You won't get a compiler error, but a compiler warning. As the sources you cite say (especially the first one), you can get an unpredictable runtime error when using the cast without including stdlib.h.

So the error on your side is not the cast, but forgetting to include stdlib.h. Compilers may assume that malloc is a function returning int, therefore converting the void* pointer actually returned by malloc to int and then to your pointer type due to the explicit cast. On some platforms, int and pointers may take up different numbers of bytes, so the type conversions may lead to data corruption.

Fortunately, modern compilers give warnings that point to your actual error. See the gcc output you supplied: It warns you that the implicit declaration (int malloc(int)) is incompatible to the built-in malloc. So gcc seems to know malloc even without stdlib.h.

Leaving out the cast to prevent this error is mostly the same reasoning as writing

if (0 == my_var)

instead of

if (my_var == 0)

since the latter could lead to a serious bug if one would confuse = and ==, whereas the first one would lead to a compile error. I personally prefer the latter style since it better reflects my intention and I don't tend to do this mistake.

The same is true for casting the value returned by malloc: I prefer being explicit in programming and I generally double-check to include the header files for all functions I use.

Lii
  • 11,553
  • 8
  • 64
  • 88
Ferdinand Beyer
  • 64,979
  • 15
  • 154
  • 145
  • 2
    It would seem that since the compiler warns about the incompatible implicit declaration, then this is a non-issue as long as you pay attention to your compiler warnings. – Robert S. Barnes Oct 14 '09 at 12:11
  • This is maybe a completely separate question, but from an implementation point of view it doesn't seem to make sense that the compiler would link to the correct object code definition of `malloc`, yet convert it's return value to int and then convert it to T*. How is it that the implicit declaration actually generates object code? – Robert S. Barnes Oct 14 '09 at 12:17
  • 4
    @Robert: yes, given certain assumptions about the compiler. When people are giving advice for how best to write C *in general*, they can't assume that the person receiving the advice is using a recent version of gcc. – Steve Jessop Oct 14 '09 at 12:19
  • 4
    Oh, and the answer to the second question is that the caller contains the code to pick up the return value (which it thinks is an int), and convert it to T*. The callee just writes the return value (as a void*) and returns. So depending on the calling convention: int returns and void* returns may or may not be in the "same place" (register or stack slot); int and void* may or may not be the same size; converting between the two may or may not be a no-op. So it might "just work", or the value could be corrupted (some bits lost, perhaps), or the caller could pick up completely the wrong value. – Steve Jessop Oct 15 '09 at 11:20
  • @Ferdinand Beyer: See this program: http://ideone.com/91tN6C. I've tested it & it works fine on gcc 4.7.1,4.8.1, 4.9.2 & MSVS 2010. so is it the reason that pointer & integers are same on my platform so program works fine on various version of compilers? – Destructor Jun 23 '15 at 12:19
  • @PravasiMeet You probably have `stdlib.h` included by including `stdio.h`, so you don't run into this issue. In addition, on the same machine, all compilers will most likely (or certainly?) have the same sizes for pointers and integers. It might (will?) also depend on whether you create a 32-bit or 64-bit executable. – Ferdinand Beyer Jun 23 '15 at 18:44
  • 1
    @RobertS.Barnes late to the party, but: The return value is generally not part of the function signature, not even in C++. The linker just generates a jump to a symbol, that's all. – Peter - Reinstate Monica Nov 19 '15 at 16:58
  • 3
    _You can get an unpredictable runtime error when using the cast without including stdlib.h_. That's true, but not including `stdlib.h` is already an error by itself, even if you only get "implicite declaration" warnings. – Jabberwocky Apr 14 '16 at 17:55
46

One of the good higher-level arguments against casting the result of malloc is often left unmentioned, even though, in my opinion, it is more important than the well-known lower-level issues (like truncating the pointer when the declaration is missing).

A good programming practice is to write code, which is as type-independent as possible. This means, in particular, that type names should be mentioned in the code as little as possible or best not mentioned at all. This applies to casts (avoid unnecessary casts), types as arguments of sizeof (avoid using type names in sizeof) and, generally, all other references to type names.

Type names belong in declarations. As much as possible, type names should be restricted to declarations and only to declarations.

From this point of view, this bit of code is bad

int *p;
...
p = (int*) malloc(n * sizeof(int));

and this is much better

int *p;
...
p = malloc(n * sizeof *p);

not simply because it "doesn't cast the result of malloc", but rather because it is type-independent (or type-agnositic, if you prefer), because it automatically adjusts itself to whatever type p is declared with, without requiring any intervention from the user.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • Fwiw, I think this is more or less the same reason as this: http://stackoverflow.com/questions/953112/should-i-explicitly-cast-mallocs-return-value/954785#954785 but focused on the type-independence rather than the DIY. Of course the first follows from the second (or vice versa), so at least it's mentioned *sometimes*. :) – unwind Oct 16 '09 at 08:09
  • 5
    @unwind you most likely mean **DRY** rather than **DIY** – kratenko Jun 15 '12 at 17:03
18

Non-prototyped functions are assumed to return int.

So you're casting an int to a pointer. If pointers are wider than ints on your platform, this is highly risky behavior.

Plus, of course, that some people consider warnings to be errors, i.e. code should compile without them.

Personally, I think the fact that you don't need to cast void * to another pointer type is a feature in C, and consider code that does to be broken.

unwind
  • 391,730
  • 64
  • 469
  • 606
  • 14
    I have this belief that the compiler knows more about the language than I do, so if it warns me about something, I pay attention. – György Andrasek Oct 14 '09 at 10:43
  • 3
    In many projects, C code is compiled as C++ where you do need to cast the `void*`. – laalto Oct 14 '09 at 11:02
  • nit: "_by default_, non-prototyped functions are assumed to return `int`." -- Do you mean it is possible to change the return type of non-prototyped functions? – pmg Oct 14 '09 at 11:40
  • @pmg: of course, you can provide your own definitions of functions. :) – Michael Foukarakis Oct 14 '09 at 12:02
  • 1
    @laalto - It is, but it shouldn't be. C is C, not C++, and should be compiled with a C compiler, not a C++ compiler. There's no excuse: GCC (one of the best C compilers out there) runs on nearly every platform imaginable (and generates highly optimized code, too). What reasons could you possibly have to compile C with a C++ compiler, other than laziness and loose standards? – Chris Lutz Oct 14 '09 at 18:53
  • dont you know c&c++ mixed programming? – Test Oct 15 '09 at 01:52
  • @EffoStaff: Mixing in the same translation unit is a Bad Idea, in my opinion. What Chris said, basically. – unwind Oct 15 '09 at 06:51
  • @EffoStaff: Don't you know `extern "C"`? – Ferdinand Beyer Oct 15 '09 at 07:22
  • 3
    Example of code which you might want to compile as both C and C++: `#ifdef __cplusplus \nextern "C" { \n#endif static inline uint16_t swb(uint16_t a) {return ((a << 8) | ((a >> 8) & 0xFF); } \n#ifdef __cplusplus\n } \n#endif`. Now, why you'd want to call malloc in a static inline function I really don't know, but headers which work in both are hardly unheard of. – Steve Jessop Oct 15 '09 at 16:20
  • It is crazy not to compile with warnings for undeclared functions; do that and @unwind's argument evaporates. – PJTraill May 05 '15 at 22:23
  • @unwind: While the divergence of C and C++ makes such things more difficult than they should be, I have at times found it useful to have a program that compiles as C code on an embedded platform and as C++ code on the PC. A statement like `WOOZLE_OUT = 1;` writes to a hardware register on the embedded platform, and uses an assignment operator to call a suitable emulation routine on the PC. – supercat Jul 31 '15 at 23:22
  • @unwind: In C++, it's possible to define a type which will, regardless of the size of `int`, behave the way `uint16_t` works on platforms where `int` is 16 bits. In C, there is presently no way to specify such a type. – supercat Jul 31 '15 at 23:24
11

If you do this when compiling in 64-bit mode, your returned pointer will be truncated to 32-bits.

EDIT: Sorry for being too brief. Here's an example code fragment for discussion purposes.

main()
{
   char * c = (char *)malloc(2) ;
   printf("%p", c) ;
}

Suppose that the returned heap pointer is something bigger than what is representable in an int, say 0xAB00000000.

If malloc is not prototyped to return a pointer, the int value returned will initially be in some register with all the significant bits set. Now the compiler say, "okay, how do I convert and int to a pointer". That's going to be either a sign extension or zero extension of the low order 32-bits that it has been told malloc "returns" by omitting the prototype. Since int is signed I think the conversion will be sign extension, which will in this case convert the value to zero. With a return value of 0xABF0000000 you'll get a non-zero pointer that will also cause some fun when you try to dereference it.

Peeter Joot
  • 7,848
  • 7
  • 48
  • 82
  • 1
    Could you explain in detail how this would occur? – Robert S. Barnes Oct 14 '09 at 12:00
  • 5
    i think Peeter Joot was figuring out that "By default, non-prototyped functions are assumed to return int" w/o including stdlib.h, and sizeof(int) is 32 bits while sizeof(ptr) is 64. – Test Oct 14 '09 at 12:32
4

A Reusable Software Rule:

In the case of writing an inline function in which used malloc(), in order to make it reusable for C++ code too, please do an explicit type casting (e.g. (char*)); otherwise compiler will complain.

Test
  • 1,697
  • 1
  • 11
  • 10
  • hopefully, with the (recent) inclusion of link-time optimizations in gcc (see http://gcc.gnu.org/ml/gcc/2009-10/msg00060.html ), declaring inline-functions in header files will no longer be necessary – Christoph Oct 14 '09 at 19:07
  • you got bad ideas. do you aware of that what is portable and cross-platform among different compilers/versions/architectures? ok, you may not. then what does reusable mean? – Test Oct 15 '09 at 01:47
  • 2
    when writing C++, malloc/free is NOT the right methodology. Rather use new/delete. I.E. there should be no/nada/zero calls to malloc/free in C++ code – user3629249 May 18 '15 at 14:14
  • 3
    @user3629249: When writing a function which needs to be usable from within *either* C code or C++ code, using `malloc`/`free` for both is apt to be better than trying to use `malloc` in C and `new` in C++, especially if data structures are shared between C and C++ code and there's a possibility that an object might get created in C code and released in C++ code or vice versa. – supercat Mar 08 '17 at 23:59
3

A void pointer in C can be assigned to any pointer without an explicit cast. The compiler will give warning but it can be reusable in C++ by type casting malloc() to corresponding type. With out type casting also it can be use in C, because C is no strict type checking. But C++ is strictly type checking so it is needed to type cast malloc() in C++.

Yogeesh H T
  • 2,777
  • 21
  • 18
0

The malloc() function could often require a conversion cast before. For the returned type from malloc it is a pointer to void and not a particular type, like may be a char* array, or a string. And sometimes the compiler could not know, how to convert this type.

int size = 10; 
char* pWord = (char*)malloc(size); 

The allocation functions are available for all C packages. So, these are general functions, that must work for more C types. And the C++ libraries are extensions of the older C libraries. Therefore the malloc function returns a generic void* pointer.

Cannot allocate an object of a type with another of different type. Unless the objects are not classes derived from a common root class. And not always this is possible, there are different exceptions. Therefore a conversion cast might be necessary in this case.

Maybe the modern compilers know how to convert different types. So this could not be a great issue, when is doing this conversion. But a correct cast can be used, if a type conversion is possible. As an example: it cannot be cast "apples" to "strawberries". But these both, so called "classes", can be converted to "fruits".

There are custom structure types, which cannot be cast directly. In this case, any member variable has to be assigned separately. Or a custom object would have to set its members independently. Either if it is about a custom object class, or whatever else...

Also a cast to a void pointer must be used when using a free call. This is because the argument of the free function is a void pointer.

free((void*)pWord); 

Casts are not bad, they just don't work for all the variable types. A conversion cast is also an operator function, that must be defined. If this operator is not defined for a certain type, it may not work. But not all the errors are because of this conversion cast operator.

With kind regards, Adrian Brinas

Adrian
  • 60
  • 2