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
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.
Related
When I compile this code with gcc 7.3.0, I get an "assignment from incompatible pointer type".
int intVar = 1;
char* charPointer;
charPointer = &intVar;
printf("%d", *charPointer);
So far so good. I can deal with it by doing the pointer assignment this way:
charPointer = (char*)&intVar;
Now my doubt is: how is the second case different? I can still mess things up if I don't cast charPointer to int* when I, for example increment it by n or dereference it. So why does the compiler act differently in those two cases? Why should he care if pointer type does not match during an assignment? I would just like to understand the logic behind it.
Because of the casual type change int * to char *, the compiler warns of potential pitfalls. With a cast, the compiler assumes the coders know what they are doing.
In C, the various type of pointers can live in different places, have different sizes and be encoded differently.
It is very common for all object pointers (int*, char *, struct foo *) to share the same size and encoding, but, in general, that is not required.
In is not uncommon for Function pointers to be of a different size than object pointers.
char * and void * share the same size and encoding and character pointers point to data with a minimal alignment requirement. Converting a non-char* object pointer to char * always "work". The reverse is not always true. leading to undefined behavior (UB).
Converting to non-character pointer also risks anti-aliasing (the need for the compiler to keep track that changes of data via one pointer type is reflected in another). Very strange undefined behavior can result.>
Better code avoids pointer type changes1 and when needed, is explicit with a cast.
1 Exceptions include when changing an object pointer to void * or form void* when there is no alignment concerns.
Pointer conversions are kind of dangerous.
If the pointer type your converting from is insufficiently aligned for the target type, you get UB (==undefined behavior; read up on what it is unless you have already) already at the conversion.
Otherwise, if you get usually get UB on dereferencing because C's strict aliasing rules require that you access objects through lvalue types sufficiently compatible with their effective type.
While the last paragraph doesn't quite apply to conversions to char pointers as char pointers can alias any type, the warning (compilers could make it a hard error too) is still useful because the conversion is still kind of dangerous.
printf("%d", *(char*)&(int){0xFFFF});
will get you only the first byte (it is endianness dependent whether that is the most significant one or the least significant one), printing 255 (if the implementation's char type is unsigned) or -1 (if it is signed).
printf("%d", *&(int){0xFFFF});
will get you all the bytes that are in an int.
If the compiler lets you assign to a char * from an int * with just a warning, it should behave the same as with the cast, but you do need the cast for your C to be conformant (and for the compiler to be silent about the conversion).
As #Mike Holt says, it's not actually different, except that you have told the compiler "Don't worry, I mean to do this".
The compiler does worry because assigning to a pointer of a different type is usually not what you want to do. Your code is telling the compiler "Treat the memory holding this variable as if it were holding a variable of a different type". This is almost certainly platform specific behavior, and possibly undefined behavior, depending on the types.
Considering
T1 *p1;
T2 *p2;
Can we assign p1 to p2 or vice versa? If so, can it be done without the casting or we must use a cast?
First, let’s consider assignment without casting. C 2018 6.5.16.1 1 lists constraints for simple assignment, saying that one of them must hold. The first two are for arithmetic, structure, and union types. The last two deal involve null pointer constants or _Bool. The middle two deal with assigning pointers to pointers:
the left operand has atomic, qualified, or unqualified pointer type, and … 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
the left operand has atomic, qualified, or unqualified pointer type, and … one operand is a pointer to an object type, and the other is a pointer to a qualified or unqualified version of void, and the type pointed to by the left has all the qualifiers of the type pointed to by the right
The latter says we can assign void * to any object pointer and vice-versa, as long as no qualifiers (const, volatile, restrict, or __Atomic) are removed.
The former says we can assign pointers to compatible types, as long as no qualifiers are removed. What are compatible types?
6.2.7 1 says:
Two types are compatible if they are the same.
Additional rules are in 6.7.2, 6.7.3, and 6.7.6.
Two structure, union, or enumerated types declared in separate translation units are compatible if they are, in essence, declared identically. (Interestingly, two such types declared in the same translation unit are not compatible.)
6.7.2 4 says each enumerated type (an enum type) is compatible with an implementation-defined choice of char or a signed or unsigned integer type. So a pointer to some enum can be assigned to a pointer to one char or integer type (and vice-versa), but you cannot know which one without knowing something about the particular C implementation.
6.7.3 11 says qualified types must have the same qualifiers to be compatible. Thus an int is not compatible with a const int, and this prevents an int * from being assigned to a const int *.
6.7.6.1 2 says for two pointer types to be compatible, they must be identically qualified pointers to compatible types. This tells us, for example, that int * is not compatible with char *, and, therefore, by assignment constraints above, a char ** may not be assigned to an int **.
6.7.6.2 6 says for two array types to be compatible, they must have compatible element types and, if they both have integer constant sizes, they must be the same. (This allows that an array with unknown size may be compatible with an array of known size. However, additional text says that if the arrays ultimately have different sizes, using them in a context that requires them to be compatible has undefined behavior. So an assignment of pointers to such arrays may satisfy its constraints and compile without error, but the resulting program may misbehave.)
6.7.6.3 15 presents somewhat complicated rules for the compatibility of function types. These rules are complicated because functions may be declared with or without parameter lists, with ellipses, and so on. I will omit complete discussion of these.
Those are the rules that tell you what pointer assignments may be made without casts.
6.5.4 discusses casts. Its constraints do not restrict which pointer types may be converted to which other pointer types. (They do prohibit other things involving pointers, such as converting a pointer type to a floating-point type.) So you can specify any pointer conversion you want in a cast, and, as long as the resulting type is compatible with the type to which it is being assigned, no assignment or cast constraint is violated. However, there is a still a question about whether the conversion is proper.
6.3.2.3 specifies rules for pointer conversions. Those dealing with conversion from pointers to pointers (excluding integers and null pointer constants) say:
Any pointer to an object type (not to function types) may be converted to a pointer to void and vice-versa. The result of converting an object pointer to a void pointer and back compares equal to the original.
A pointer may be converted to the same type with more qualifiers, and the result compares equal to the original.
A pointer to an object type may be converted to a pointer to a different object type if the resulting pointer is correctly aligned for its type (otherwise, the behavior is undefined). When converted back, the result compares equal to the original pointer. (Note that, while you are allowed to make this conversion, this rule does not say the resulting pointer may be used to access an object of the new type. There are other rules in C about that.)
A pointer to a function type may be converted to a pointer to another function type. When converted back, the result compares equal to the original pointer. (As with objects, you are allowed to make this conversion, but using the resulting pointer to call an incompatible function has undefined behavior.)
So, when casts are used, you may convert any object pointer type to any object pointer type and assign it as long as the alignment requirement is satisfied, and you may convert any function pointer type to any function pointer type and assign it.
When types T1 and T2 are different, assignments between T1 *p1 and T2 *p2 are formally disallowed, unless at least one of T1 and T2 is void and the other is an object (not function) type.
In many cases incompatible assignments will work in practice, especially on machines (such as all popular ones today) with "flat" address spaces and where all pointer types share the same internal representation.
After a "mixed-mode" pointer assignment, however, problems may very well occur when the pointers are dereferenced, due to (1) alignment issues and (2) strict aliasing.
Since "mixed mode" pointer assignments are formally illegal and often a bad idea, most compilers will warn about them. Most compilers allow the warnings to be suppressed with an explicit cast. Most of the time, the cast serves only to suppress the warning; it does not introduce any actual conversion that wouldn't have been performed anyway. (That is, changing p1 = p2 to p1 = (T1 *)p2 is a lot like changing i = f to i = (int)f, where i is an int and f is a float.)
Addendum: I wrote, "When types T1 and T2 are different," but a more precise statement would be when they are incompatible. For example, types char and unsigned char are compatible, so assignment between pointers of those types is fine. See Eric Postpischil's longer answer for more details.
I can't seem to make sense of a GCC compiler warning I get when I try to assign a void * value to a intptr_t variable. Specifically, when I compile with -std=c99 -pedantic, I get the following warning regarding the initialization of variable z on line 7:
warning: initialization makes integer from pointer without a cast [-Wint-conversion]
Here is the source code:
#include <stdint.h>
int main(void){
unsigned int x = 42;
void *y = &x;
intptr_t z = y; /* warning: initialization makes integer from pointer without a cast [-Wint-conversion] */
return 0;
}
Naturally, if I explicitly cast y to intptr_t then the warning disappears. However, I confused why the warning is present for implicit conversions when the whole purpose of intptr_t is in the conversion and manipulation of void * values.
From section 7.18.1.4 of the C99 standard:
The following type designates a signed integer type with the property that any valid
pointer to void can be converted to this type, then converted back to pointer to void,
and the result will compare equal to the original pointer:
intptr_t
Am I misinterpreting the standard, or is GCC simply overly pedantic in its "integer from pointer" check in this case?
Summing up! Apologies in advance for any errors — please leave me a comment.
In C99:
Any pointer can be converted to an integer type.1
You might want to do that, e.g., if you are implementing your own operating system!
Conversions between pointers and integers can go horribly wrong,1 so are usually not what you want.
Therefore, the compiler warns you when you convert pointers to integers without casting. This is not overly pedantic, but to save you from undefined behaviour.
intptr_t (and uintptr_t, and likewise throughout) is just an integer type,2 so it is subject to the same risks as any other pointer-to-integer conversion. Therefore, you get the same warning.
However, with intptr_t, you at least know that the conversion from a pointer won't truncate any bits. So those are the types to use — with explicit casts — if you really need the integer values of pointers.
The spec1, #6 says that
... the
result is implementation-defined. If the result cannot be represented in the integer type,
the behavior is undefined.
With intptr_t, the result can be represented in the integer type. Therefore, the behaviour is not undefined, but merely implementation-defined. That is (as far as I know) why those types are safe to use for receiving values from pointers.
Edit
Reference 1, below, is part of section 6.3, "Conversions." The spec says:3
Several operators convert operand values from one type to another automatically. This
subclause specifies the result required from such an implicit conversion...
and refers to section 6.5.4 for a discussion of explicit casts. Therefore, the discussion in Reference 1 indeed covers implicit casts from any pointer type to intptr_t. By my reading, then, an implicit cast from void * to intptr_t is legal, and has an implementation-defined result.1, 4
Regarding whether the explicit cast should be used, gcc -pedantic thinks it should, and there must be a good reason! :) I personally agree that the explicit cast is more clear. I am also of the school of thought that code should compile without warnings if at all possible, so I would add the explicit cast if it were my code.
References
1C99 draft (since I don't have a copy of the final spec), sec. 6.3.2.3 #5 and #6).
2Id., sec. 7.18.1.4
3Id., sec. 6.3
4Id., sec. 3.4.1, defines "implementation-defined behavior" as "unspecified behavior where each implementation documents how the choice is made." The implication is that the conversion is legal, but that the result may be different on one platform than on another.
I am reading some open-source C code and encountering A a = (A) b; type conversion many times. For example,
static void hexdump(const void* pv, int len)
{
const unsigned char* p = (const unsigned char*) pv;
// some other code
}
A a = (A) b; code happens mainly when b is a pointer, void * pointer most often. I have C++ background. I think in C++, the assignment operator takes= care of the type conversion automatically? Because it already know that a is of type A.
Is the explicit type conversion necessary in C?
No, conversion from void* to char* in C (it's a common example to explain where they're different!) is implicit so casting is unnecessary (then wrong because it may hide a problem if you wrongly change char to int).
Quoting "The C Programming Language, 2nd edition" by K&R (§A.6.8):
Any pointer to an object may be converted to type void* without loss of information. If the result is converted back to the original pointer type, the original pointer is recovered. Unlike the pointer-to-pointer conversions discussed in Par.A.6.6, which generally require an explicit cast, pointers may be assigned to and from pointers of type void*, and may be compared with them.
Please note "If the result is converted back to the original pointer type" because is crucial: if instead of char* you had int* then it may be wrong because of memory alignment.
From C99 standard (§6.3.2.3) about when conversion is possible:
A pointer to void may be converted to or from a pointer to any incomplete or object type. A pointer to any incomplete or object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.
Now let's see when can be implicit (thanks to mafso for very quick search), from C11 (n1570) §6.5.4p3:
Conversions that involve pointers, other than where permitted by the constraints of 6.5.16.1, shall be specified by means of an explicit cast.
Then §6.5.16.1:
One of the following shall hold: [...] the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) one operand is a pointer to an object type, and the other is a pointer to a qualified or unqualified version of void, and the type pointed to by the left has all the qualifiers of the type pointed to by the right
You've got it the other way around: the cast is absolutely necessary in C++, but it is not necessary in C.
This will compile in C, but not in C++:
static void hexdump(const void* pv, int len) {
const unsigned char* p = pv;
...
}
It is not necessary in C but it helps silent compiler warnings for implicit casts in some cases.
In C++ however you need explicit casts to cast a void pointer into a pointer of a different type.
In my practice, no casting at first, though there's compiler warnings, after testing pass, casting is added to eliminate warnings.
In this case you are making a cast.
You are just saying for the compile. Hey, I'm sure that this is a type (A).
C++ style casts are checked by the compiler. C style casts aren't and can fail at runtime
It's called a cast. It means that it converts b in the (A) type.
char c = 'a'; // c is a char
int b = (int) c // you tell your compiler to interpret c as an int
Given the following C code, what is the difference between a = f; and a = (int *) f;?
float *f;
int *a;
...
a = f;
a = (int *) f;
float *f;
int *a;
a = f;
This assignment is erroneous (there is a C constraint violation), there is no implicit conversion between pointer types (except with void *). A compiler can refuse to compile a program with this assignment.
Given:
float *f;
int *a;
This:
a = f;
is a constraint violation. It requires a diagnostic from any conforming compiler. After issuing the required diagnostic, it may or may not reject the program. (IMHO it should do so.) A conforming compiler may choose to accept it with a mere warning (which qualifies as a diagnostic), but once it does so the behavior of the program is undefined. Compilers that do this most commonly generate an implicit conversion from float* to int*, giving the same behavior as if there were a cast (an explicit conversion), but the standard does not require that.
Non-conforming compilers, of course are free to do anything they like.
Conclusion: Don't write code like that. Even if your compiler lets you get away with it, another compiler might not. If you want to convert from one pointer type to another, use a cast. Aside from validity issues, the cast makes it much clearer to the reader that something funny is going on. If your compiler gave you a warning, heed it. If it didn't, find out how to increase the warning levels on your compiler.
This:
a = (int *) f;
takes the value of f (which is of type float*) and explicitly converts it to type int*, then assigns that int* value to a. (I'll assume that something between the declaration and the assignment has set f to some valid value.)
If f is a null pointer, the conversion is well defined, and yields a null pointer of type int*. The rules for converting a non-null object pointer to another pointer type are (quoting N1570 6.3.2.3p7):
A pointer to an object type may be converted to a pointer to a
different object type. If the resulting pointer is not correctly
aligned for the referenced type, the behavior is undefined.
Otherwise, when converted back again, the result shall compare equal
to the original pointer.
This kind of conversion, assuming int and float are the same size and have similar alignment requirements, is likely intended to let you treat a float object as if it were an int object. This is called "type-punning". If int and float aren't the same size, or if they have different alignment requirements, this can easily blow up in your face, crashing your program (if you're lucky) or giving you garbage results (if you're not). (Yes, crashing the program is a good outcome; it lets you know there's a problem.)
If you really need to do that for some reason, it's better to define a union with int and float members, or to use memcpy() to copy the contents of a float object into an int object.
But it very rarely makes sense to do that kind of thing. If you want to examine the representation of a float object, it's better to treat it as an array of unsigned char something that the language standard explicitly permits.
6.5.16.1 Simple assignment
the left operand has atomic, qualified, or unqualified pointer type, and (considering
the type the left operand would have after lvalue conversion) 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.
So, a = f is a constraint violation and invokes undefined behavior.
In second case you are making f (by casting it)to be compatible to a's type. It is legal to do a casting in C (not sure about other languages).
But it should be noted that after casting f is still pointer to float and you have to cast it every time when you will assign it to a.
a = (int*) f; makes explicit that you want to cast a float* pointer to an int* pointer. Without it, you'll receive an incompatible pointer types error.
Your code will compile (at least in my linux and gcc). But you will get a warning.
If you use a = f; and then use a somewhere in your code, you will get erroneous data, because a float is stored in a different format in memory. Even if you do the casting first you probably will get erroneous results, but the compiler sees your casting and assumes you know what you are doing.
a = f; //assignment
// is a constraint violation
a = (int *) f; //cast + assignment
Explicitly casting float pointer to int pointer.simply hides compiler warnings or errors.
but very well might crash when running as the sizes of what the program expects when dereferencing the pointer differs from reality.