Can I cast a pointer to char? - c

Can I cast void* to char[], or is this illegal? I just want to take a look at the bits. I'm aware of intptr_t, but I would rather not use a typedef that may or may not exist on a given platform.

Can I cast a pointer to char?
Yes, but that is only useful in esoteric circumstances, such as checking the low bits to see the alignment.
Can I cast void* to char[], or is this illegal?
Casting to char [] violates the constraint for the cast operator in C 2018 6.5.4 2:
Unless the type name specifies a void type, the type name shall specify atomic, qualified, or unqualified scalar type, and the operand shall have scalar type.
char [] is not any of those scalar types.
You can cast to char *, but that will not give you the bytes of the pointer; it will give you the pointer as a pointer to char.
I'm aware of intptr_t, but I would rather not use a typedef that may or may not exist on a given platform.
uintptr_t is generally preferable to intptr_t, to avoid complications caused by the sign. There are few C implementations in which it would not exist.
However, there could be some. For that, you can convert not the pointer, but the address of the pointer to unsigned char *. That will give you a new pointer to the bytes of the pointer, and then you can use that to examine those bytes. Again, prefer the unsigned type, unsigned char *, to avoid complications from signedness.

Related

What is the appropriate arithmetic type for a pointer?

In my code I have two char pointer, one to a string and the other as an error indicator for strtoumax() and strtod(). I am currently using the type size_t (aka unsigned long) to calculate the difference between them. Is there any type designed to specifically match pointer type size on every machine? Or do I have to check it myself with macros?
For pointer difference use ptrdiff_t. If you're just trying to store a pointer as an integer, use uintptr_t (or intptr_t).
In my code I have two char pointer, one to a string and the other as
an error indicator for strtoumax() and strtod(). I am currently using
the type size_t (aka unsigned long) to calculate the difference
between them.
Don't do that. If you want a pointer difference then compute a pointer difference:
#include <stdint.h>
// ...
ptrdiff_t difference = p2 - p1;
And note ptrdiff_t, which is the type of the result of a pointer difference.
If you want a difference in bytes instead of in units the size of the pointed-to type (including if the pointed-to type is incomplete, such as void) then first convert to pointers to char:
ptrdiff_t difference_in_bytes = (char *) p2 - (char *) p1;
(char is the smallest addressible unit of storage, but technically, it might be larger than 8 bits on some C implementations. CHAR_BIT will help you figure that out if you're concerned about such cases.)
Do not compute a pointer difference by converting to integer and performing integer arithmetic, because although the behavior of that is defined (+/- signed integer overflow), the meaning of the result is not.
Is there any type designed to specifically match pointer
type size on every machine? Or do I have to check it myself with
macros?
Yes. In stdint.h there are definitions of uintptr_t and intptr_t, which can support round-trip pointer to integer to pointer conversions without data loss. But C does not define the meaning of the value resulting from converting a pointer to an integer, so these are best used as opaque types.
To store pointers as integers you can use intptr_t and uintptr_t declared in the header <stdint.h>.
From the C Standard (7.20.1.4 Integer types capable of holding object pointers)
1 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
The following type designates an unsigned 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:
uintptr_t
These types are optional.
To store difference between two pointers you can use ptrdiff_t declared in the header <stddef.h>.
Pay attention to that you may calculate difference between two pointers if they both point to elements of the same array or one past the last element. Otherwise you will get undefined behavior.

C Guarantees Re: Pointers to Void and Character Types, Typecasting

My best-effort reading of the C specification (C99, primarily) makes me think that it is valid to cast (or implicitly convert, where void *'s implicit conversion behavior applies), between any of these types:
void *, char *, signed char *, unsigned char *
I expect that this will trigger no undefined behavior, and that those pointers are guaranteed to have the same underlying representation.
Consequently, it should be possible to take a pointer of either one of those four types that is already pointing to an address which can be legally dereferenced, typecast and/or assign it to one of the three char type pointers, and dereference it to access the same memory, with the only difference being whether your code will treat the data at that location as a char, signed char, or unsigned char.
Is this correct? Is there any version of the C standard (lack of void * type in pre-standardization C not withstanding) where this is not true?
P.S. I believe that this question is answered piecemeal in passing in a lot of other questions, but I've never seen a single clear answer where this is explicitly stated/confirmed.
Consequently, it should be possible to take a pointer of either one of those four types that is already pointing to an address which can be legally dereferenced, typecast and/or assign it to one of the three char type pointers, and dereference it to access the same memory, with the only difference being whether your code will treat the data at that location as a char, signed char, or unsigned char.
This is correct. In fact you could take a valid pointer to an object of any type and convert it to some of those three and access the memory.
You correctly mention the provision about void * and char * etc. having the same representation and alignment requirements, but that actually does not matter. That refers to the properties of the pointer itself, not the properties of the objects being pointed to.
The strict aliasing rule is not violated because that contains an explicit provision that a character type may be used to read or write any object.
Note that if we have for example, signed char ch = -2;, or any other negative value, then (unsigned char)ch may differ from *(unsigned char *)&ch. On a system with 8-bit characters, the former is guaranteed to be 254 but the latter could be 254, 253, or 130 depending on the numbering system in use.

Is explicit type conversion necessary in A a = (A) b;

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

Cast pointer to larger int

I have a pointer. On a 32-bit system it's 32 bits. On a 64-bit system it's 64 bits.
I have a long long integer field used as an identifier, and sometimes I want to use the pointer value in there. (I never cast back to a pointer - once I've cast it to the integer field, I only ever compare it for equality).
On both 32-bit and 64-bit systems, it seems safe to do this. (On larger pointered systems not so). Is that true?
And then, is there a way to make GCC not give the following warning only when building on platforms where this is safe (which is, at the moment, all target platforms)?
error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast]
According to the standard, there is no guarantee that a pointer fits in an integer type. In practical, otherwise, on mostly personnal computers, there exists several memory models. You can see pointer and integer types have not always the same size (even on "conventional" computers).
You should rather use the optional types intptr_t and uintptr_t, from C99.
C11 (n1570), § 7.20.1.4
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.
The following type designates an unsigned 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: uintptr_t.
Here is a small example:
#include <stdio.h>
#include <stdint.h>
int n = 42;
int *p = &n;
intptr_t i = (intptr_t)(void *)p;
int *q = (void *)i;
printf("%d\n", *q);
If you want to have an integer type that is guaranteed to be big enough to hold a pointer, consider intptr_t (signed) or uintptr_t. The standard guarantees that these datatypes are big enough to hold a pointer. Nothing else might be assumed, especially not that long int is long enough.

What is intptr_t,is it a type for integer or pointer?

It's defined in /usr/include/stdint.h:
typedef long int intptr_t;
is it supposed to be a type for integer or pointer?
It is a signed integer type that is big enough to hold a pointer.
It is a signed integer type that guaranteed to can hold a void* type.
And why there is also [u]intptr_t? Because:
Any valid pointer to void can be converted to intptr_t or uintptr_t
and back with no change in value. The C Standard
guarantees that a pointer to void may be converted to or from a
pointer to any object type and back again and that the result must
compare equal to the original pointer. Consequently, converting
directly from a char * pointer to a uintptr_t is allowed on implementations that support the uintptr_t.

Resources