2

Take this code for instance:

const char *s;
char *t = s;

This would emit this: warning: assignment discards 'const' qualifier from pointer target type

It's easy to silence the compiler, by just adding a cast:

char *t = (char*)s;

And you can do similar things for regular variables and not just pointers, and it does not only apply to the const qualifier. You can basically cast from any type to any type, even if some of those casts would cause trouble.

I have also read that you never should cast malloc because a cast to and from void pointer is always safe.

But what does the casting actually do? Does it do something else than just preventing the compiler from spitting out warnings. When are explicit casts actually necessary?

Clarification:

I'm talking about assignments here. Not things like double res = (double)a / b. In that situation, I know what the cast does, and in this case we could easily get rid of the explicit cast in favor of an implicit cast like this: double c = a; double res = c/b; or just double res = (1.0 * a) / b

klutt
  • 30,332
  • 17
  • 55
  • 95
  • 1
    I’m not sure if there’s a situation where you can’t use a variable, but it would often reduce readability in expressions like `(double)a / b` to avoid integer division. Is that what you’re asking? – Ry- Oct 10 '20 at 02:32
  • Also, it seems these warnings are about standard-nonconforming code and are allowed to be errors: https://stackoverflow.com/questions/36828742/technical-legality-of-incompatible-pointer-assignments – Ry- Oct 10 '20 at 02:34
  • 2
    The bottom line is the cast masks the warning by telling the compiler `s` is type `char*` instead of `const char*` so it allows the assignment to `t` without warning -- but -- (and there is always a but...) now `t` points to `s`, but... `t` is mutable and any function that receives `t` can validly assume it points to mutable memory -- which isn't the case and if something attempts to modify the memory `t` points to (and it happens to be a string-literal, etc..) -- BAM SEGFAULT. Essentially the cast makes the type the compiler has for `t` a lie. – David C. Rankin Oct 10 '20 at 02:38
  • 1
    This is an interesting question. There are some casts that actually change the code generated by the compiler, like the `(double)a/b` variety, and some that just allow the compiler to accept code it would otherwise complain about. I'm not sure whether there is a one-sentence answer to the "where are explicit casts needed" question. – Kevin Boone Oct 10 '20 at 08:00
  • There's also this case where a cast is necessary: https://stackoverflow.com/questions/24867814/printfp-and-casting-to-void and https://stackoverflow.com/questions/20469958/when-is-casting-void-pointer-needed-in-c – Bob__ Oct 10 '20 at 11:28

1 Answers1

2

C 2018 6.5.16.1 1 specifies constraints for the simple assignment operator. For left and right operands that are both pointers other than pointers to void, these say:

One of the following shall hold: … both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right; …

Thus, the C standard requires that pointers be of the same “kind” (compatible types) and that the assignment not remove any qualifiers (the left side has all the qualifiers of the right side, but possibly more). If this requirement is violated, the compiler must issue a diagnostic message (although it can still choose to accept the program; the message can be a warning rather than an error).

6.5.4 specifies constraints for casts, in paragraphs 2 to 4. None of them limit conversions from one pointer type to another. Therefore, you can specify any pointer conversion with a cast, and the compiler is not required to issue a diagnostic (although some may choose to).

The underlying philosophy is that an assignment allows you to implicitly do conversions that are “normal” uses of pointers, but explicitly using a cast allows “special” uses of pointers.

The fact that a particular conversion is allowed with a cast does not necessarily mean it is meaningful or well-defined. 6.3.2.3 defines some rules for conversions of pointers. For example, an int * may be converted to a char *, and the resulting pointer can be used to examine the bytes that represent an int. But converting a char * to an int * may result in undefined behavior. So a cast will allow you to do a conversion, but whether the result is useful or is what you want depends on other rules in the C standard.

When a conversion is allowed and defined, it will have the same effect whether it occurs implicitly by assignment or explicitly by cast. The cast does not change what the conversion is; it only changes whether it is allowed (without a diagnostic) or not.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312