pointer in array of pointer print value without * - c

#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.

Related

How does printf("%s\n", x) work, if x is of type char * instead of char[N]?

Some example code:
#include <stdio.h>
void func0(char *x)
{
printf("func0, %s, %zu\n", x, sizeof(x));
}
//for comparison with func0
void func1(char **x)
{
printf("func1, %s, %zu\n", *x, sizeof(*x));
}
int main()
{
char x[10] = "hello";
printf("%s, %zu, %zu, %zu\n", x, sizeof(x), sizeof(*x), sizeof("hello"));
func0(x);
func1(&x);
return 0;
}
For func1() (for comparison with func0), there is a "warning: incompatible pointer types passing 'char (*)[10]' to parameter of type 'char **'" and "Segmentation fault" error.
But for func0(), isn't the type 'char [10]' converted to 'char *'? If so, how is printf("%s") still able to print out "hello"? As shown in the output of func0():
hello, 10, 1, 6
func0, hello, 8
Is the information of length (i.e. N of char[N]) passed to func0?
What is the difference between x in func0 and *x in func1? Aren't they both an address pointing to a char?
Thank you very much.
[EDIT] Thank you so much for the detailed answers!
Let me summarise the warning and error for func1().
char x[10] = "hello";
The type of &x is char (*)[10], which is a pointer to an array, not pointer to pointer (e.g. char **). Hence the warning.
x is already the address (pointing to the string), and you can only take the address of an actual memory space. &x is not the address of x. **x in func1(): trying to read from an illegal address (the value of "H" in this example?), hence the Segmentation fault.
The extra question is:
What is the use case for using &x (e.g. char (*) [N])?
I see the difference between x and &x, char * / char[N] vs. char (*) [N], but I cannot figure out a case when you have to use char( *) [N]. x and &x are the same address after all.
there is a "warning: incompatible pointer types passing char (*)[10] to parameter of type char **" and "Segmentation fault" error.
That's right, &x expression produces a pointer to array of ten characters, hence the warning. If you want to pass a pointer to pointer, make a pointer, and take a pointer of it:
char *px = x; // alternatively you could write &x[0]
func1(&px);
But for func0(), isn't the type char [10] converted to char *?
That's right, character array "decays" to character pointer when you make a function call.
how is printf("%s") still able to print out "hello"
This is possible because x contains a null-terminated sequence of characters. Therefore, printf does not need to know the size of the array when processing %s: stopping at '\0' is sufficient.
Essentially, %s does not need to know the size of the array, it needs to know the length of the string, in the same way strlen "knows" it:
printf("func0, %s, %lu\n", x, strlen(x));
// Prints hello 5
I see the difference between x (char / char[N]) and &x, but I cannot figure out a case when you have to use char(*) [N]
Here is a small example:
void print_many(char (*rows)[10], size_t count) {
for (size_t i = 0 ; i != count ; i++) {
printf("%zu: %s\n", i+1, rows[i]);
}
}
you call it like this:
char rows[10][] = {"quick", "brown", "fox"};
print_many(rows, 3);
Demo.
Is the information of length (i.e. N of char[N]) passed to func0?
No. The parameter for func0 is char *. This is a pointer to char, nothing else. There is no length associated with it.
But for func0(), isn't the type 'char [10]' converted to 'char *'?
Yes. When x, which has type char [10], is used as a function argument, it is automatically converted to a pointer to its first argument. So the function receives a pointer to char.
If so, how is printf("%s") still able to print out "hello"?
The pointer passed to printf must point to the first character of a string whose end is marked by a null character. Thus, the pointer it receives points to a char containing 'h', after which there is a char containing 'e', then 'l', then 'l', then 'o', then zero. printf prints characters one by one until it sees the zero.
What is the difference between x in func0 and *x in func1?
If func1 were passed a pointer to a pointer to char, then using *x to pass a pointer to char to printf could be okay. However, your call to func1 is func1(&x). This is not passing a pointer to a pointer to a char. Since x is an array of 10 char, &x is a pointer to an array of 10 char. It is not a pointer to a pointer.
An array is not a pointer. Although arrays are often automatically converted to pointers, this automatic conversion does not happen when the array is the operand of unary &. (It also does not happen when the array is the operand of sizeof or _Alignof, or when the array is a string literal used to initialize an array.)
To produce a pointer to a pointer to char from the array x, you would have to take the address of a pointer to its first character. There is no way to do this in a simple expression, because x is the array, not a pointer. You could create a pointer to its first character with char *y = x;, and then you could pass &y to func1.
Note
Do not print sizes from sizeof using %lu. Use %zu. The z modifier is specifically for the size type, size_t.
For the question in the title, print("%s\n", x) does not care if x is a pointer or an array, as long as it contains a zero-terminated string.
Next, only the topmost dimension of an array can be transparently coerced to a pointer, that's what the warning about.
Finally, &x points to the zero elements of your array, so when you dereference it in func1, array's first few elements (might be even eight of them, so two the rightmost are not even initialized) are reinterpreted as a pointer-to char which almost certainly points to nowhere. That's how the segfault arised.

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.

understanding pointer logistics in printf statement

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

Reference to Array vs reference to array pointer

void check(void* elemAddr){
char* word = *((char**)elemAddr);
printf("word is %s\n",word);
}
int main(){
char array[10] = {'j','o','h','n'};
char * bla = array;
check(&bla);
check(&array);
}
Output:
word is john
RUN FINISHED; Segmentation fault; core dumped;
First one works, but second not. I don't understand why this happens.
The problem is, when we do &array, we are getting a char (*)[10] from an char [10], instead of a char **.
Before we do our experiment, I will emphasize that, when we pass an array as an argument to a function, C actually casts the array to a pointer. The big bucket of data is not copied.
Thus, int main(int argc, char **argv) is identical to int main(int argc, char *argv[]) in C.
This made it available for us to print the address of an array with a simple printf.
Let's do the experiment:
char array[] = "john";
printf("array: %p\n", array);
printf("&array: %p\n", &array);
// Output:
array: 0x7fff924eaae0
&array: 0x7fff924eaae0
After knowing this, let's dig into your code:
char array[10] = "john";
char *bla = array;
check(&bla);
check(&array);
bla is char *, and &bla is char **.
However, array is char [10], and &array is char (*)[10] instead of char **.
So when you pass &array as an argument, char (*)[10] acts like a char * when passing as an argument, as is said above.
Therefore **(char **) &bla == 'j' while *(char *) &array == 'j'. Do some simple experiments and you will prove it.
And you are casting void *elemAddr to a char ** and try to deference it. This will only work with &bla since it is char **. &array will cause a segfault because "john" is interpreted as an address as you do the cast.
For check(&bla); you are sending pointer to pointer
void check(void* elemAddr){
char* word = *((char**)elemAddr); // works fine for pointer to pointer
printf("word is %s\n",word);
}
This is working fine.
But, for check(&array); you are passing pointer only
void check(void* elemAddr){
char* word = *((char**)elemAddr); // This is not working for pointer
char* word = *(char (*)[10])(elemAddr); // Try this for [check(&array);]
printf("word is %s\n",word);
}
Full Code--
Code for check(array);:
void check(void* elemAddr){
char* word = *(char (*)[10])(elemAddr);
printf("word is %s\n",word);
}
int main() {
char array[10] = {'j','o','h','n'};
check((char*)array);
return 0;
}
Code for check(&bla);:
void check(void* elemAddr){
char* word = *((char**)elemAddr);
printf("word is %s\n",word);
}
int main() {
char array[10] = {'j','o','h','n'};
char* bla = array;
check(&bla);
return 0;
}
The C specification says that array and &array are the same pointer address.
Using the name of an array when passing an array to a function will automatically convert the argument to a pointer per the C specification (emphasis mine).
6.3.2.1-4
Except when it is the operand of the sizeof operator or the unary &
operator, or is a string literal used to initialize an array, an
expression that has type ‘‘array of type’’ is converted to an
expression with type ‘‘pointer to type’’ that points to the initial
element of the array object and is not an lvalue. If the array object
has register storage class, the behavior is undefined.
So calling func(array) will cause a pointer to char[] to be passed to the function. But there is a special case for using the address-of operator on an array. Since array has type "array of type" it falls into the 'Otherwise' category of the specification (emphasis mine).
6.5.3.2-3
The unary & operator yields the address of its operand. If the operand
has type ‘‘type’’, the result has type ‘‘pointer to type’’. If the
operand is the result of a unary * operator, neither that operator nor
the & operator is evaluated and the result is as if both were omitted,
except that the constraints on the operators still apply and the
result is not an lvalue. Similarly, if the operand is the result of a
[] operator, neither the & operator nor the unary * that is implied by
the [] is evaluated and the result is as if the & operator were
removed and the [] operator were changed to a + operator. Otherwise,
the result is a pointer to the object or function designated by its
operand
So calling func(&array) will still cause a single pointer to be passed to the function just like calling func(array) does since both array and &array are the same pointer value.
Common-sense would lead you to believe that &array is a double pointer to the first element of the array because using the & operator typically behaves that way. But arrays are different. So when you de-reference the passed array pointer as a double pointer to the array you get a Segmentation fault.
This is not a direct answer to your question, but it might be helpful to you in the future.
Arrays are not pointers:
type arr[10]:
An amount of sizeof(type)*10 bytes is used
The values of arr and &arr are necessarily identical
arr points to a valid memory address, but cannot be set to point to another memory address
type* ptr = arr:
An additional amount of sizeof(type*) bytes is used
The values of ptr and &ptr are typically different, unless you set ptr = (type*)&ptr
ptr can be set to point to both valid and invalid memory addresses, as many times as you will
As with regards to your question: &bla != bla == array == &array, and therefore &bla != &array.
One problem is that your char array is NOT NECESSARILY going to be null-terminated. Since array is an automatic variable that is allocated locally on the stack, it is not guaranteed to be zeroed-out memory. So, even though you are initializing the first 4 chars, the latter 6 are left undefined.
However ...
The simple answer to your question is that &bla != &array so your check() function is assuming it will find null-terminated character arrays at 2 different addresses.
The following equations are true:
array == &array // while not the same types exactly, these are equivalent pointers
array == bla
&array == bla
*bla == array[0]
&bla is never going to equal anything you want because that syntax references the address of the bla variable on the local stack and has nothing to do with its value (or what it points to).
Hope that helps.

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