0

Based on this old question malloc returns a pointer to void that it

is automatically and safely promoted to any other pointer type

But reading K&R I've found this following code

char *strdup(char *s)
{
char *p;
/* make a duplicate of s */
p = (char *) malloc(strlen(s)+1)

What is the difference?

Community
  • 1
  • 1
  • 1
    I think `K&R` is an old book, and apparently old compilers didn't do this correctly. – Iharob Al Asimi Feb 11 '15 at 16:52
  • 1
    Very very old versions of C did not have (void*) so a cast was needed to pass "lint". Your compiler NEVER needs the cast. – user3710044 Feb 11 '15 at 16:53
  • 1
    Cast in C++ and not in C. Though the use of `malloc()` would be discouraged over `new` and `delete`. If your compiler is giving you warnings, time to switch a compiler. – initramfs Feb 11 '15 at 16:54
  • 1
    That's from the second edition (since it uses prototypes). The unnecessary cast is a little surprising. – Keith Thompson Feb 11 '15 at 16:55
  • This particular cast isn't needed in ANY version of C, because the return type of malloc() would be either a (void*) or a (char*) so no C messages either way. Exactly what "lint" would do is another matter. (C++ OTOH has redefined (void*) from "pointer to anything" to "pointer to nothing" so it doesn't really count.) – user3710044 Feb 11 '15 at 17:02
  • 1
    @user3710044: As I've stated in my answer, and as iharob said --- it's entirely possible that certain ancient non-compliant compilers had trouble with that. That was *then* though, and *nowadays*, good luck finding a compiler with this problem. – Tim Čas Feb 11 '15 at 17:04
  • 1
    Note that the quote about promotions is not entirely correct. A `void *` is automatically and safely promoted to any pointer *to object* type, but not to any function pointer. – John Bollinger Feb 11 '15 at 17:08

3 Answers3

8

For any implementation conforming to C89 or later, casting the result of malloc() is never necessary.

Casting the result of malloc() is mentioned in the Errata for The C Programming Language, Second Edition:

142(§6.5, toward the end): The remark about casting the return value of malloc ("the proper method is to declare ... then explicitly coerce") needs to be rewritten. The example is correct and works, but the advice is debatable in the context of the 1988-1989 ANSI/ISO standards. It's not necessary (given that coercion of void * to ALMOSTANYTYPE * is automatic), and possibly harmful if malloc, or a proxy for it, fails to be declared as returning void *. The explicit cast can cover up an unintended error. On the other hand, pre-ANSI, the cast was necessary, and it is in C++ also.

(That link is no longer valid. The late Dennis Ritchie's home page is now here; it points to an updated location for the home page for the book, but that link is also invalid.)

Best practice is to assign the result of a malloc() call directly to a pointer object, and let the implicit conversion from void* take care of type consistency. The declaration of malloc() needs to be made visible via #include <stdlib.h>.

(It's barely conceivable that you might want to use malloc() in a context where an explicit conversion is needed. The only case I can think of is passing the result of malloc() directly to a variadic function that needs an argument of a pointer type other than void*. In such an unusual and contrived case, the cast can be avoided by assigning the result to a pointer object and passing that object's value to the function.)

The only good reasons to cast the result of malloc() are (a) compatibility with C++ (but the need to write code that compiles both as C and as C++ rarer than you might expect), and (b) compatibility with pre-ANSI C compilers (but the need to cater to such compilers is rare and becoming rarer).

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • "_The only case I can think of is passing the result of malloc() directly to a variadic function that needs an argument of a pointer type other than void*_" : even this is partially true, as even if the expected argument type was `void*`, the implicit conversion to `void*` cannot be performed by the compiler on the passing argument, because of the variadic aspect of the function. The compiler cannot perform implicit conversion to a type it doesn't know ! Indeed, the only type of conversion the compiler performs on variadic and prototypeless functions are _default integer promotion_ ... – programmersn Jul 12 '19 at 17:37
  • ... and `float` -> `double` conversion, nothing else. – programmersn Jul 12 '19 at 17:45
  • _"In such an unusual and contrived case, the cast can be avoided by assigning the result to a pointer object and passing that object's value to the function.) "_ : even in such a case you would need to cast the passed argument if the source (_T1_) and destination (_T2_) types are different. Failing to do so would lead to _UB_ on architectures where the _T1_ and _T2_ types have different representations. – programmersn Jul 12 '19 at 17:51
  • The only case I can think of where a variadic function wouldn't need its trailing argument to be cast to the expected type is when the source and destination types are either `char*` and `void*` , as they are compelled to have same representation/alignment per the C standard, making any objects of such types interchangeable. AFAIK this is the only case where neither _implicit_ nor _explicit_ (_cast_) conversion is needed. – programmersn Jul 12 '19 at 17:52
2

Firstly: K&R C is ancient.

Secondly: I don't know if that's the full code in that example, but in K&R C, functions are assumed to have an int return value if not otherwise declared. If he's not declaring that malloc, it means it returns int as far as the compiler is concerned, hence the cast. Note that this is undefined behavior even in C89 (the earliest standarized version) and extremely bad practice; but he may have done it this way for brevity, or perhaps due to laziness --- or maybe for some other historical reason; I don't know all the intricacies of K&R C, and it might be that void* to char* casts were not implicit back then.

I should add that this is a very serious problem, since int and void* may have different sizes (and often do --- the most common example being most x86_64 code, where pointers are 64-bit, but ints are 32-bit).

UPDATE: @KeithThompson has informed me that the code is from 2nd edition, and malloc is declared there. That one is (unlike 1st ed) still relevant, if very much outdated at places.

I'm guessing that the cast was likely done for compatibility with non-conforming compilers, which mattered at the time, as many [most?] compilers were not fully conforming yet. Nowadays, you'd have to get out of your way to find one that would need the cast (and no, C++ compilers don't count; they're C++ compilers, not C compilers).

Tim Čas
  • 10,501
  • 3
  • 26
  • 32
  • The code is from the second edition (it uses a prototype, which didn't exist when the first edition was published). The cast is unnecessary. – Keith Thompson Feb 11 '15 at 16:56
  • @KeithThompson: Probably added for compatibility with non-compliant compilers, then. Back then, it mattered; nowadays, pretty much every compiler implemnets C89 (or at least the vast majority of it). – Tim Čas Feb 11 '15 at 16:58
  • It's also in the errata (see my answer). – Keith Thompson Feb 11 '15 at 17:07
  • @KeithThompson: Nice, I didn't know about that. And I think that settles the "cast malloc" vs "don't cast malloc" argument --- the standards committee *themselves* say the cast is/can be harmful. I'll need to read K&R C oneday, just so that I can help other people learning C better (when they quote it). – Tim Čas Feb 11 '15 at 17:10
  • The K&R eratta list was not written by the committee. – Keith Thompson Feb 11 '15 at 17:12
  • @KeithThompson: Ah, disregard that part then. Still, I agree with the sentiment. – Tim Čas Feb 11 '15 at 17:13
1

People who want (or may want) to cross compile C and C++ often cast malloc(.) because C++ won't automatically promote void*.

As others point out in the good old days when function prototypes were optional the compiler would assume malloc() returned int if it hadn't seen the prototype with potentially catastrophic run-time outcomes.

I try to see some point in that argument but I simply can't believe such situations occur with a regularity that warrants the flaming people then get for casting malloc(.).

Look at any Question that casts malloc(.) on this site and it doesn't matter what it's about some one will angrily demand they remove their casts as though they make their eyes bleed.

NB: It does no good and makes nothing wrong right to cast malloc(.) so it is clutter. In fact the only (real) argument I know is that it leads people to look at the cast and not look at the argument - which is what matters!

I saw code on here today:

int** x=(int**)malloc(sizeof(int)*n);

It's very easy to be lulled into a false sense of security. The sizeof(int) is doing (or in this case failing to do) the work to make sure what comes back from malloc(.) is right for the purpose. (int**) does no work.

I'm not saying cast it. I am appealing for calm. I certainly think if a novice is failing to call free(.) we teach them that lesson before we get into nuances of casting malloc(.).

Persixty
  • 8,165
  • 2
  • 13
  • 35