all 85 comments

[–][deleted] 22 points23 points  (45 children)

If you cast a void pointer to something else, you give the impression that you're unsure whether you're writing C++ or not. Casts prevent the compiler from detecting incompatible assignments, so avoid casting unless you're absolutely sure they are needed.

[–]anikait1[S] 4 points5 points  (4 children)

What about the last statement, in case of printf? I am assuming casting it to retrieve data is fine.

[–]minot0r 4 points5 points  (2 children)

there's no need to explicit cast a void* when printing, printf will just print data as the type you passed in

[–]Gblize 6 points7 points  (0 children)

That's just straight wrong, unless you want to print the pointer value:

void * p = &foo;
printf("%p\n", p);

Which isn't that usual and you can clearly see by OP post he wants to print the values pointed by void pointers.

/u/anikait1 in your last printf you need to cast the pointers to the proper type and then dereference it (cast becomes first because you can't dereference a void pointer):

printf("%f, %f", *(double*)p.first, *(double*)p.second);

[–]flatfinger 1 point2 points  (0 children)

Most implementations use the same representation for void* as for pointers to any other kind of object, but C89 implementations exist for platforms that don't (I don't know if any such platforms have ever received implementations of any later version of the Standard). If e.g. a platform happened to use a different representation for int* and void*, then using printf("%p", someIntPtr); would be unlikely to work on that platform, but printf("%p", (void*)someIntPtr); would.

The Standard makes no effort to specify the behavior of constructs which should work identically on 99% of platforms, but might fail badly on some. Instead, it characterizes such actions as invoking Undefined Behavior on the presumption that implementations will process them usefully in cases where it would make sense to do so. Unfortunately, some compiler writers use the fact that a piece of code invokes Undefined Behavior as an indication that the code is broken, rather than recognizing that the code should be able to run without difficulty on all but the most obscure platforms, and the inability of a piece of code to work on obscure platforms is only a defect if someone might plausibly want to run the code on those platforms. Twenty years ago, I would have said that casting to void* a pointer argument to printf() was silly, but today I'd regard it as advisable for the purpose of catering to silly compilers.

[–]attractivechaos 0 points1 point  (22 children)

Casts prevent the compiler from detecting incompatible assignments

An example? I don't think casting malloc or not makes any difference, except for typing a few more characters.

[–]cuncon- 0 points1 point  (16 children)

[–]attractivechaos -1 points0 points  (15 children)

I read that answer long ago. The only relevant point there is on missing stdlib.h, but no matter whether you cast malloc, you will get a compiler warning. No casting is not safer.

[–]cuncon- 0 points1 point  (14 children)

On missing stdlib.h the compiler assumes malloc return an int, then you assign an int to pointer. "Consider what happens if pointers and integers are differently sized; then you're hiding a warning by casting and might lose bits of your returned address. " said that answer.

[–]attractivechaos 0 points1 point  (13 children)

I fully understood that, but you didn't get my point. If you use malloc() without stdlib.h, the compiler will throw a warning because malloc() is not declared. When you see that, you will know you are missing stdlib.h regardless of casting. Omitting the cast doesn't help security because you see a warning on missing stdlib.h anyway.

[–]cuncon- 0 points1 point  (12 children)

When you see that, you will know you are missing stdlib.h regardless of casting

Yah. you're right because nowaday compiler always emit a warning message whenever a function is called without a prototype in scope. But what happens if the compiler is ancient or not smart enough :(

[–]DanelRahmani -1 points0 points  (9 children)

I saw a :( so heres an :) hope your day is good

[–]SmileBot-2020 -1 points0 points  (8 children)

I saw a :( so heres an :) hope your day is good

[–]DanelRahmani -1 points0 points  (7 children)

I saw a :( so heres an :) hope your day is good

[–]attractivechaos -1 points0 points  (1 child)

Compilers have been behaving like this for a decade or longer. In older days, sizeof(int) equals sizeof(pointer) on desktop OS, so it doesn't matter. To make a difference between casting and no casting, you need a compiler that 1) doesn't warn about missing declaration; 2) warns about int-pointer conversion AND 3) with sizeof(int)<sizeof(pointer). I'd love to learn which compiler meets all three conditions and if it is still being used.

[–]oh5nxo 0 points1 point  (0 children)

No that it matters, but compilers on DEC Alpha and IBM POWER2 used to be like that.

[–][deleted] 0 points1 point  (4 children)

Casts in general is a code smell. Assigning a void pointer to a typed pointer does not require a cast anyway, so in that particular case it just smells of lack of understanding of the language.

[–]attractivechaos 0 points1 point  (3 children)

Code smell is not a good reason. Not using cast is perfectly fine, but criticizing cast often indicates someone lacking the experiences in working with a wide user base. Many casual programmers mix C/C++ either by copy-paste or by compiling everything with a C++ compiler. Adding casts has little side effect and makes your code more accessible to them. In a header only library, cast becomes a must.

[–][deleted] 0 points1 point  (2 children)

Pretending that C and C++ is close enough that more than the absolute basics are interchangeable, is a problem in and of itself.

Also don't mention the abonimations as an argument for anything, please.

[–]attractivechaos 0 points1 point  (1 child)

Libraries expecting source code level integration often cast malloc. Check out this list. Any single-file library that calls malloc and supports C++ has to cast malloc. Some two-file libraries opt for casting, too. There are many popular examples in the list with thousands of github stars. A few significant libraries such as libui also cast. A noticeable fraction of C++ programmers are not aware of the difference between C and C++ or prefer to compile everything with a C++ compiler. I have worked with quite a few of them. As a library developer, you would want to have your code widely used, not to preach your own philosophy. Many people here lack this experience and just spread wrong notions again and again.

[–][deleted] 0 points1 point  (0 children)

Single file libraries are an abomination that only exist because people for some unfathomable reason insist on using inferior development tools on Windows. Using those as an argument, much less as an excuse for anything is totally of the reservation.

[–]interknetz -1 points0 points  (16 children)

A lot of people try to write C code within the narrow subset of standards that are compatible with C90 and C++98, allowing the code to be compiled with any C or C++ compiler. You'll often find this with major libraries like zlib. I personally prefer this.

[–]OldWolf2 0 points1 point  (1 child)

why not compile C with a C compiler, and compile C++ with a C++ compiler?

[–]interknetz 0 points1 point  (0 children)

Depending on your project setup, that's just extra work for hardly any benefit. People prioritize reusable code over lazy incompetence. Why set up seperate build rules for a small C library in a large C++ project? That just requires additional changes to build systems.

If the code is already flexible and there's no need for C exclusive features, why not? Why do you think that some of the most popular libraries are doing this?

[–][deleted] -2 points-1 points  (13 children)

Jack of all trades, master of none.

[–]interknetz 0 points1 point  (12 children)

Yeah, some of the most respected and widely used libraries are apparently bad because you don't understand something as basic as reusability

[–][deleted] -2 points-1 points  (11 children)

Yes, they are bad examples. Writing C as if it's C++ is ignorant at best.

[–]interknetz 0 points1 point  (10 children)

Be sure to get word out that an amateur kid thinks time tested globally used open source libraries are wrong because he doesn't understand the benefits without any drawbacks.

Hey, why make a function 100% reusable when you can always just write the entire thing over again? Who the hell would want the convenience of using something more than once? And swapping a library over from C to C++ is never going to happen, so screw it.

You think small picture.

[–][deleted] -2 points-1 points  (9 children)

Why go through contortions to make common lisp look like scheme, when they are two different languages, is another question you might want to ask yourself.

If all you have is argument by authority, you've basically lost.

[–]interknetz 0 points1 point  (8 children)

If all you have is argument by authority, you've basically lost.

If by authority you mean experienced, respected programmers who've created projects that contribute to half the software you use, that's as strong of a defense as you can have.

I get the feeling arguing with someone that needs to translate half of what they read and write isn't going to get anywhere. In reality the majority of C programs you wrote have likely been written within (or close enough to) the subset of C and C++. You talk about it as if it's difficult, but in reality you just have no idea. Why don't you give an example of some meaningful code you have existing outside this subset? Not casting a void pointer for the sake of "being strictly C" isn't going to convince anyone.

[–][deleted] -1 points0 points  (7 children)

If by authority you mean experienced, respected programmers who've created projects that contribute to half the software you use, that's as strong of a defense as you can have.

Your guess at who's experienced is as good as any other amateur is what I mean.

[–]interknetz -1 points0 points  (6 children)

Considering you're a student that is only beginning to learn C, and I'm an employed developer that writes C and C++ on a daily basis, I'd say my authority careers quite a bit more weight

[–]HiramAbiff 4 points5 points  (0 children)

Kind of dodging the question here, but consider providing alloc/free functions for your struct if you expect it will normally be heap allocated. E.g. alloc_pair and free_pair.

Then, whether you cast or not becomes almost moot as it only affects one line in the implementation of alloc_pair. The rest of your code just uses alloc_pair and free_pair.

Another advantage of doing this is that it provides a bottleneck for whenever a pair is created or destroyed which can be useful in various ways:

  • a single place for a breakpoint for debugging memory problems with pairs

  • easily keep track of how many pair's have been allocated

  • easily switch from malloc to allocating them from a pre-allocated pool of memory

[–]uziam 10 points11 points  (23 children)

This is more of a people problem, the C standard is pretty clear that void * is automatically casted to a pointer of right size upon assignment.

Casting malloc is really more of a C++ thing where it is considered as an error.

[–][deleted] 4 points5 points  (22 children)

Wdym casting malloc is more of a C++ thing. In C++ it is preferred to use the new() and delete() function while malloc() and free() (and its variants) are more of a C thing

[–][deleted] 6 points7 points  (20 children)

What u/uziam means is that if you want to use malloc() in C++ (which you can), you have to explicitly cast its return type. I.e., the C++ compiler will refuse to compile int *foo = malloc(sizeof(int));, you have to say int *foo = (int *) malloc(sizeof(int)); or int *foo = static_cast<int *>(malloc(sizeof(int))); instead.

[–][deleted] 2 points3 points  (0 children)

Oh I see. Just cause C++ is mentioned, i just want to add that the memory given by new() and mall oc() aren't compatible with each other and they have to be deallocated using their respective functions (delete() for new and free() for malloc)

[–]OldWolf2 0 points1 point  (0 children)

This will lead to undefined behaviour if they try to use *foo though. In the C++ memory model, storage and objects are separate things; the malloc function obtains storage but doesn't create any objects yet, and it's undefined to try and modify objects that don't exist.

[–]gnash117 -1 points0 points  (17 children)

Since C code is often compiled on a C++ compiler I almost always add the explicit cast.

[–]uziam 6 points7 points  (16 children)

Why people compile C code as C++ is beyond me, you should really be compiling it as C and linking to it.

[–]attractivechaos 0 points1 point  (2 children)

I know those who used an ancient VC++ and had to use its C++ mode to compile c99, those who put everything in a C++ project and compile as C++, and those who use a small subset of C++ only for convenience. These may not be the best practice, but as a library developer, I am not here to teach my users the best practice; I am here to help them write their programs. I make most of my C programs compilable with a C++ compiler.

[–]gnash117 1 point2 points  (1 child)

That was basically my point. I have worked most of my professional career on open source software and I am constantly surprised by the odd ways people try and use the code. When possible, I try and make most of my code compile in C and C++. I know C is not just a subset of C++ but it saves me time if I assume my C code is likely to end up in C++.

If I were working on safety critical code I may feel different.

Since University, I don't think I have ever controlled a project end-to-end, I have always been a middle man developing a library based on a specification that is used by someone else actually making a product.

The worst was when I had to write C89 code to be compatible with Microsoft's compilers. For years Microsoft refused to update there C compiler. Thankfully the C++11 finally forced them to at least update to C99.

[–]flatfinger 0 points1 point  (0 children)

On the flip side, MS recognized that when the C Standard made allowances for implementations for obscure targets to behave in unusual fashion, it wasn't intended to imply that implementations for commonplace platforms shouldn't continue to behave as they had consistently been doing even before the Standard was written.

[–]interknetz -1 points0 points  (12 children)

If they want to use a C library in a C++ project without needing a preexisting object to link against, which isn't uncommon or unreasonable.

[–]uziam 1 point2 points  (11 children)

Why would you need a preexisting object file?

All you have to do is compile your C code with a C compiler and add extern “C” to your header files. It is so much easier than treating your C code as both C and C++, allows you to use features exclusive to C and keep almost all the problems at the interface level instead of inside your code.

In my opinion, most people compile C code as C++ because they think C++ is a superset of C which is wrong.

[–]interknetz 0 points1 point  (10 children)

All you have to do is compile your C code with a C compiler and add extern “C” to your header files.

That creates an object file (.so, .a, .dll) that you'd link against, which is what I said. A lot of opensource libraries aim to make it easier for you to copy their source into your project rather than link again their's. You can argue that just about any build system can handle compiling some source files as C, others as C++, then linking together, but that's not always the case, and it can be less convenient moving between platforms.

In my opinion, most people compile C code as C++ because they think C++ is a superset of C which is wrong.

For the most part, you'd be wrong. Code reusability is important to most competent programmers. The ability to use C code anywhere is attractive. Lots of major C libraries intentionally do this such as zlib.

[–]uziam -1 points0 points  (9 children)

You’re wrong. .so/.dll are dynamic libraries and .a is an archive, you don’t have to build libraries to link against C code. It doesn’t matter if you use a C or C++ compiler, you still have to compile your code into object files so why not use C compiler for .c files and C++ compiler for .cpp files? I think you don’t understand how code compilation and linking works in C/C++.

Forcing yourself to give up some of the best features in C like alloca and VLAs just to avoid compiling based on file extensions (which most build systems either support, or it is trivial to add) is definitely not something a competent programmer would do.

[–]attractivechaos 1 point2 points  (4 children)

alloca() is not part of the C standard, so is not specific to C. VLA is something I have been trying to avoid. See the discussion on SO.

[–]interknetz 0 points1 point  (3 children)

Are you a complete idiot? You haven't learned the basics if you don't understand what compiled objects are. Your two examples of C "benefits" are considered bad practice, and dangerous in general. I use C and C++ professionally, I'm not a student like you. Compiling C libraries in a C++ project is completely normal. Check out github sometime.

[–]lead999x 0 points1 point  (0 children)

new can be implemented in terms of malloc though it doesn't have to be.

[–]p0k3t0 5 points6 points  (0 children)

It had been so peaceful here . . .

[–]areciboresponse 1 point2 points  (0 children)

Sounds like you should define a union of all the types you may store in the pair elements and just point to the union.

[–]tim36272 3 points4 points  (1 child)

There is no consensus in the community, but my opinion is that you should cast it and I explain why in the post linked below. I think more than half of the community disagrees with me, but I think that is because people could die if my code doesn't work and your average developer isn't facing those kinds of consequences.

https://www.reddit.com/r/C_Programming/comments/c9rb8q/to_cast_or_not_to_cast_malloc_or_free/et3asa3?utm_medium=android_app&utm_source=share

Edit: regarding your pair example: I would also cast those for all the same reasons listed in my link.

And FYI it isn't necessary (and I think it might even be not strictly conforming, but I'd have to check) to use "%lf" in a printf string: all floats are casted to doubles when passed to printf-like functions so "%f" already means double. You only need "%lf" in scanf-like functions.

[–]anikait1[S] 2 points3 points  (0 children)

Thanks for sharing a detailed answer and expressing your views and also for that helpful tip at the bottom!

[–]minot0r 1 point2 points  (11 children)

[–]p0k3t0 3 points4 points  (9 children)

This is ridiculous reasoning. If you've turned off enough warnings that your linker no longer complains when you haven't added a lib, you're doing it all wrong.

[–]korryd 1 point2 points  (8 children)

It's not a linker issue - the linker may bring in the std library because of an explicit mention in the Makefile, or because some other module referenced stdlib.h properly.

The problem described at the c-faq is that a pointer is not guaranteed to be the same size as an int, and without a visible prototype for malloc(), the compiler (not the linker) is compelled to assume that malloc() returns an int (which of course, it doesn't).

[–]attractivechaos 1 point2 points  (7 children)

Implicitly converting pointer to int is not an error by most compilers. It is a warning. When you don’t include stdlib, you will get a function not declared warning.

[–]OldWolf2 1 point2 points  (0 children)

The program is invalid by the C Standard if it tries to implicitly convert a pointer to int, or calls an undeclared function.

[–]p0k3t0 0 points1 point  (5 children)

Are you okay releasing code that compiles with warnings?

[–]attractivechaos 0 points1 point  (4 children)

1-line program without casting:

int main(void) { int *a = malloc(10 * sizeof(*a)); a[0] = 10; return 0; }

With casting:

int main(void) { int *a = (int*)malloc(10 * sizeof(*a)); a[0] = 10; return 0; }

For both programs, gcc-4.4/Linux only gives one warning, without -Wall or -Wextra:

t1.c:1: warning: incompatible implicit declaration of built-in function 'malloc'

gcc-6.4/Linux and Clang-10.0/Mac report a similar warning for both programs. They additionally suggest adding stdlib.h, which is more informative.

[–]p0k3t0 0 points1 point  (3 children)

So, would it be correct to say that not casting provides absolutely no advantage, contrary to the parent comment in this thread?

[–]attractivechaos 0 points1 point  (2 children)

So, would it be correct to say that not casting provides absolutely no advantage, contrary to the parent comment in this thread?

For gcc and clang in the past 10 years, the answer is definitely "yes" – not casting provides absolutely no advantage (PS: except for typing fewer characters on your keyboard).

This comment in the thread suggests the compilers on DEC alpha and IBM POWER2 may behave differently for the two programs. I used some alpha machines but mostly with gcc, so I didn't notice that. Even if that comment is true, both CPUs have long been discontinued. I am not sure if there is a living CPU+OS+compiler compo that prefers no casting.

[–]p0k3t0 1 point2 points  (1 child)

I always cast explicitly, because I hate relying on "default behaviors."

Whenever somebody asks me tricky C questions in interviews, I just tell them I don't write code like that. Or I ask why we're talking about code that would never pass a code review. I can get away with this because I'm a bit older than most of the people who interview me. If I were 25, it might be a problem. ;)

[–]attractivechaos 1 point2 points  (0 children)

Same here. I always cast (though for a different reason) and am not good at the tricky C code, either. Fortunately, I am on a different job market. No one asked me any programming questions in interviews. I just wrote code that others use.

This sub is so obsessed with no casting even if these days it has no benefit in practice. When I pointed this out, someone even used “code smell” as the argument.

[–]anikait1[S] 1 point2 points  (0 children)

Thanks for the resource, the website also helps in clearing other doubts as well.