31

In C, there is no need to cast a void * to any other pointer type, it is always safely promoted. However, in C++, this is not the case. E.g.,

int *a = malloc(sizeof(int));

works in C, but not in C++. (Note: I know that you shouldn't use malloc in C++, or for that matter new, and should instead prefer smart pointers and/or the STL; this is asked purely out of curiosity) Why does the C++ standard not allow this implicit cast, while the C standard does?

Kilian Foth
  • 107,706
  • 45
  • 295
  • 310
wolfPack88
  • 678
  • 2
  • 7
  • 15
  • 3
    `long *a = malloc(sizeof(int));` Oops, someone forgot to change only one type! – Doval Mar 09 '15 at 14:42
  • 6
    @Doval: That's still easily fixed by using `sizeof(*a)` instead. – wolfPack88 Mar 09 '15 at 14:46
  • 3
    I believe @ratchetfreak's point is that the reason C does this implicit conversion is because `malloc` cannot return a pointer to the type allocated. `new` is C++ does return a pointer to the type allocated, so properly written C++ code will never have any `void *`s to cast. – Gort the Robot Mar 09 '15 at 15:59
  • 1
    @StevenBurnap: Which is all well and good, but irrelevant to the question. I stated explicitly that I just wanted to know why it couldn't happen purely out of curiosity, not out of any need in actual code. As C++ was originally built on C, it seemed like this was deliberately taking a feature out, and I wanted to know why. – wolfPack88 Mar 09 '15 at 16:05
  • 3
    As an aside, it's not any other pointer type. Only data-pointers need apply. – Deduplicator Mar 09 '15 at 16:28
  • 1
    The first C++ compiler most PC users experienced was the Zortech compiler, which happened to *allow* the assignment as in C. A certain columnist (who's also a drummer) wrote that Cfront was wrong since it differed from what he was used to. How stupid not to at least fact-check, or read Stroustrup's book before going into business as an expert. – JDługosz Mar 09 '15 at 18:54
  • 3
    @wolfPack88 you have your history wrong. C++ had `void`, C did *not*. When that keyword/idea was added to C, *they* changed it to suit C's needs. That was shortly after pointer types started being checked *at all*. See if you can find the K&R C description pamphlet online, or a vintage copy of a C programming text like Waite Group's *C Primer*. ANSI C was full, of features backported or inspired by C++, and K&R C was much simpler. So it's more correct that C++ extended C as it existed at the time, and the C that you know was stripped down from C++. – JDługosz Mar 09 '15 at 19:02
  • `it is always safely promoted`... er, no. It is always assigned, but that doesn't make it *safe*. – J... Mar 10 '15 at 10:34
  • 1
    @Doval, `long *a = (long*)malloc(sizeof(int));` does not look any safer to me – tstanisl Jul 28 '22 at 09:49

4 Answers4

39

Because implicit type conversions are usually unsafe, and C++ takes a more safe stance to typing than C does.

C will usually allow implicit conversions, even if most chances are that the conversion is an error. That's because C assumes that the programmer knows exactly what they are doing, and if not, it is the programmer's problem, not the compiler's problem.

C++ will usually disallow things that could potentially be errors, and require you to explicitly state your intention with a type cast. That's because C++ is trying to be programmer-friendly.

You might ask how come it is friendly when it is actually requiring you to type more.

Well, you see, any given line of code, in any program, in any programming language, will generally be read many more times than it will be written (*). So, ease of reading is much more important than ease of writing. And when reading, having any potentially unsafe conversions stand out by means of explicit type casts helps to understand what is going on and to have a certain level of certainty that what is happening is in fact what was intended to happen.

Besides, the inconvenience of having to type the explicit cast is trivial compared to the inconvenience of hours upon hours of troubleshooting to find a bug which was caused by a mistaken assignment that you could have been warned about, but never were.

(*) Ideally it will be written only once, but it will be read every time someone needs to review it to determine its suitability for reuse, and every time there is troubleshooting going on, and every time someone needs to add code near it, and then every time there is troubleshooting of nearby code, and so on. This is true in all cases except for "write-once, run, then throw away" scripts, and so it is no wonder that most scripting languages have a syntax which facilitates ease of writing with complete disregard to ease of reading. Ever thought that perl is completely incomprehensible? You are not alone. Think of such languages as "write-only" languages.

Mike Nakis
  • 32,003
  • 7
  • 76
  • 111
  • C is essentially one step above machine code. I can pardon C for things like this. – Qix - MONICA WAS MISTREATED Mar 09 '15 at 18:18
  • 8
    Programs (whatever the language) are meant to be read. Explicit operations stand out. – Matthieu M. Mar 09 '15 at 18:50
  • 11
    It's worth noting, that casts through `void*` are *more* unsafe in C++, because with the way certain OOP features are implemented in C++, pointer to same object might have different value depending on pointer type. – hyde Mar 09 '15 at 19:50
  • @MatthieuM. very true. Thanks for adding that, it is worth being part of the answer. – Mike Nakis Mar 10 '15 at 09:53
  • @MikeNakis: Then feel free to integrate it, since comments are meant to be ephemeral :) – Matthieu M. Mar 10 '15 at 10:10
  • 1
    @MatthieuM.: Ah, but you really don't want to have to do everything explicitly. Readability is not enhanced by having more to read. Though in this point the balance is clearly for being explicit. – Deduplicator Mar 10 '15 at 16:39
29

Here is what Stroustrup says:

In C, you can implicitly convert a void* to a T*. This is unsafe

He then goes on to show an example of how void* can be dangerous and says:

... Consequently, in C++, to get a T* from a void* you need an explicit cast. ...

Finally, he notes:

One of the most common uses of this unsafe conversion in C is to assign the result of malloc() to a suitable pointer. For example:

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

In C++, use the typesafe new operator:

int* p = new int;

He goes into a lot more detail on this in The Design and Evolution of C++.

So the answer boils down to: The language designer believes it is an unsafe pattern, and so made it illegal and provided alternate ways of accomplishing what the pattern was normally used for.

Gort the Robot
  • 14,733
  • 4
  • 51
  • 60
  • 1
    Relating to the `malloc` example, it's worth noting that `malloc` will (obviously) not call a constructor, so if it is a class type you are allocating memory for, being able to implicitly cast to the class type would be misleading. – chbaker0 Mar 09 '15 at 23:37
  • 1
    This is a very good answer, arguably better than mine. I only slightly dislike the "argument from authority" approach. – Mike Nakis Mar 10 '15 at 09:52
  • "new int" - wow, as a C++ newbie I was having trouble thinking of a way to add a base type to the heap, and I didn't even know you could do that. -1 use for malloc. – Katana314 Mar 10 '15 at 13:57
  • 3
    @MikeNakisFWIW, my intent was to complement your answer. In this case, the question was "why did they do that", so I figured hearing from the chief designer is justified. – Gort the Robot Mar 10 '15 at 15:49
  • It is worth to add that the cast make the code even **less** safe. Imagine `void *foo(); int *x = (int*)foo();`. After some time someone changes the signature of `foo` to `float* foo();`. You will get *no* warning because the explicit cast bypass type checking. Without the cast the warning would be emitted. Now "argument by authority" is messing up new C code that tries to be usable in C++ by forcing this brain-dead cast. – tstanisl Jul 28 '22 at 09:54
11

In C, there is no need to cast a void * to any other pointer type, it is always safely promoted.

It's always promoted, yes, but hardly safely.

C++ disables this behaviour precisely because it attempts to have a safer type system than C, and this behaviour is not safe.


Consider in general these 3 approaches to type conversion:

  1. force the user to write everything out, so all conversions are explicit
  2. assume the user knows what they're doing and convert any type to any other
  3. implement a complete type system with type-safe generics (templates, new-expressions), explicit user-defined conversion operators, and force explicit conversions only of things the compiler can't see are implicitly safe

Well, 1 is ugly and a practical obstacle to getting anything done, but might genuinely be used where great care is needed. C roughly opted for 2, which is easier to implement, and C++ for 3, which is harder to implement but safer.

Useless
  • 12,380
  • 2
  • 34
  • 46
  • It was a long hard road to get to 3, with exceptions added later and templates later still. – JDługosz Mar 09 '15 at 19:06
  • Well, templates aren't _really_ needed for such a complete safe type system: [Hindley-Milner](https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system) based languages get along without them quite well, without implicit conversions at all and no need to write out types explictly either. (Of course, these languages rely on type erasure / garbage collection / higher-kinded polymorphism, things which C++ rather avoids.) – leftaroundabout Mar 10 '15 at 00:48
2

By definition, A void pointer can point to anything. Any pointer can be converted to a void pointer and thus, you will be able to convert back arriving at the exact same value. However, pointers to other types may have constraints, such as alignment restrictions. For example, imagine an architecture where characters can occupy any memory address but integers must start on even address boundaries. In certain architectures, integer pointers might even count 16, 32 or 64 bits at a time so that a char * might actually have a multiple of the numeric value of int * while pointing to the same place in memory. In this case, converting from a void * actually would round off bits which can not be recovered and is therefore not reversible.

Put simply, the void pointer can point to anything, including things that other pointers may not be capable of pointing to. Thus, the conversion to the void pointer is safe but not the other way around.

roserez
  • 29
  • 1