I have been studying a code on Programming in C lately. I got stuck here while dealing with two dimensional strings and pointers. Also the functions printf(), putchar(), and puts() are confusing! Please help me out with the following snippet:
#include<stdio.h>
int main()
{
char wer[3][4]= {"bag", "let", "bud"};
char (*ptr)[4]=wer;
printf("%d %d %d\n",ptr, ptr+1, ptr+1); // points to bag, let and bud respectively
printf("%d %d %d\n",wer, wer+1, wer+1); // points to bag, let and bud respectively
printf("%d %d %d\n", (*ptr), (*ptr+1), (*ptr +2)); // points to b,a,g respectively
printf("%s\n",*(ptr+1)); //prints let
printf("%s\n", (ptr+1)); //prints let
printf("%s\n", (*ptr +1)); //prints ag
puts(*(ptr+1)); //prints let
//puts((ptr+1)); //gives error
puts((*ptr+1)); //prints ag
putchar(**(ptr+1));//prints l
putchar(*(*ptr +1));//prints a
return 0;
}
I want to know why do *(ptr+1) and (ptr+1) both work for printf while (ptr+1) gives an error for puts. Also I know that putchar takes an integer argument. Why do we use double pointer here?
Your ptr variable is of type char (*)[4] (a pointer to char array). The prototype of puts is:
int puts(const char *s);
So, as your compiler probably says, puts expects a pointer on char but you give an argument of type char (*)[4].
I want to know why do *(ptr+1) and (ptr+1) both work for printf while (ptr+1) gives an error for puts.
The argument types are not checked on the call to printf (probably because printf takes variadic arguments). Add -Wall in your CFLAGS to add more warnings and your compiler should emit a warning about this.
Also I know that putchar takes an integer argument. Why do we use double pointer here?
You dereference ptr twice to get a char (you can also do (*ptr)[1]). This char is implicitly cast into int when you call putchar.
Related
Why does the following code terminate to a segmentation fault
why the alternate version i.e. commented code, does not? The two versions of the code look the same to me. What am I missing?
#include <stdio.h>
1
2 void get_input(char**); // void get_input(char*);
3
4 int main(void)
5 {
6 char name[20];
7 get_input((char**)&name); //get_input(name);
8 printf("%s", name);
9
10 }
11
12 void get_input(char** m)//get_input(char* m)
13 {
14 scanf("%s", *m); // scanf("%s", m);
15 }
name is an array of characters. Its type is char[20].
In certain cases arrays decay into pointers. This is not one of those cases.
The C standard specifically mentions that an argument of the address-of operator does not decay. The result of applying the address-of operator to an array name is, unsurprisingly, is the address of the array.
In this case &name has the type char (*)[20]. This type is very different from char**. The former describes a pointer that points to a memory location that contains 20 characters. The latter describes a pointer that points to a memory location which contains a pointer that points to another memory location that contains a character. You cannot cast one to the other and hope it will work.
The answer by n.m. is correct. But if you want to know where the problem occurs "under-the-hood", take a look at the following code:
#include <stdio.h>
void get_input(char**);
int main(void)
{
char name[20];
char* pname = name;
char** ppname = (char**)&name; //this is what you were passing to get_input
printf("Address of name: %d\n", name);
printf("Value of pname: %d\n", pname);
printf("Value of &name: %d\n", &name);
printf("Value of ppname: %d\n", ppname);
get_input(ppname);
printf("Input: %s\n", name);
}
void get_input(char** ppinput)
{
char* pinput = *ppinput;
printf("Value of ppinput: %d\n", ppinput);
printf("Value of pinput: %d\n", pinput);
// The next line of code causes SEGMENTATION FAULT because
// pinput is the value of name[0], which is garbage,
// so you don't own the memory it points to.
scanf("%s", pinput);
}
If you compile and run that, you will see output similar to this:
Address of name: 2358816
Value of pname: 2358816
Value of &name: 2358816
Value of ppname: 2358816
Value of ppinput: 2358816
Value of pinput: 1
Segmentation fault
Take a look at the address of name and compare that with the value of pname (the pointer to name) and the value of ppname (which is defined as a char**).
The OP was perhaps expecting that &name would return a pointer to pname (i.e. that &name returns a pointer to a pointer to the first char in the array). However, you can see that pname and ppname are the same! This is because the compiler interprets &name as a pointer to the array, which incidentally is at the same address as the first character in the array (which is what pname points to).
The get_input function is actually perfectly fine. If ppinput (which the OP called "m") were truly a pointer to a pointer to a char, the function would work as expected. The char** would be dereferenced to a char* and scanf would fill it without a problem.
But as shown above, ppname is actually a pointer to an array, which is the same as a pointer to the first element of that array. So ppname is, in effect, the same thing as pname. So in the OP's code, he was really passing a value that is effectively a char* to get_input, instead of a char**.
Then, when get_input dereferences ppinput, it gets the VALUE of the first character in the char[] array (which in my output happened to be 1) instead of a pointer to that value, which is what scanf expects.
The OP could do exactly what he was intending to do in his question by simply changing the line (from my code example):
char** ppname = (char**)&name;
to
char** ppname = &pname;
Now this value for ppname truly IS a pointer to a pointer, which is what the OP was expecting &name to be. After you make that change, you really will be passing a char** to get_input and the function will work as expected.
I hope that sheds more light on the issue. The important dogmatic points were already mentioned by n.m. but a practical note to take from this is that a reference to an array returns the same value as a pointer to the first element. I.e.
(int)&name is (unintuitively) the same as (int)name when name is declared as an array. I say "unintuitively" because if you are not familiar with c++ arrays, you might expect that &var would always return a different value than var, but as this example shows, that turns out to not be true for arrays.
(Note that above, I've used int for pointer values and likewise %d for printf. This is bad practice in terms of portability, but for this illustration it should work and get the point across.)
char ** is a pointer to a pointer.when you pass the address of the array,it has type char (*)[20] which is incompatible with parameter of type char**.This is how you can correct the code :
#include <stdio.h>
void get_input(char* m); // void get_input(char*);
int main(void)
{
char name[20];
get_input(name); //get_input(name);
printf("%s", name);
}
void get_input(char* m)//get_input(char* m)
{
scanf("%s", m); // scanf("%s", m);
}
I'm trying to learn C, more specifically pointers and malloc(). I wrote the following program and need a few clarifications about warning messages I got and the output.
#include<stdio.h>
#include<stdlib.h>
void printem(char *ptr, char loopagain)
{
int i;
for (i = 0; i < loopagain; i++)
{
printf("Found %c at address %c \n",ptr[i],&ptr[i]);
}
return;
}
void initializeblock(char *ptr, char loopagain)
{
int i;
for (i = 0; i < loopagain; i++)
{
ptr[i] = 65+i;
}
return;
}
int main()
{
char neededbytes = 10;
char *p;
p = (char *) malloc(neededbytes * sizeof(char));
if(p==NULL)
{
printf("Error! memory not allocated.");
exit(0);
}
else
{
printem(p,neededbytes);
initializeblock(p,neededbytes);
printem(p,neededbytes);
}
free(p);
}
The first problem I have is I don't get the address of each of the 10 char elements to print out correctly as numbers. Instead of a numbers I get symbols. This is the line of code that prints out the values and addresses.
printf("Found %c at address %c \n",ptr[i],&ptr[i]);
What am I doing wrong?
The second problem is inside the else statement I have the following code
printem(p,neededbytes);
initializeblock(p,neededbytes);
printem(p,neededbytes);
originally I used an & before p but got the following warnings
[Warning] passing argument 1 of 'printem' from incompatible pointer type
[Note] expected 'char *' but argument is of type 'char **'
The examples I found online show that a function expecting a pointer should have the address passed to it, why am I getting the warings when I use
printem(&p,neededbytes);
initializeblock(&p,neededbytes);
printem(&p,neededbytes);
Also, when I allocate a block using malloc(), is there another way to access/modify the data other than using array brackets, p[i]?
Is there a way to know the size of the block allocated so I don't have to pass it as an argument in the following line
printem(p,neededbytes);
First of all
printf("Found %c at address %c \n",ptr[i],&ptr[i]);
should be at least
printf("Found %c at address %p \n",ptr[i],(void *)&ptr[i]);
because, &ptr[i] is an address and it needs %p format specifier. Then, printf() being a variadic function, only default argument promotion takes place, so you need to cast the argument to void *, the expected type of %p. Using inappropriate type argument for conversion specifier leads to undefined behavior.
That said, even this won't save you. At the point you're calling printem(), the memory location pointed by p is uninitialized [#]and the content of it is undeterministic. Using uninitialized value will again lead you to UB. So, you need to initialize the memory before trying to print the value.
Then, check the signature of initializeblock()
void initializeblock(char *ptr, char loopagain)
and the call on which you got the warning
initializeblock(&p,neededbytes);
in this call, you're trying to pass &p which is of type char ** and trying to receive the same in a char *, which triggers the warning. You should be fine with a call like
initializeblock(p,neededbytes);
in this case.
Lastly
Is there a way to know the size of the block allocated so I don't have to pass it as an argument in the following line...
I'm afraid, no. There is no standard way of knowing the allocated size just from the pointer, so you have to pass the size as a second parameter.
[#] malloc() returns uninitialized memory.
main()
{
const char **a = {"string1","string2"};
printf("%c", *a); /* prints s */
printf("%s", a); /* prints string1 */
printf("%s", a+1);/* prints ng1 */
}
GCC v4.8.3 prints "%s" for the last printf, where as http://codepad.org/ prints "ng1".
I thought that the code will create an array of pointers to two strings and the base address assigned to a, which allows normal pointer arithmetic. but it seems that there is something wrong with the assumption.The first printf suggests that my assumption is wrong. can anyone explain why this behavior is observed? ( note that VS 2012 has thrown an error saying too many initalizers where as GCC has thrown a warning for incompatible pointer assignment). I am aware of the warning due to incompatible pointer assignment.
const char **a is not an array of pointer to two strings. It declares a to be a pointer to pointer to const char.
const char **a = {"string1","string2"}; //No memory is allocated to store string literals
will invoke undefined behavior and you may get either expected or unexpected results.
To declare a as an array of two pointers you need to change the declaration as
const char *a[] = {"string1","string2"};
The memory range in your program's stack looks like this: (notice that it is not allocated before assignment which is wrong)
char** a = {s, t, r, i, n ,g, 1, \0, s, t, r, i, n, g, 2, \0}
Therefore when you print the command:
printf("%c", *a);
You are dereferencing the first character of the string which is 's'.
On the other hand, when you print the command:
printf("%s", a);
you are printing a string that starts at pointer a and finishes at '\0'. That's why you see the output 'string1'.
Lastly, when you type "a+1" you increase the pointer in one step (example here: How to increment a pointer address and pointer's value?). in this case because char** is a pointer, and every pointer is 4 byte, the "+1" jumps 4 chars forward.
Therefore when you print the command:
printf("%s", a+1);
The printf starts at the pointer 'a' + 4 bytes and ends at '\0'. That's why the output is 'ng1'.
Hope it was clear enough.
This is due to the following peculiar initialization performed by GCC.
please see int q = {1,2}; peculiar initialization list. the statement const char **a = {"string1","string2"}; results in a being treated as if const char **a = "string1". this solves the mystery as *a would print 's', a would print string1.
I have to correct an existing C file which has a bunch of format specifiers compilation errors.
Can anyone tell the correct format specifiers for the following cases:
u_char(*) [10] (I tried %s, but didn't work)
char(*) [10] (I tried %s and %c, but didn't work)
Thanks.
Both are pointers to arrays, so you can dereference them to become arrays, which decay to pointers-to-first-element:
char arr[10];
char (*pa)[10] = &arr;
printf("%s", *pa); // or &((*pa)[0])
To spell it out: the type of pa is char(*)[10], and the type of *pa is char[10], and the latter decays to a char* of value &((*pa)[0]) (equal to &(arr[0])).
I agree with Kerrek, but I think
printf ("%s", *pa);
may not work as is since we are not sure if there is a NULL character at the end. So to print we can do the following
char temp[10+1];
memcpy(temp, *pa, 10);
temp[10] = '\0';
printf("%s",temp);
void main()
{
char str[2][7] = {"1234567", "abcdefg"};
char** p = str;
printf("%d\n", *(p+1));
printf("%c\n", *(p+1));
}
The output is:
1631008309
5
Edit: Thank you. I see the '5' is only 0x35, other than str[0][4] I supposed to be. Why can't I get out str[0][4] instead of this strange 1631008309??
OMG, I'm foolish enough to ask this question! Thank you all, guys.
You're pointing a char** at the beginning of the memory allocated to your 2-d array.
When you add 1 to a pointer it moves you along by the sizeof the type pointed to, in this case the sizeof a char *, which is evidently 4 bytes in your system. Then you're dereferencing it and treating the result as an int (%d), which gives you the a765 I mentioned in my comment to unwind. When you dereference it and treat it as a char you correctly get 5.
[I see that unwind has deleted their answer, so just for completeness I'll add that the "a765" is the ASCII interpretation of the larger number you get (0x61373635).]
This line:
char** p = str;
makes no sense. str is an array of 2 arrays of 7 chars. When used in an expression context, it evaluates to a pointer to its first element, equivalent to:
&str[0]
which has type "pointer to array of 7 chars". That is in no way similar to a "pointer to pointer to char", which is the type of p. Any of these would make sense instead:
char *p = str[0];
char *p = str[1];
char (*p)[7] = str;
Using %d does not convert an ASCII string of alphanumerics into a number. Using %d means it expects that the value found will be interpreted as a binary representation of the number and converted TO ASCII for printing.
Note that the C standard defines that the main() program returns an int, not void.
Given the code:
#include <stdio.h>
int main()
{
char str[2][7] = {"1234567", "abcdefg"};
char** p = str;
printf("%d\n", *(p+1));
printf("%c\n", *(p+1));
return(0);
}
When compiled withgcc -o x -Wall x.c, you get the warnings:
x.c: In function ‘main’:
x.c:5: warning: initialization from incompatible pointer type
x.c:6: warning: format ‘%d’ expects type ‘int’, but argument 2 has type ‘char *’
x.c:7: warning: format ‘%c’ expects type ‘int’, but argument 2 has type ‘char *’
(If you omit the -Wall, you don't get the format warnings.)
This tells you that you are passing a pointer to printf(). Printing a pointer is not going to be all that useful. However, the previous warning also tells you that you are mis-initializing the variable p. However, the net result is that p points to the start of the space in str. When you print
One interesting little quirk: normally, a string such as "1234567" includes a terminating NUL '\0'; in your arays, because you specified that the length of the array is 7, not 8, the terminating null is missing. Be careful how you print the strings!
Here's another variant of the code:
#include <stdio.h>
int main()
{
char str[2][7] = {"1234567", "abcdefg"};
char** p = str;
printf("%d\n", *(p+1));
printf("%c\n", *(p+1));
printf("%p %p %p -- %p %p %p\n", str, &str[0], &str[0][0], p, p+1, *(p+1));
printf("%.4s\n", (p+1));
return(0);
}
That gives me the following output (from an Mac):
1631008309
5
0xbffffa5e 0xbffffa5e 0xbffffa5e -- 0xbffffa5e 0xbffffa62 0x61373635
567a
Note that the addresses str, &str[0] and &str[0][0] all have the same value, and that p+1 is four bytes further along. When treated as a string, it prints the last three bytes of the first initializer and the first byte of the second.
Also, for fun, compiled with gcc -m64 -o x64 x.c, the output is:
1701077858
b
0x7fff5fbff9e0 0x7fff5fbff9e0 0x7fff5fbff9e0 -- 0x7fff5fbff9e0 0x7fff5fbff9e8 0x676665646362
bcde
I understand your confusion, the two statements is exactly the same.
Both statements cast *(p + 1) to an pointer to an integer prior dereferencing it, the default is to cast everything to an integer in a va_arg argument.
printf then truncates the integer to a char prior printing it in the second printf statement.
This code snippet makes it more clear what is going on:
void main()
{
char str[2][7] = {"1234567", "abcdefg"};
char** p = str;
printf("%d\n", *(p+1));
printf("%c\n", *(p+1));
int n = *(p+1);
char c = *(p+1);
printf("0x%08X\n", n);
printf("0x%08X\n", c);
}
Which outputs:
1631008309
5
0x61373635
0x00000035
Type safety in stdarg is explained here: stdarg