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.
Related
I have declared string and pointer ptr of int type.But to access char type through pointer we use char pointer.
But here it is working properly,only giving warning.
I have written code in Codeblocks.
Warning: assignment from incompatible pointer type [-Wincompatible-pointer- types]
char name[]="Welcome";
int *ptr;
ptr=name;
int i=0;
while(name[i]!='\0') {
printf("%c",*(ptr+i));
i++;
}
Your code has undefined behaviour, the fact that it "works" means nothing.
You are mixing types here and that's what the compiler is warning you. The
problem here is this:
ptr = name;
because you are assigning a char pointer to a int pointer, they are
incompatible because char and int have different sizes and this will be a
problem when accessing memory through the int pointer.
When you do this:
printf("%c", *(ptr+i));
you eventually accessing memory out of bounds. ptr + i is pointer arithmetic,
that means that the compiler takes the size into account. When the compiler
calculates the offset for the expression ptr + i, it calculates the offset by doing i * sizeof(int).
Depending on the length of the string, this offset will be larger than limit of the name array. So it will
dereference a pointer past the bounds of the memory, and this yields undefined behaviour.
So the code is just simply wrong, like I said, the fact that it "works" means
nothing. The code could have ended with a segfault or erasing your hard drive.
Try following code:
int main()
{
char name[]="Welcome";
char *ptr;
ptr=(char*)name;
int i=0;
while(name[i]!='\0') {
printf("%c \n",*(ptr+i));
i++;
}
return 0;
}
My question is about dereferencing a char pointer
Here is my code -
#define MAX 10
char s[80]="Hello";
int main(){
char *stackValue;
stackValue=&s;//here I assined the address of s to stackValue
if(!stackValue){
printf("No place for Value");
exit(1);
}
else{
printf("\n%s",*stackValue);//This doesn't work with * before it
printf("\n%s",stackValue);//This works properly
}
return 0;
}
In the above code I have assigned the address of S[] to stackValue and when I am printing *stackValue it doesn't work ,
But If I print only 'stackValue' That works.
When I do same thing with Integer
int main(){
int i=10, *a;
a=&i;
printf("%d",*a);//this gives the value
printf("%d",a)//this gives the address
return 0;
}
Is printing char pointer and integer pointer is different. When I use * in int value it gives the value but gives an error when I use it as a char pointer.
Help me out?
With the first code snippet:
stackValue=&s; is incorrect given s is already an array to char. If you write like that then stackValue becomes pointer to pointer to char (not pointer to char).
Fix that by changing to stackValue=s;
Also, again %s expect a pointer to char (NOT pointer to pointer to char) - that explains why this doesn't work
printf("\n%s",*stackValue); // this doesn't work
You need printf("\n%s",stackValue); instead.
With the second code snippet.
a=&i; is ok because i is a single int, NOT an array.
What you are trying to do is this:
int main(void)
{
char a_data = "Hello, this is example";
char *pa_stack[] = {a_data};
printf("We have: %s\n", *pa_stack);
}
The "%s" format specifier for printf always expects a char* argument.
so this is working and correct statement
printf("\n%s",stackValue);
and in first statement you are passing value so it will give you undefined behaviour.
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);
}
#include <stdio.h>
// this works
void print_stuff (void* buf) {
printf ("passed arg as buf*: %s\n", buf);
}
/* This works */
void print_stuff_3 (char* buf) {
printf ("passed arg as char*: %s\n", buf);
}
// this does not work
void print_stuff_2 (char** buf) {
printf ("%s\n", *buf);
}
int main () {
char s [] = "hi";
printf ("s = %s\n", s);
// these work
print_stuff (&s);
print_stuff_3 (&s);
// this results in a Segfault
print_stuff_2(&s);
return 0;
}
I am a bit confused about the way things are passed around in C. I feel like &s should be of type char**, but it behaves as if it is of type char* when passed to a function. Why does this behaviour happen?
In particular, print_stuff_2 segfaults, whereas I thought that print_stuff_3 would give an error.
EDIT: To clarify, I expected print_stuff(&s) and print_stuff_3(&s) to fail (while they succeed), while print_stuff_2(&s) fails, whereas I feel it should succeed.
You need to remember that strings are not fundamental types in C. They are arrays of characters. Therefore
char s [] = "hi";
makes s a char * (in terms of variable type), i.e. a pointer to the first character of a 3 character array (h, i and NUL).
So in order to pass a pointer to the string, you what to use your print_stuff_3, as printf()'s %s argument takes exactly that (a pointer to the string, i.e. a pointer to the first character). Call this with print_stuff_3(s).
print_stuff works because a pointer is a pointer. It will be translated to a void * pointer on calling print_stuff, then printf()'s %s will convert it back to a char *. Call this with print_stuff(s).
print_stuff_2 doesn't work because you are taking the address of where s is stored. Had you written char *s = "hi"; that would work if you used print_stuff_2(&s). You'd pass the address of the pointer, then dereference that (to get the value of the pointer, i.e. the pointer to the first character) in by using *buf. Except buf then would be a poor choice of name, as you would be passing a pointer to a pointer to characters.
The complication is as follows. As it is, you are doing &s which just returns s when you have
char s [] = "hi";
(see How come an array's address is equal to its value in C? ), but returns the address at which the pointer variable s is stored on the stack if you have:
char *s = "hi";
Taking the address of an array doesn't really make sense (so evaluates to the address of the first element). You need to use char *s = "hi"; if you want to take the address of the pointer.
In C, array names are decays to pointer to its first element when passed to a function in most cases. When passing s to the function print_stuff, s decays to pointer to h. No need to pass it with &. &s is of pointer to array (char (*)[3]) type, i.e, it is giving the address of the entire array s.
In function call
print_stuff_3 (&s);
your compiler should warn you
[Warning] passing argument 1 of 'print_stuff_3' from incompatible pointer type [enabled by default]
I feel like &s should be of type char**, but it behaves as if it is of type char* when passed to a function. Why does this behavior happen?
No. You thought wrong. &s is of type char (*)[3].
void print_stuff (void* buf) & void print_stuff_3 (char* buf) In both functions, buf is of char * taking address as argument. Which should be print_stuff (s) & print_stuff_3 (s) respectively as s is the base address of char array s. So you shouldn't pass &s which is address of s.
As the below function buf is of type char **, it will expect address of address like print_stuff_2(&s) provided your declaration is char *s = "hi",
void print_stuff_2 (char** buf) {
printf ("%s\n", *buf);
}
In this code to print the value of int and char pointer variables, why do I access them differently? For the char pointer I write sampleclient but for the int I write *intid. Why does one use * but not the other?
int main()
{
char client[] = "ABCD";
int intid = 10;
samplepass(&client, &intid);
return 0;
}
void samplepass(char *sampleclient, int *intid)
{
printf("%s %d\n", sampleclient, *intid);
}
This is because %s format specifier expects a char pointer, while %d expects an integer argument. If you want to see the value of the pointers themselves(i.e. the address they point to) use %p.
In C, you can't pass a string (character array) to a function like printf, so you do the next best thing: pass it its address in memory. The function can then treat the memory address like an array using pointer arithmetic. So the way printf reads strings is by taking in a pointer to the string. Then %s displays the dereferenced string (ABCD here), not the pointer address (which you could get by using %p instead of %s).
The integer problem is more straightforward: the * in *intid means 'get the value stored at this address'. That's why it prints out 10, not the memory address.
The 'correct' format specifier to get a memory address is %p. But you could write:
int main()
{
char client[] = "ABCD";
int intid = 10;
samplepass(&client, &intid);
return 0;
}
void samplepass(char *sampleclient, int *intid)
{
printf("%d %d\n", sampleclient, intid);
}
On my machine, the output was
-2140958000 -2140958004
(and those are the addresses in memory of client and intid). Substituting %p for %d gives nicely formatted pointers with a 0x in front and conforms to the standard, so I'd use that.
For an output that looks like:
ABCD 10
change your line
from samplepass(&client,&intid);
to samplepass(client,&intid);