I found that we can print the pointer value with %p format specifier. N1570 7.21.6.1(p8):
p The argument shall be a pointer to void. The value of the pointer is
converted to a sequence of printing characters, in an
implementation-defined manner
Since the pointer to void can be converted to pointer to any other object type I'm curious would the conversion by hand is necessary. Example:
struct test_t{
int a;
}
void foo(){
struct test_t *test_ptr = malloc(sizeof(*test_ptr));
printf("Pointer test_ptr = %p\n", test_ptr);
}
Here I did not convert it to void * assuming that the compiler does this for me. Is it conforming? Or I should convert such pointers to void * on my own like
printf("Pointer test_ptr = %p\n", (void *) test_ptr);
The Standard specifies that the representation of and alignment of void * is the same as char *. It is not specified if the void * representation/alignement is must be the same as any object type.
The version with (void *) is correct, and your original code causes undefined behaviour. (You already provided the relevant Standard quote so I have no need to add further quotes).
Since the pointer to void can be converted to pointer to any other object type I'm curious would the conversion by hand is necessary
"can be" means there is potential for a conversion to happen. Such a conversion only actually happens when it was requested , with the language standard specifying which constructs request a conversion.
For the arguments to printf, only the default argument promotions are applied. There are not any other conversions.
Related
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.
This question already has answers here:
printf("%p") and casting to (void *)
(7 answers)
Closed 5 years ago.
What does (void*) mean in the following code? I tried removing (void*) typecast but it still works just fine and prints the address of usrInt variable. Can you explain this, please?
#include <stdio.h>
int main(void) {
int usrInt = 0; // User defined int value
int* myPtr = NULL; // Pointer to the user defined int value
// Prompt user for input
printf("Enter any number: ");
scanf("%d", &usrInt);
// Output int value and location
printf("We wrote your number into variable usrInt.\n");
printf("The content of usrInt is: %d.\n", usrInt);
printf("usrInt's memory address is: %p.\n", (void*) &usrInt);
printf("\nWe can store that address into pointer variable myPtr.\n");
// Grab location storing user value
myPtr = &usrInt;
// Output pointer value and value pointed by pointer
printf("The content of myPtr is: %p.\n", (void*) myPtr);
printf("The content of what myPtr points to is: %d.\n", *myPtr);
return 0;
}
Normally, a cast to or from void * is simply redundant, because this conversion is implicit in C for any data pointer type.
Passing a pointer to a variadic function is a special case that requires the cast.
printf() is a variadic function. The printf() prototype looks like this:
int printf(const char *format, ...);
This means the compiler doesn't see any type declarations for parameters that come after format. Therefore, it doesn't know the type passed should be void *, so the conversion doesn't happen automatically.
Often, omitting the cast won't have any visible effect because in most implementations, all pointers have the exact same internal representation. But the C standard doesn't guarantee that, so implementations are possible where e.g. an int * would have a different representation when it is converted to a void *. If you omit the cast, your code is technically incorrect (it invokes undefined behavior for passing the wrong type for the conversion %p) and would lead to wrong results on platforms where representations of pointers to different types are different.
c11: 7.21.6 Formatted input/output functions (p8):
p - The argument shall be a pointer to void. The value of the pointer is converted to a sequence of printing characters, in an implementation-defined manner.
Dereferencing a float variable using a void pointer:
#include <stdio.h>
int main() {
float a = 7.5;
void *vp = &a;
printf("%f", *(float*)vp); /* Typecasting a void pointer to float for dereference */
printf("\n");
}
Output: 7.500000
Dereferencing a variable using an integer pointer:
#include <stdio.h>
int main() {
float a = 7.5;
int *ip = &a;
printf("%f", *(float*)ip); /* Typecasting an int pointer to float for dereference */
printf("\n");
}
Output: 7.500000
In both, the outputs are same. What is the reason to go for dereferencing of different datatype variable, when we are able to do by typecasting a normal pointer?
Converting any data pointer to void* pointer and back is guaranteed to give back original pointer.
From C11 standard draft N1570:
6.3.2.3 Pointers
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.
Converting data pointer to other data pointer than void* (int* in your example) may work. It depends on the compiler you are using and the system you are on. Not all systems might use same internal representation for different pointer types.
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. Otherwise, when converted back again, the result shall compare equal to the
original pointer. 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.
This is different from strict aliasing rules.
float a = 7.5;
int *ip=&a;
int i = *ip; // Dereferenced using invalid type
Code above breaks strict aliasing rule as dereferenced type is not the same as the original type. This results in undefined behaviour and is always invalid code.
A void pointer is a generic pointer which can hold the address of any type and can be typecast to any type.
In the first case, the program successfully compiled and ran without any warning or error, because using a void pointer to convert from one pointer type to another and then storing or casting it to the final type is safe without losing data.
But in the second case the GCC compiler generated a warning
prog.c: In function 'main':
prog.c:5:9: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
int *ip=&a;
^
clang compiler:
warning: incompatible pointer types initializing 'int *' with an expression of type 'float *' [-Wincompatible-pointer-types]
int *ip=&a;
^ ~~
The C11 Standard, 6.3.2.3, paragraph 7:
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 referenced type, the behavior
is undefined.
A void pointer is (sort of) untyped. It can point to anything without the compiler complaining. e.g. If you have an int variable, you can safely create a void pointer that points to this and pass it around. e.g.
int x = 10;
void *p = &x
is fine but
int x = 10;
float *p = &x;
will upset the compiler
This is especially useful for functions that operate on multiple pointer types or if you will decide what something is at runtime.
However, void pointers cannot be dereferenced (*) directly because the compiler doesn't know their type. So,
printf("%d\n", *p);
will break if p is void pointer. We have to know the size of what it points to to dereference it and this is done using a manual type cast (like what you've done).
In your specific case, you have a pointer that points to a float which you type cast back into float before printing it. So, you will get the same output. The void * pointer is not really playing a big role here.
An example of where you need a void * is the malloc function, If you look at the prototype, it returns a void *. i.e. a block of raw memory. You need to typecast this as a concrete type before you can do pointer arithmetic and dereferencing.
int main()
{
int a;
int* b;
a = 40;
b = &a;
printf("the address of a is %p, and the value of a is %d \n",&a, a);
return 0;
}
I find that both (void*)&a and &a print the same thing. So why do people still add (void*)? Is it just a habit?
You use specifier %p to print address stored in pointer, and this format specifier expects type to be void * . And as &a is of type int * , cast void * is used .
The printf() format specifier %p expects a void* type pointer. Since what you are passing might not be a void* type pointer, and the standard does not mandate for all pointers to have the same format, is is important that you cast a pointer to void* before passing it to printf.
For instance:
int* a = malloc(sizeof(int));
printf("a is %p",(void*)a);
Is done as best practice incase int* and void* are not similar
C Standard says that using an incorrect specifier for an argument in printf will result in undefined behavior.
7.21.6.1 The fprintf function
If a conversion specification is invalid, the behavior is undefined. 282) If any argument is
not the correct type for the corresponding conversion specification, the behavior is
undefined.
Since pointer to int is not the same type as pointer to void, and %p may only be used for a pointer to void and even if some other rules says that any pointer may be converted to pointer to void and back, that doesn't change the fact that the behavior is undefined, because of the quoted rule.
Firstly, different pointer types are not necessarily interchangable.
Secondly, for varargs no implicit conversion takes place as the compiler does not know the expected type.
I have the compiler complaining (warning) about the folowing.
Am I missing something? Because I thought this didn't need a cast
char* CurrentCh = some ptr value;
int size;
size = func(&CurrentCh);
with func defined like this
int func(void** ptr);
Compiler warning:
passing argument 1 of 'func'
from incompatible pointer type
Thx
In C you can pass any pointer type to a function that expects a void*. What it says is "I need a pointer to something, it doesn't matter what it points to". Whereas void** says "I need a pointer to a void*, not a pointer to another pointer type".
In C, void * is the generic pointer type. But void ** is not a generic pointer-to-pointer type! If you want to be able to pass a pointer to a pointer in a generic way, you should use void * anyway:
#include <stdio.h>
void func(void *ptr)
{
char **actual = ptr;
const char *data = *actual;
printf("%s\n", data);
}
int main(void)
{
char *test = "Hello, world";
func(&test);
return 0;
}
The cast is necessary as what you do is a form of type punning: You reinterpret the memory which is pointed to from char * to void *.
For these types, the C standard guarantees that this actually works as char * and void * have the same representation. For other type combinations, this may not be the case.
The relevant parts of the standard are section 6.2.5, §27
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.
and less relevant (but perhaps also interesting) section 6.3.2.3, §7
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. 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.
Anything beyond that is implementation-specific.
In C, any pointer can downcast to void*, but not to void**. You will need an explicit cast.