understanding pointer logistics in printf statement - c

I'm learning pointers in C but I'm slightly confused with this example. What is the pointer logistic for the pointers in the three printf() statements below? What are these: *(char*)ptr, *(int*)ptr, (char*)ptr+2, exactly doing?
#include<stdio.h>
int main()
{
void *ptr;
char ch=74, *cp="CS107";
int j=65;
ptr=&ch;
printf("%c\n", *(char*)ptr);
ptr=&j;
printf("%c\n", *(int*)ptr);
ptr=cp;
printf("%s\n", (char*)ptr+2);
return 0;
}

I believe you're already got your answer, but just to clarify a hidden point, let me add some more info to already existing answers.
printf("%c\n", *(char*)ptr);
Cast the void pointer ptr to a char pointer, then de-reference to print the char value.
printf("%c\n", *(int*)ptr);
Cast the void pointer ptr to an int pointer, then de-reference to print the char representation of that int value.
printf("%s\n", (char*)ptr+2);
Here, the operator precedence comes into play. As the cast operator will take precedence over binary addition, first the ptr will be casted to char *, and then, the pointer arithmetic will come into effect, incrementing the pointer to point to the 3rd char element (0 based indexing, remember?).

(char*)ptr is called casting. A pointer of one type(ptr) is cast to point to a variable of another type(char*).
In your example, ptr is a pointer of type void, and it is used at various places to point to different types of variables.
ptr=&ch; this makes it point to a variable of type char.
However, the pointer ptr itself is of type void only, so later in printf() statement, it has to be explicitly casted to type char* to make it work.
printf("%c\n", *(char*)ptr);
^^^^^^^
Then, it is dereferenced to access the element residing in that memory.
printf("%c\n", *(char*)ptr);
^
Same goes for other types which follows.

*(char*)ptr : Treat the value of ptr as a pointer that pointing to char data, then read the char data pointed by ptr
*(int*)ptr : Treat the value of ptr as a pointer that pointing to int data, then read the int data pointed by ptr
(char*)ptr+2 : Treat the value of ptr as a pointer that pointing to char data, then calculate a pointer pointing to a char data which is 2 elements ahead from the element pointed by ptr

Related

dereference pointer to an array

I have a basic doubt, when we de-reference a pointer to an array why we get name of the array instead the value of first member of an array, I see this has been asked here but I didn't get it
exactly how?
dereferencing pointer to integer array
main()
{
int var[10] = {1,2,3,4,5,6,7,8,9,10};
int (*ptr) [10] = &var;
printf("value = %u %u\n",*ptr,ptr); //both print 2359104. Shouldn't *ptr print 1?
}
So, is it like when we dereference a pointer, it cancel out the reference operator and what we get is variable name, and in case of pointer to an array, it is the array name ?
main()
{
int a = 10;
int *p = &a;
printf("%d\n", *p) /* p = &a; *p = *(&a); and having a dereference cancel out the &, gives us *p = a ? */
}
Because ptr has type int (*)[10] which is a pointer to an array, *ptr has type int[10] which is an array type.
This array decays to a pointer to its first element when it is passed to printf which then prints the pointer value. The result would be the same if you passed var to printf instead of *ptr.
You do not get a variable name when you dereference a pointer. If the pointer points to an object the you get that object. If it does not point to an object then you get undefined behavior. In particular, if the pointer points to a whole array, then you get that array. That's a fundamental aspect of how pointers work.
However, a fundamental aspect of how arrays work is that in most contexts, an expression of array type is automatically converted to a pointer to the first array element. This address corresponds to the address of the array itself, but has different type (int * in your case, as opposed to int (*)[10]). Typically, converting these to an integer type produces the same value. Thus, in your example code, *ptr is equivalent to var, and each is automatically converted to a pointer of type int *, equivalent to &var[0].
But note also that it is not safe to convert pointers to integers by associating them with printf directives, such as %u, that expect a corresponding integer argument. The behavior is undefined, and in practice, surprising or confusing results can sometimes be observed. One prints a pointer value with printf by using a %p directive, and converting the pointer value to type void *. Example:
printf("pointer = %p %p\n", (void *)*ptr, (void *)ptr);
Combined with the type and value of ptr from your example, this can be expected to reliably print two copies of the same hexadecimal number.

C pointer to pointer warning

I get a warning when i point to a pointer. I am not sure why; the types are the same.
char delay_buffer1[40];
char delay_buffer2[40];
char** delay_buffer_front;
delay_buffer_front = &delay_buffer1;
the objective is to swap buffers:
void swap_string_buffer(void** pointer, void* buffer1, void* buffer2) {
printf("pointer: %u. buffer1: %u. buffer2 %u\n", pointer, buffer1, buffer2);
if(*pointer == buffer1) *pointer = buffer2;
else *pointer = buffer1;
}
example code:
copy_content_to(delay_buffer_front);
swap_string_buffer((void**) delay_buffer_front, (void*) delay_buffer1,(void*)delay_buffer2);
fprintf(file, "%s", delay_buffer_front);
warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
delay_buffer_front = &delay_buffer1;
the types are the same.
No, they aren't. You're probably confused (this is quite common) by the fact, that the identifier of an array evaluates to a pointer to its first element in nearly all contexts (exceptions are e.g. with the operators & and sizeof).
But that doesn't change the fact that an array is an array and a pointer is a pointer.
This is a pointer to a pointer:
char **delay_buffer_front;
What you probably wanted was a pointer to an array:
char (*delay_buffer_front)[40];
Or you might simply have wanted a plain pointer pointing to the first element of the array:
char *delay_buffer_front;
With that declaration, you can just assign the array's identifier because as explained above, it evaluates to such a pointer:
char *delay_buffer_front = delay_buffer1;
It means exactly that, the two pointers are incompatible. It's more an error than a warning.
You probably want this:
char* delay_buffer_front;
delay_buffer_front = delay_buffer1;
instead of:
char** delay_buffer_front;
delay_buffer_front = &delay_buffer1;
As all you want is to swap the buffers you dont have to create a pointer to a pointer. Simply create an intermediate pointer of similar type and use it for swapping.
char* delay_buffer_front; //Intermediate pointer to store the address of first buffer
delay_buffer_front = delay_buffer1; //Store the address of the first buffer in the
//intermediate pointer.

Two ways of viewing a char ** pointer chain and can pointer types be mixed?

Suppose I have the variable declaration char **p. Does that mean that p is a pointer to a char pointer or that p is a pointer to a pointer of some type that points to a char?
There is a subtle difference between these two chains of pointers.
In other words, what I am trying to ask is given a char pointer to a pointer char **p, *p can obviously be a pointer to a char *, but could it also point to some other pointer type like void * which in turn points to a char?
The type of *p is always char *. It cannot be a void* that happens to be pointing to a char.
Pointer types are derived from some other type - an object type, a function type, or an incomplete type. The type from which the pointer is derived is called its reference type (C99, 6.2.5.20).
The reference type of char** is char*, meaning that dereferencing char** expression yields a char*.
A pointer contains an address. The C compiler uses the variable type such as char of a definition such as char *pC; to know how to access the data at the address contained in the pointer variable pC. So to C all addresses are pretty much the same, at least for all the main stream computer architectures, and the type just tells the C compiler how many bytes of memory to access when dereferencing the pointer or dereferencing the pointer pointed to by a variable.
So a definition such as char **p; tells the compiler that the variable p contains the address of a memory location, which is accessed by reading the number of bytes of a pointer, that points to another address, which is accessed by reading the number of bytes of a pointer, and that the address pointed to contains the address of a char.
And remember that with the C programming language you can use a cast to persuade the compiler to accept almost anything.
And a void * pointer variable is by definition capable of holding a pointer to any data type.
However it is your responsibility that what you are doing actually makes sense. So it is assumed the void * pointer contains the address of a character; that when the variable char **p; is dereferenced as in char aChar = **p; it is up to the programmer that the variable p contains a valid address and that the memory location whose address is pointed to, *p, contains a valid address. Or if you are doing something like char aStr[128]; strcpy (aStr, *p); then the pointer address pointed to by *p contains the address of a zero terminated string of characters.
And to some extent it depends on the C compiler. Some are more accepting than others. Some will issue warnings and some may issue errors and it probably also depends heavily on the compiler options selected for the compile.
Doing a test compile with Visual Studio 2017 Community Edition I can do the following:
char aStr[] = "this is a string";
void *p = aStr; // perfectly fine
char *pc = aStr; // perfectly fine
char **pp = &p; // warning C4133: 'initializing': incompatible types - from 'void **' to 'char **'
char **pp2 = (char **)&p; // perfectly fine since we are casting the pointer
char **pp3 = &aStr; // warning C4047: 'initializing': 'char **' differs in levels of indirection from 'char (*)[17]'
By the way, the last definition, char **pp3 = &aStr; really should be an error since if you dereference pp3 you do not get a valid pointer to a string.
However using the debugger to look at pp, it points to a valid pointer to a string and I can see the text of aStr.
In C programming language char **p will be described as, p is a pointer of pointer to a char. That means p can hold a address of another char pointer.
For example:-
char c = 'A';
char *b = &c;// here b is a char pointer so it can hold the address of a char variable which is c
char **p = &b;// p is a pointer of pointer to a char so here it can hold the address of a pointer of char i.e. b.
printf("%c", **p);// correct and will print A
Now here p is not a void pointer but you can make p also a void * pointer. So following is also correct
void **p = &b;
But to get the value of char c we have to type cast it like below
printf("%c", **p);// Not correct and will not print A
printf("%c", **((char **)p));//correct, first we type cast it to a pointer of pointer to char and dereference it to get the value stored in c
If you use the right-left rule on the declaration you’d see that:
you start with identifier p (P)
p has nothing on the right
you go left to the first * pointer (*P)
you go left again and you see * pointer (**P)
then you go left again and see char (char **P)
all together you can make the conclusion:
P is a pointer to a pointer to char.
Which is a fancy way of saying P is a double pointer to char.

pointer in array of pointer print value without *

#include<stdio.h>
#include<string.h>
int main()
{
char arr[3][10];
char *ptr[3];
strcpy(arr[1],"abcde");
ptr[1]=arr[1];
printf("%s\n",arr[1]);
printf("%p\n", &arr[1]);
printf("%p\n", ptr[1]);
printf("%p\n", &ptr[1]);
printf("%s\n",ptr[1]);
printf("%s\n", *(ptr+1));
return 0;
}
Result
abcde
0x7ffdcbbd8daa
0x7ffdcbbd8daa
0x7ffc30ed1188
abcde
abcde
I know ptr is an array of pointer. Dereferencing a pointer needs a unary operator before it. Why can we dereference the pointer ptr[1] without the *? Thanks
%s specifier in printf expects and argument of type char * (it should point to a null terminated string). ptr[1] is of char * type. So, no need to apply dereference operator here.
You got it wrong. Here:
printf("%s\n", ptr[1]);
The format %s expects a pointer to char.
ptr[1] is a char pointer.
why I use *(ptr+1) it prints the same value?
Because this will apply pointer arithmetics, which involve ptr, add 1 to it (by one here we mean the size of an int), and then use the * operator and refer to the content of this memory address (where it points) to, which is the same value as ptr[1].
arr[1] and &arr[1] might be pointing to the same location, but sematically they are different. arr[1] is equal to &arr[1][0] which is of type char*. &arr[1] is of type char (*)[10], as Some programmer dude said.

Why is a pointer to a pointer incompatible with a pointer to an array?

OK, I'm having trouble understanding pointers to pointers vs pointers to arrays.
Consider the following code:
char s[] = "Hello, World";
char (*p1)[] = &s;
char **p2 = &s;
printf("%c\n", **p1); /* Works */
printf("%c\n", **p2); /* Segmentation fault */
Why does the first printf work, while the second one doesn't?
From what I understand, 's' is a pointer to the first element of the array (that is, 'H').
So declaring p2 as char** means that it is a pointer to a pointer to a char. Making it point to 's' should be legal, since 's' is a pointer to a char. And thus dereferencing it (i.e. **p2) should give 'H'. But it doesn't!
Your misunderstand lies in what s is. It is not a pointer: it is an array.
Now in most contexts, s evaluates to a pointer to the first element of the array: equivalent to &s[0], a pointer to that 'H'. The important thing here though is that that pointer value you get when evaluating s is a temporary, ephemeral value - just like &s[0].
Because that pointer isn't a permanent object (it's not actually what's stored in s), you can't make a pointer-to-pointer point at it. To use a pointer-to-pointer, you must have a real pointer object to point to - for example, the following is OK:
char *p = s;
char **p2 = &p;
If you evaluate *p2, you're telling the compiler to load the thing that p2 points to and treat it as a pointer-to-char. That's fine when p2 does actually point at a pointer-to-char; but when you do char **p2 = &s;, the thing that p2 points to isn't a pointer at all - it's an array (in this case, it's a block of 13 chars).
From what I understand, 's' is a pointer to the first element of the array
No, s is an array. It can be reduced to a pointer to an array, but until such time, it is an array. A pointer to an array becomes a pointer to the first element of the array. (yeah, it's kinda confusing.)
char (*p1)[] = &s;
This is allowed, it's a pointer to an array, assigned the address of an array. It points to the first element of s.
char **p2 = &s;
That makes a pointer to a pointer and assigns it the address of the array. You assign it a pointer to the first element of s (a char), when it thinks it's a pointer to a pointer to one or more chars. Dereferencing this is undefined behavior. (segfault in your case)
The proof that they are different lies in sizeof(char[1000]) (returns size of 1000 chars, not the size of a pointer), and functions like this:
template<int length>
void function(char (&arr)[length]) {}
which will compile when given an array, but not a pointer.
Here's the sample that works, plus printouts of pointer addresses to make things simple to see:
#include <stdio.h>
char s[] = "Hello, World";
char (*p1)[] = &s;
char *p2 = (char*)&s;
int main(void)
{
printf("%x %x %x\n", s, p2, *p2);
printf("%x\n", &s); // Note that `s` and `&s` give the same value
printf("%x\n", &s[0]);
printf("%c\n", **p1);
printf("%c\n", *p2);
}

Resources