Pointer to int == Pointer to char (somewhat)? - c

In this code given below , i have declared a pointer to int and we all know that memcpy returns a void pointer to destination string , so if ptr is a pointer to int then why printf("%s",ptr); is totally valid , ptr is not a pointer to char after all.
#include <stdio.h>
#include <string.h>
//Compiler version gcc 6.3.0
int main()
{
char a1[20] ={0} , a2[20] ={0};
int *ptr;
fgets(a1,20,stdin);
fgets(a2,20,stdin);
ptr = memcpy(a1,a2,strlen(a2)-1);
printf("%s \n",ptr);
if(ptr)
printf("%s",a1);
return 0;
}

First consider ptr = memcpy(a1,a2,strlen(a2)-1);. memcpy is declared as void *memcpy(void * restrict, const void * restrict, size_t), so it accepts the a1 and a2 passed to it because pointers to any unqualified object type may be converted to void * or to const void *. (Pointers to object types qualified with const may also converted to const void *.) This follows from the rules for function calls in C 2018 6.5.2.2 7 (arguments are converted to the parameter types as if by assignment) and 6.5.16 1 (one operand is a possibly-qualified void * and the left has all the qualifiers of the right) and 6.5.16 2 (the right operand is converted to the type of the left).
Then memcpy returns a void * that is its first argument (after conversion to void *), and we attempt to assign this to ptr. This satisfies the constraints of the assignment (one of the operands is a void *), so it converts the pointer to the type of ptr, which is int *. This is governed by 6.3.2.3 7:
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…
Since a1 is a char array with no alignment requested, it could have any alignment. It might not be suitable for an int. If so, then the C standard does not define the behavior of the program, per the above.
If a1 happens to be suitably aligned for an int or the C implementation successfully converts it anyway, we go on to printf("%s \n",ptr);.
printf is declared as int printf(const char * restrict, ...). For arguments corresponding to ..., there is no parameter type to convert to. Instead, the default argument promotions are performed. These affect integer and float arguments but not pointer arguments. So ptr is passed to printf unchanged, as an int *.
For a %s conversion, the printf rules in 7.21.6.1 8 say “the argument shall be a pointer to the initial element of an array of character type.” While ptr is pointing to the same place in memory as the initial element, it is a pointer to an int, not a pointer to the initial element. Therefore, it is the wrong type of argument.
7.21.6.1 9 says “… If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.” Therefore, the C standard does not define the behavior of this program.
In many C implementations, pointers are simple addresses in memory, int * and char * have the same representation, and the compiler will tolerate passing an int * for a %s conversion. In this case, printf receives the address it is expecting and will print the string in a1. That is why you observed the result you did. The C standard does not require this behavior. Because printf is part of the standard C library, the C standard permits a compiler to treat it specially when it is called with external linkage. The compiler could, hypothetically, treat the argument as having the correct type (even though it does not) and change the printf call into a loop that used ptr as if it were a char *. I am not aware of any compilers that would generate undesired code in this case, but the point is the C standard does not prohibit it.

why printf("%s",ptr); is totally valid
It isn’t - it may work as expected, but it isn’t guaranteed to. By passing an argument of the wrong type to printf, you’ve invoked undefined behavior, which simply means the compiler isn’t required to handle the situation in any particular way. You may get the expected output, you may get garbage output, you may get a runtime error, you may corrupt the state of your system, you may open a black hole to the other side of the universe.

Related

What does it mean that void* has the same representation and memory alignment as char*?

I've been reading some articles about void* type pointers and found this requirement from the Standard.
6.2.5.27:
A pointer to void shall have the same representation and alignment requirements as a pointer to a character type.39) Similarly, pointers to qualified or unqualified versions of compatible types shall have the same representation and alignment requirements.
I see that the Standard does not guarantee all pointer types have the same length, so the bottom line here is that a void* pointer has the same length and alignment rules as char*, right?
What I don't get is the footnote 39), which says
The same representation and alignment requirements are meant to imply interchangeability as arguments to functions, return values from functions, and members of unions.
My questions are:
What does it mean by "interchangeability"? Does it say the argument and the return values of a function void* Func(void*) can both be char*?
If so, is it an implicit conversion made by the compiler?
And what is it about the members of unions? I really don't get a grasp of the meaning of this. Can anyone give me a simple example?
In C any data pointer can be passed to a function that expects a void * and a void * can be stored to any pointer type. There is an implicit conversion between void * and other pointer types. But this does not mean that this conversion is harmless. On some architectures where void * and int * have a different representation, converting from int * to void * and then back to int * is specified as producing the same pointer value, but the converse does not hold: converting a void * to int * and back to void * may produce a different value, especially if the void * was not obtained by converting an int *.
Interchangeability means that this implicit conversion does not change the representation of the pointer. the conversion can be operated both ways successfully: converting a character pointer to void * and back produces the same pointer and vice versa.
Here is an example:
#include <assert.h>
#include <stdio.h>
#include <string.h>
int main() {
char *s = "abc";
char *s1;
void *p;
void *p1;
assert(sizeof(p) == sizeof(s));
memcpy(&p, &s, sizeof(p));
p1 = s;
assert(p == p1);
memcpy(&s1, &p1, sizeof(s1));
assert(s == s1);
return 0;
}
Note however that this does not imply that !memcmp(&p1, &s, sizeof(p1)) because pointers could have padding bits. Neither can you violate the strict aliasing rule by casting through a void *:
float f = 1.0; unsigned int i = *(int *)(void *)&f; incorrect.
float f = 1.0; unsigned int i; memcpy(&i, &f, sizeof(i)); correct if sizeof(int) == sizeof(float) but may produce a trap value.
What does it mean by "interchangeability"? Does it say the argument and the return values of a function void* Func(void*) can both be char*?
Yes, that is what it says, but it is non-normative text that conflicts with the normative text of the standard. Let’s discuss question 2 and then come back to this.
If so, is it an implicit conversion made by the compiler?
No, not in the situations intended to be addressed by this note.
If there is a visible declaration of void *Func(void *);, and you execute:
char *p = something;
char *q = Func(p);
then the argument p is converted void * and the returned value is converted to char *. But these conversions occur as part of the normal operations of function calls and assignments; they have nothing to do with the types having the same representation or being interchangeable. For example, if you executed code like the above but with int * instead of char *, the conversions would occur between int * and void * even if they do not have the same representations and are not interchangeable. The argument conversion is made because the compiler knows the parameter type of Func, so it performs the conversion as required by the rules for function calls, and the assignment conversion is made because the compiler knows the type of the destination of the assignment, so it performs the conversion as required by the rules for assignment.
However, suppose we have this code:
char *Func(char *);
char *p = something;
char *q = Func(p);
but Func is in fact defined in its library source code as void *Func(void *);. Then the rule in C 2018 6.2.5 281 applies. In the calling code, the compiler is told the parameter and the return type are char *, so no conversion is performed in either case. When passing the char * argument, the compiler passes exactly the bytes that represent a char *. In the receiving function, the code expects a void *. Since the bytes representing a char * are exactly the same as the bytes representing a void *, with the same meaning in regard to the represented address, this works: The function receives the bytes it expects to receive, with the intended meaning. Similarly, when the function returns the bytes for a void * and the calling code interprets those bytes as a char *, it works because the bytes are the same, with the same meaning.
Getting back to question 1, this example where Func is called using the type char *Func(char *) but is defined using the type void *Func(void *) violates the normative part of the C standard. C 2018 6.5.2.2 6 says:
If the function is defined with a type that includes a prototype, and… the types of the arguments after promotion are not compatible with the types of the parameters, the behavior is undefined.
char * is not compatible with void *, so the behavior is not defined by this rule. However, if the calling code is in one translation unit and the called code is in another translation unit, and no information about the calling code or the called function (notably no type information) is passed between translation units except for linking the name to the function, then it is impossible for the C implementation to distinguish our example code from code in which the function is called using a type compatible with its definition. In particular, the fact that char * has the same representation as void * means that the result of compiling the calling code must be identical whether it uses char *Func(char *) or void *Func(void *) (given the caveat that no type information ais passed between translation units), and it means that the result of compiling the function definition must be identical whether it is defined using char * or void *. In other words, a rule of the C standard says the behavior is not defined, but it is logically impossible in this situation for the compiler to compile the example code differently from the code with defined behavior.
I conjecture that this note in the standard may have been the result of the committee, or at least one or more members of it, wanting to say that, at least in some senses, char * could be used in place of void * and vice-versa, but that the committee did not have the time or motivation or other opportunity to draft formal language for this and make it a normative part of the standard, so it settled for making it a note.
And what is it about the members of unions? I really don't get a grasp of the meaning of this. Can anyone give me a simple example?
Consider this union:
union foo
{
void *v;
char *c;
float *f;
} u;
When we write into one union member, as with u.v = &a;, and read from another union member, as with char *p = u.c;, the bytes in the union are reinterpreted in the new type (C 2018 6.5.2.3 3 and note 99). Since void * and char * have the same representation, this reinterpretation must produce the same value. Thus, we are guaranteed that:
char a;
u.v = &a;
printf("%d\n", u.c == &a);
prints “1”. On the other hand, we are not guaranteed that for this code:
float f;
u.v = &f;
printf("%d\n", u.f == &f);
In this code, when &f is converted to void *, a void * might have a different representation from a float *, so the bytes representing &f may be different from the bytes representing (void *) &f. The latter are the bytes stored in u.v. When those bytes are read as u.f and reinterpreted as a float *, they might represent a different value, so the comparison might not evaluate as true.
Footnote
1 The question cites “6.2.5.27,” but the quoted passage is found in clause 6.2.5, paragraph 28, of the official 2018 C standard. The note cited as note 39 is found as note 49.
A pointer is just an address in the memory. You can think the memory is continuous region of a byte, which is very large (e.g. on a 32 bit process it will be 4 GB but usually the process is not able to use the whole depend on the system).
That mean the value of a pointer is actually an integer represent the zero-based index of a byte in the memory (e.g. pointer with value 0 refer to the first byte in the memory but in really you will not be able to de-reference this address due to it is a null pointer).
When you de-reference a pointer what it does is reading/writing to that address. The size to read/write is depend on the type of pointer. If a pointer is int and its size on that system is 32 bits, which is 4 bytes; it will read/write 4 bytes starting at that address. What alignment means is how the value stored in the memory. Let say if the value stored in memory need to be 16-bytes alignment that means its starting address must be multiply with 16.
What I explain here is just a high-level of the pointer, which should be enough for getting started. In reality it have a lot of things related to it like memory protection, paging, etc.

C - Conversion behavior between two pointers

Update 2020-12-11: Thanks #"Some programmer dude" for the suggestion in the comment.
My underlying problem is that our team is implementing a dynamic type storage engine. We allocate multiple char array[PAGE_SIZE] buffers with 16-aligned to store dynamic types of data (there is no fixed struct). For efficiency reasons, we cannot perform byte encoding or allocate additional space to use memcpy.
Since the alignment has been determined (i.e., 16), the rest is to use the cast of pointer to access objects of the specified type, for example:
int main() {
// simulate our 16-aligned malloc
_Alignas(16) char buf[4096];
// store some dynamic data:
*((unsigned long *) buf) = 0xff07;
*(((double *) buf) + 2) = 1.618;
}
But our team disputes whether this operation is undefined behavior.
I have read many similar questions, such as
Why does -Wcast-align not warn about cast from char* to int* on x86?
How to cast char array to int at non-aligned position?
C undefined behavior. Strict aliasing rule, or incorrect alignment?
SEI CERT C C.S EXP36-C
But these are different from my interpretation of the C standard, I want to know if it’s my misunderstanding.
The main confusion is about the section 6.3.2.3 #7 of C11:
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 68) for the referenced type, the behavior is undefined.
68) In general, the concept ‘‘correctly aligned’’ is transitive: if a pointer to type A is correctly aligned for a pointer to type B, which in turn is correctly aligned for a pointer to type C, then a pointer to type A is correctly aligned for a pointer to type C.
Does the resulting pointer here refer to Pointer Object or Pointer Value?
In my opinion, I think the answer is the Pointer Object, but more answers seem to indicate the Pointer Value.
Interpretation A: Pointer Object
My thoughts are as follows: A pointer itself is an object. According to 6.2.5 #28, different pointer may have different representation and alignment requirements. Therefore, according to 6.3.2.3 #7, as long as two pointers have the same alignment, they can be safely converted without undefined behavior, but there is no guarantee that they can be dereferenced.
Express this idea in a program:
#include <stdio.h>
int main() {
char buf[4096];
char *pc = buf;
if (_Alignof(char *) == _Alignof(int *)) {
// cast safely, because they have the same alignment requirement?
int *pi = (int *) pc;
printf("pi: %p\n", pi);
} else {
printf("char * and int * don't have the same alignment.\n");
}
}
Interpretation B: Pointer Value
However, if the C11 standard is talking about Pointer Value for referenced type rather than Pointer Object. The alignment check of the above code is meaningless.
Express this idea in a program:
#include <stdio.h>
int main() {
char buf[4096];
char *pc = buf;
/*
* undefined behavior, because:
* align of char is 1
* align of int is 4
*
* and we don't know whether the `value` of pc is 4-aligned.
*/
int *pi = (int *) pc;
printf("pi: %p\n", pi);
}
Which interpretation is correct?
Interpretation B is correct. The standard is talking about a pointer to an object, not the object itself. "Resulting pointer" is referring to the result of the cast, and a cast does not produce an lvalue, so it's referring to the pointer value after the cast.
Taking the code in your example, suppose that an int must be aligned on a 4 byte boundary, i.e. it's address must be a multiple of 4. If the address of buf is 0x1001 then converting that address to int * is invalid because the pointer value is not properly aligned. If the address of buf is 0x1000 then converting it to int * is valid.
Update:
The code you added addresses the alignment issue, so it's fine in that regard. It however has a different issue: it violates strict aliasing.
The array you defined contains objects of type char. By casting the address to a different type and subsequently dereferencing the converted type type, you're accessing objects of one type as objects of another type. This is not allowed by the C standard.
Though the term "strict aliasing" is not used in the standard, the concept is described in section 6.5 paragraphs 6 and 7:
6 The effective type of an object for an access to its stored value is the declared type of the object, if any.87) If a
value is stored into an object having no declared type through an
lvalue having a type that is not a character type, then the type of
the lvalue becomes the effective type of the object for that access
and for subsequent accesses that do not modify the stored value. If a
value is copied into an object having no declared type using memcpy
or memmove, or is copied as an array of character type, then the
effective type of the modified object for that access and for
subsequent accesses that do not modify the value is the effective type
of the object from which the value is copied, if it has one. For all
other accesses to an object having no declared type, the effective
type of the object is simply the type of the lvalue used for the
access.
7 An object shall have its stored value accessed only by an lvalue expression that has one of the following types:88)
a type compatible with the effective type of the object,
a qualified version of a type compatible with the effective type of the object,
a type that is the signed or unsigned type corresponding to the effective type of the object,
a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a
subaggregate or contained union), or
a character type.
...
87 ) Allocated objects have no declared type.
88 ) The intent of this list is to specify those circumstances in which
an object may or may not be aliased.
In your example, you're writing an unsigned long and a double on top of char objects. Neither of these types satisfies the conditions of paragraph 7.
In addition to that, the pointer arithmetic here is not valid:
*(((double *) buf) + 2) = 1.618;
As you're treating buf as an array of double when it is not. At the very least, you would need to perform the necessary arithmetic on buf directly and cast the result at the end.
So why is this a problem for a char array and not a buffer returned by malloc? Because memory returned from malloc has no effective type until you store something in it, which is what paragraph 6 and footnote 87 describe.
So from a strict point of view of the standard, what you're doing is undefined behavior. But depending on your compiler you may be able to disable strict aliasing so this will work. If you're using gcc, you'll want to pass the -fno-strict-aliasing flag
The Standard does not require that implementations consider the possibility that code will ever observe a value in a T* that is not aligned for type T. In clang, for example, when targeting platforms whose "larger" load/store instructions do not support unaligned access, converting a pointer into a type whose alignment it doesn't satisfy and then using memcpy on it may result in the compiler generating code which will fail if the pointer isn't aligned, even though memcpy itself would not otherwise impose any alignment requirements.
When targeting an ARM Cortex-M0 or Cortex-M3, for example, given:
void test1(long long *dest, long long *src)
{
memcpy(dest, src, sizeof (long long));
}
void test2(char *dest, char *src)
{
memcpy(dest, src, sizeof (long long));
}
void test3(long long *dest, long long *src)
{
*dest = *src;
}
clang will generate for both test1 and test3 code which would fail if src or dest were not aligned, but for test2 it will generate code which is bigger and slower, but which will support arbitrary alignment of the source and destination operands.
To be sure, even on clang the act of converting an unaligned pointer into a long long* won't generally cause anything weird to happen by itself, but it is the fact that such a conversion would produce UB that exempts the compiler of any responsibility to handle the unaligned-pointer case in test1.

Why void pointer if pointers can be casted into any type(in c)?

I want to understand the real need of having a void pointer, for example in the following code, i use casting to be able to use the same ptr in different way, so why is there really a void pointer if anything can be casted?
int main()
{
int x = 0xAABBCCDD;
int * y = &x;
short * c = (short *)y;
char * d = (char*)y;
*c = 0;
printf("x is %x\n",x);//aabb0000
d +=2;
*d = 0;
printf("x is %x\n",x);//aa000000
return 0;
}
Converting any pointer type to any other pointer type is not supported by base C (that is, C without any extensions or behavior not required by the C standard). The 2018 C standard says in clause 6.3.2.3, paragraph 7:
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…
In that passage, we see two limitations:
If the pointer is not properly aligned, the conversion may fail in various ways. In your example, converting an int * to a short * is unlikely to fail since int typically has stricter alignment than short. However, the reverse conversion is not supported by base C. Say you define an array with short x[20]; or char x[20];. Then the array will be aligned as needed for a short or char, but not necessarily as needed for an int, in which case the behavior of (int *) x would not be defined by the C standard.
The value that results from the conversion mostly unspecified. This passage only guarantees that converting it back yields the original pointer (or something equivalent). It does not guarantee you can do anything useful with the pointer without converting it back—you cannot necessarily use a pointer converted from int * to access a short.
The standard does make some additional guarantees about certain pointer conversions. One of them is in the continuation of the passage above:
… When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.
So you can use a pointer converted from int * to access the individual bytes that represent an int, and you can do the same to access the bytes of any other object type. But that guarantee is made only for access the individual bytes with a character type, not with a short type.
From the above, we know that after the short * c = (short *)y; in your example, y does not necessarily point to any part of the x it originated from—the value resulting from the pointer conversion is not guaranteed to work as a short * at all. But, even if it does point to the place where x is, base C does not support using c to access those bytes, because 6.5 7 says:
An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
— a type compatible with the effective type of the object,
— a qualified version of a type compatible with the effective type of the object,
— a type that is the signed or unsigned type corresponding to the effective type of the object,
— a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
— a character type.
So the *c = 0; in your example is not supported by C for two reasons: c does not necessarily point to any part of x or to any valid address, and, even if it does, the behavior of modifying part of the int x using short type is not defined by the C standard. It might appear to work in your C implementation, and it might even be supported by your C implementation, but it is not strictly conforming C code.
The C standard provides the void * type for use when a specific type is inadequate. 6.3.2.3 1 makes a similar guarantee for pointers to void as it does for pointers to objects:
A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.
void * is used with routines that must work with arbitrary object types, such as qsort. char * could serve this purpose, but it is better to have a separate type that clearly denotes no specific type is associated with it. For example, if the parameter to a function were char *p, the function could inadvertently use *p and get a character that it does not want. If the parameter is void *p, then the function must convert the pointer to a specific type before using it to access an object. Thus having a special type for “generic pointers” can help avoid errors as well as indicate intent to people reading the code.
Why void pointer if pointers can be casted into any type(in c)?
C does not specify that void* can be cast into a pointer of any type. A void * may be cast into a pointer to any object type. IOWs, a void * may be insufficient to completely store a function pointer.
need of having a void pointer
A void * is a universal pointer for object types. Setting aside pointers to const, volatile, etc. concerns, functions like malloc(), memset() provide universal ways to allocate and move/set data.
In more novel architectures, a int * and void * and others have different sizes and interpretations. void* is the common pointer type for objects, complete enough to store information to re-constitute the original pointer, regardless of object type pointed to.

Why do I get warnings when I try to assign the address of a variable to a pointer that was declared to point to a variable of a different type?

Take a look at the following program. What I don't understand is why do I have to cast the address of the variable x to char* when it actually would be absolutely useless if you think about it for a second. All I really need is only the address of the variable and all the necessary type information is already in place provided by the declaration statement char* ptr.
#include <stdio.h>
int main(void) {
int x = 0x01020309;
char* ptr = &x; /* The GCC compiler is going to complain here. It will
say the following: "warning: initialization from
incompatible pointer type [enabled by default]". I
need to use the cast operator (char*) to make the
compiler happy. But why? */
/* char* ptr = (char*) &x; */ /* this will make the compiler happy */
printf("%d\n", *ptr); /* Will print 9 on a little-endian machine */
return 0;
}
The C Standard, 6.2.5 Types, paragraph 28 states:
A pointer to void shall have the same representation and
alignment requirements as a pointer to a character type.
Similarly, pointers to qualified or unqualified versions of
compatible types shall have the same representation and
alignment requirements. All pointers to structure types shall have
the same representation and alignment requirements as each other.
All pointers to union types shall have the same
representation and alignment requirements as each other.
Pointers to other types need not have the same representation or alignment requirements.
Since different types of pointers can have differing implementations or constraints, you can't assume it's safe to convert from one type to another.
For example:
char a;
int *p = &a
If the implementation has an alignment restriction on int, but not on char, that would result in a program that could fail to run.
This is because pointers of different types point to blocks of memory of different sizes even if they point to the same location.
&x is of type int* which tells the compiler the number of bytes (depending on sizeof(int)) to read when getting data.
Printing *(&x) will return the original value you entered for x
Now if you just do char* ptr = &x; the compiler assigns the address in &x to your new pointer (it can as they are both pointers) but it warns you that you are changing the size of the block of memory being addressed as a char is only 1 byte. If you cast it you are telling the compiler that this is what you intend.
Printing *(ptr) will return only the first byte of the value of x.
You are correct that it makes no practical difference. The warning is there to inform you that there might be something fishy with that assignment.
C has fairly strong type-checking, so most compilers will issue a warning when the types are not compatible.
You can get rid of the warning by adding an explicit cast (char*), which is you saying:
I know what I'm doing, I want to assign this value to my char* pointer even if the types don't match.
Its just simple as you assign integer type to character. similarly you are trying to assign integer type pointer to character type pointer.
Now why is so because this is how c works, if you increment a character pointer it will give you one byte next address and incrementing integer pointer will give you 2 byte next address.
According to your code, x is of type int. So the pointer that points to x should be of type int *. Compiler gives such error because you use a pointer which is not int *.
So make your pointer either int *, or void * then you don't need cast.

Can you ever assume typecasting pointers is safe?

I've heard from many people that you cannot guarantee typecasting will be performed lossless. Is that only true if you don't know your processor, that is, you haven't verified the number of bytes used for your data types? Let me give an example:
If you execute the following:
typedef struct
{
int i;
char c;
float f;
double d;
} structure;
size_t voidPtrSz = sizeof(void *);
size_t charPtrSz = sizeof(char *);
size_t intPtrSz = sizeof(char *);
size_t floatPtrSz = sizeof(float *);
size_t doublePtrSz = sizeof(double *);
size_t structPtrSz = sizeof(structure *);
size_t funcPtrSz = sizeof(int (*)(float, char));
printf("%lu\n", voidPtrSz);
printf("%lu\n", charPtrSz);
printf("%lu\n", intPtrSz);
printf("%lu\n", floatPtrSz);
printf("%lu\n", doublePtrSz);
printf("%lu\n", structPtrSz);
printf("%lu\n", funcPtrSz);
…and the output is the following…
4
4
4
4
4
4
4
Can you assume that in all cases you can typecast a specific data type pointer to another data type pointer safely? For example, if you execute this:
int foo(float, char)
{
}
void *bar(void)
{
return (void *)foo;
}
int (*pFunc)(float, char) = bar();
Can you assume with certitude that pFunc has the address of foo?
Regarding your specific code example, let's refer to section 6.3.2.3 of the C99 language standard:
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.
Note that a pointer-to-function is not the same as pointer-to-object. The only mention of pointer-to-function conversions is:
A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the pointed-to type, the behavior is undefined.
So your code example invokes undefined behaviour.
If we avoid function-pointer conversions, the following paragraph explains everything:
A pointer to an object or incomplete type may be converted to a pointer to a different object or incomplete type. If the resulting pointer is not correctly aligned for the pointed-to type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer.
Note: Converting between pointer types is a separate issue from converting and then dereferencing (in general, that's only valid if you're converting to char * and then dereferencing.)
Can you assume that in all cases you can typecast a specific data type pointer to another data type pointer safely?
Any data pointer can be safely cast to char* or void*. Any char* or void* thus created can be cast back to its original type. Any other data pointer cast leads to undefined behavior when indirection is performed on the pointer.
Any function pointer type can be cast to any other function pointer type, although you should not call a function through the wrong type. Casting a function pointer to void* or any other data pointer type results in undefined behavior.
Is that only true if you don't know your processor, that is, you haven't verified the number of bytes used for your data types?
Even then, you're not safe. When the C standard says a construct has undefined behavior, compiler writers are free to handle the construct as they wish. The result is that even though you think you know a construct with UB will be handled because you know the target CPU, optimizing compilers may cut corners and generate very different code than you expect.
#Oli Charlesworth gives you a great answer.
I hope I can shed a little light on what pointer are so you can better understand pointer mechanics:
A pointer is an address. This address is the address of the first byte of your data. The type of the pointer specifies how many bytes starting from that first byte are part of the data and how those bytes encode the data.
For instance, on gcc x86, if you have a int * p, the value held by p tells the starting address of data, and the type of p (int *) tells that at that address he will interpret 4 bytes (in little endian byte-order) in two's complement signed number representation.
A void * pointer is a "generic pointer". The pointer still holds an address, but the pointer type doesn't specify what kind of data you find there, or even how many bytes form the data, so you can never access data through a void * pointer, but as answered before, you can safely convert between a pointer to void and a pointer to any incomplete or object type.
A pointer to function holds the address of a function, and the type of the pointer tells how to call that function (what parameters and of what kind) and what the function returns.

Resources