Am doing a task where am supposed to used getline function read input from a user from the terminal. Here is my code:
int main(int ac, char **av)
{
printf("Write something: \n");
char **buf = NULL;
size_t buf_size = 0;
ssize_t bytes_read = getline(buf, &buf_size, stdin);
if (bytes_read != -1)
write(STDOUT_FILENO, buf, buf_size);
else
printf("An error occured\n");
free(buf);
return (0);
}
From the code above. My program displayed the text: An error occurred. Did some code refactoring and this is what I came up with:
int main(int ac, char **av)
{
printf("Write something: \n");
char *buf = NULL;
size_t buf_size = 0;
ssize_t bytes_read = getline(&buf, &buf_size, stdin);
if (bytes_read != -1)
write(STDOUT_FILENO, buf, buf_size);
else
printf("An error occured\n");
free(buf);
return (0);
}
Voila! The code displayed whatever was inputted, just what I wanted. So I fixed my the issue. But what am trying to understand is what's wrong the first code snippet? An it is right to do this?:
char **name = "John Doe";
or
char **name = NULL;
I did some quick test on an online compiler. Here is the code:
int main() {
// Write C code here
char **name = "John Doe";
printf("%p\n", name); //0x55f5f9890004
printf("%p\n", *name); //0x656f44206e686f4a
printf("%c\n", *name); //J
printf("%p", "John Doe"); //0x55f5f9890004
return 0;
}
I realised that the double pointer was just treated as a single char pointer. Not sure if my finding are right. If you can give a better explanation the above main function that would be cool.
CodePudding user response:
When you want to change a variable in a function you need to pass it by reference. Otherwise the function will deal with a copy of the value of the original object used as an argument expression and changing the copy of the value will not influence on the value of the original object.
In C passing by reference means passing an object indirectly through a pointer to it.
Thus dereferencing the pointer the function will have a direct access to the original object and can change it.
In this code snippet
char *buf = NULL;
size_t buf_size = 0;
ssize_t bytes_read = getline(&buf, &buf_size, stdin);
you want that the pointer buf
after calling the function getline
would point to a string read in the function. So you need to pass it to the function by reference the same way as you are passing another variable buf_size
the value of which is also changed within the function and the caller of the function needs to know the result value of the variable.
If you will write
char **buf = NULL;
size_t buf_size = 0;
ssize_t bytes_read = getline(buf, &buf_size, stdin);
then the function within itself will try to dereference the null pointer buf
that results in undefined behavior because it thinks that the so-called "double pointer" points to an object of the type char *
that the function needs to change.
To make it more clear consider the following demonstration programs.
#include <stdio.h>
void f( char *s )
{
s = "World!";
}
int main( void )
{
char *s = "Hello";
printf( "Before calling f s = %s\n", s );
f( s );
printf( "After calling f s = %s\n", s );
}
The program output is
Before calling f s = Hello
After calling f s = Hello
The pointer s
passed to the function be value. That is the function deals with a copy of the value of the pointer. Changing the copy does not influence on the original value of the pointer s
.
Now consider the next program
#include <stdio.h>
void f( char **s )
{
*s = "World!";
}
int main( void )
{
char *s = "Hello";
printf( "Before calling f s = %s\n", s );
f( &s );
printf( "After calling f s = %s\n", s );
}
The program output is
Before calling f s = Hello
After calling f s = World!
That is as the pointer s is passed to the function by reference the function can change the original pointer s
by dereferencing the pointer passed to the function that points to the original pointer s
.
As for this declaration
char **name = "John Doe";
then the compiler should issue a message that declaration is wrong. The string literal used as an initializer is implicitly converted tp pointer to its first element of the type char *
. But the initialized variable has the type char **
and there is no implicit conversion between these pointer types.
Also the conversion specifier %s
expects an argument of the type char *
instead of the type char **
.
So you have to write
char *name = "John Doe";
Or as you may not change a string literal then it will be better to write the declaration like
const char *name = "John Doe";
CodePudding user response:
In the char *buf
case, getline
will go to the memory location of the pointer buf
and change that address.
In case of char **buf = NULL;
, getline
will go the memory location "null" and try to change that address, which is nonsense. You essentially lie to getline
and tell it that you stored a pointer for it at memory location "null".
As for char **name = "John Doe";
it is a constraint violation of C's assignment rules, anything can happen since the code isn't valid C. Including "seems to work fine all the way until the mysterious crash some years later in live production code". You might want to take the skunk out of that online compiler by for example applying What compiler options are recommended for beginners learning C?
CodePudding user response:
A double pointer has to reference a valid pointer to work (as that pointer will store the reference to the newly allocated memory.
int main(int ac, char **av)
{
printf("Write something: \n");
char *realBuf = NULL
char **buf = &realBuf;
size_t buf_size = 0;
ssize_t bytes_read = getline(buf, &buf_size, stdin);
if (bytes_read != -1)
write(STDOUT_FILENO, *buf, buf_size);
else
printf("An error occured\n");
free(*buf);
return (0);
}
CodePudding user response:
There are no “double pointers” in C, so you cannot use them.
But for any type T, you can create a type “pointer to T”. And that’s the case if T is a pointer type as well. That’s a pointer to a pointer. Not a “double pointer”. If you see char** as a “double pointer” you will forever be confused.