Sorting through a retired engineers code and I encountered a fairly simple macro, but my C knowledge isn't great.
#define mem32(addr) (*(unsigned long volatile *)(addr))
Am I correctly calling this a type casting pointer dereference?
It types casts addr and then derefences it? Or the other way around? Does it matter?
Does order matter for type qualifiers and type specifiers? I had assumed (before this) type qualifiers had to precede type specifiers, but that must not be the case
Syntax question. What's the purpose of the 2nd * ?
Am I correctly calling this a type casting pointer dereference?
Yes.
It types casts addr and then derefences it? Or the other way around? Does it matter?
The *(unsigned long volatile *)(addr) is typecasting addr and then dereferencing it.
Does order matter for type qualifiers and type specifiers?
No. Order doesn't matter. C11 section ยง6.7.2/2
[...] the type specifiers may occur in any order, possibly
intermixed with the other declaration specifiers. [...]
The cast (unsigned long volatile *)(addr) happens before the dereferencing.
And no, the order of the words unsigned, long, and volatile does not matter as long as no further operators are mixed in. I. e. volatile int* and int volatile* are the same, but int * volatile is something different.
On a high level, the purpose of this macro is to take any pointer, and read the first four bytes from that memory address.
However, this invokes undefined behavior if the pointer that's passed to mem32 is neither a pointer to long or char! This is due to strict aliasing rules. Older compilers used to safely generate the intended code for this macro, but modern compilers may just optimize the code using that macro away if they prove a type mismatch. So, don't use this in new code.
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.
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.
unsigned int addr = 0x1000;
int temp = *((volatile int *) addr + 3);
Does it treat the incremented pointer (ie addr + 3 * sizeof(int)), as a pointer to volatile int (while dereferencing). In other words can I expect the hardware updated contents of say (0x1012) in temp ?
Yes.
Pointer arithmetic does not affect the type of the pointer, including any type qualifiers. Given an expression of the form A + B, if A has the type qualified pointer to T and B is an integral type, the expression A + B will also be a qualified pointer to T -- same type, same qualifiers.
From 6.5.6.8 of the C spec (draft n1570):
When an expression that has integer type is added to or subtracted from a pointer, the
result has the type of the pointer operand.
Presuming addr is either an integer (variable or constant) with a value your implementation can safely convert to an int * (see below).
Consider
volatile int a[4] = [ 1,2,3,4};
int i = a[3];
This is exactly the same, except for the explicit conversion integer to volatile int * (a pointer to ...). For the index operator, the name of the array decays to a pointer to the first element of a. This is volatile int * (type qualifiers in C apply to the elements of an array, never the array itself).
This is the same as the cast. Leaves 2 differences:
The conversion integer to "pointer". This is implementation defined, thus if your compiler supports it correctly (it should document this), and the value is correct, it is fine.
Finally the access. The underlying object is not volatile, but the pointer/resp. access. This actually is a defect in the standard (see DR476 which requires the object to be volatile, not the access. This is in contrast to the documented intention (read the link) and C++ semantics (which should be identical). Luckily all(most all) implementations generate code as one would expect and perform the access as intended. Note this is a common ideom on embedded systems.
So if the prerequisites are fulfilled, the code is correct. But please see below for better(in terms of maintainability and safety) options.
Notes: A better approach would be to
use uintptr_t to guarantee the integer can hold a pointer, or - better -
#define ARRAY_ADDR ((volatile int *)0x1000)
The latter avoids accidental modification to the integer and states the implications clear. It also can be used easier. It is a typical construct in low-level peripheral register definitions.
Re. your incrementing: addr is not a pointer! Thus you increment an integer, not a pointer. Left apart this is more to type than using a true pointer, it also is error-prone and obfuscates your code. If you need a pointer, use a pointer:
int *p = ARRAY_ADDR + 3;
As a personal note: Everybody passing such code (the one with the integer addr) in a company with at least some quality standards would have a very serious talk with her team leader.
First note that conversions from integers to pointers are not necessarily safe. It is implementation-defined what will happen. In some cases such conversions can even invoke undefined behavior, in case the integer value cannot be represented as a pointer, or in case the pointer ends up with a misaligned address.
It is safer to use the integer type uintptr_t to store pointers and addresses, as it is guaranteed to be able to store a pointer for the given system.
Given that your compiler implements a safe conversion for this code (for example, most embedded systems compilers do), then the code will indeed behave as you expect.
Pointer arithmetic will be done on a type that is volatile int, and therefore + 3 means increase the address by sizeof(volatile int) * 3 bytes. If an int is 4 bytes on your system, you will end up reading the contents of address 0x100C. Not sure where you got 0x1012 from, mixing up decimal and hex notation?
Looking at fm_transmitter source code, I came across this macro, which is used quite often.
#define ACCESS(base, offset) *(volatile unsigned*)((int)base + offset)
I guess its a sum of base and offset casted as int, then re-casted into unsigned pointer and then again pointer?
This macro provides access to an offset that is measured in bytes. You could rewrite it as
#define ACCESS(base, offset) *(volatile unsigned*)&((char*)base)[offset]
only that the original version does the arithmetic via the int type instead of the char* type.
Note that use of this macro likely invokes undefined behavior: The resulting pointer is not guaranteed to be properly aligned, and if the data was written as some other type than an int variant, it's a violation of strict aliasing rules as well.
Also, the choice of int to do the pointer arithmetic is a very poor choice, the calculation should at least be done via size_t or uintptr_t to guarantee that the pointer is not truncated during the cast to the integer type. My rewritten version does not have this specific problem, however, the danger of undefined behavior remains with both versions.
Finally, as Olaf rightly noted in the comments, it's also a bad idea to cast to volatile unsigned* since the width of this type is implementation defined. A cast to volatile uint32_t* would likely be more appropriate for communication with hardware.
What does the following expression mean?
unsigned char *res = malloc(5);
Now I cast res:
(long)res
What does this casting mean?
Using that value will interpret the address to which res points (which is just a number anyway) as a long.
It will work most of the time but it's not completely okay (depends a lot on how you're using it). For example if you simply want to print it, you can get away with
printf("%p", res);
As a rule of thumb: treat any cast with suspicion.
The allocated memory is not read, you're just casting the pointer to the memory to a long.
This doesn't directly answer your question but is a useful bit of information that is more-or-less relevant to your siutation.
A cast from a pointer type to an integer type is implementation defined (that means the implementation decides what happens when you cast a pointer to an integer). C99 implementations that do support some type of reversible conversion should also provide two types found in <stdint.h> specifically for converting pointers to integers, namely uintptr_t and intptr_t. If your implementation provides these two types, then you can safely convert a pointer to these types and back to the original pointer type.
Since these types are implementation defined, you will need to check your implementations documentation for what the underlying types are.