When to use Single Pointer over Double Pointer in C [duplicate] - c

This question already has answers here:
How do I modify a pointer that has been passed into a function in C?
(7 answers)
Closed 1 year ago.
I have a question about pointer.
Recently, I was looking some system programming tutorial. The teacher is talking about asprintf() Function.
According to the man page, the definition of asprintf() is:
int asprintf(char **ret, const char *format, ...);
However, I found that the teacher tends to create a single pointer and then pass the address of that single pointer to the first parameter. Like this:
int main()
{
char *buffer;
int r;
r = asprintf(&buffer,"The total is %d\n",5+8);
puts(buffer);
printf("%d characters generated\n",r);
return(0);
}
I am wondering why don't we declare double pointer and then pass that double pointer to the function, just like the definition.
In addition, the teacher use the same tricks in other function too, such as: getline()
Therefore, is there any advantage that we would choose single pointer over double pointer, even the definition of the function is double pointer?
Thanks for everyone for replying in advanced. :))

asprintf will allocate some memory and write the printf result into that memory.
To get this resulting memory buffer, you pass a pointer to a local variable (in your case: buffer) as the first argument. This pointer is then dereferenced in the function, to change the value of your local variable, so the code in asprintf semantically does something like this:
int asprintf(char **ret, const char *format, ...) {
*ref = malloc(some_size);
sprintf(*ret, format, ...);
}
What would now happen if you didn't have a local variable of type char * and passed a pointer to this variable, but instead had a local variable of type char **, like you suggested:
char **buffer;
asprintf(buffer, "The total is %d\n", 5+8);
Well, asprintf would again just dereference this pointer you just passed. But in this code, it doesn't yet point to anything meaningful, so this would result in an error. We must have enough memory allocated at the address we are passing to asprintf so it can store a char* there for us.
Now, this would work:
char *buffer;
char **pointer_to_buffer = &buffer;
asprintf(pointer_to_buffer, "The total is %d\n", 5+8);
but it is just more complicated and verbose.
But why do we do this whole pointer-to-pointer thing? Well, you would certainly first try to simply pass the pointer, instead of a pointer-to-pointer, like this:
char *buffer;
asprintf(buffer, "The total is %d\n", 5+8);
with asprintf semantically doing something like this:
int asprintf(char *ret, const char *format, ...) {
ref = malloc(some_size);
sprintf(ret, format, ...);
}
However, it is critical that here, buffer is passed by value. This means that when asprintf locally modifies ret, it modifies a copy of the value of our buffer. We, the caller, will not be able to see this change in our variable buffer. This is why we pass a pointer-to our local variable buffer, not the value itself.

I am wondering why don't we declare double pointer and then pass that double pointer to the function, just like the definition.
Because you want to update the value stored in buffer. The type of the expression &buffer is char **.
Remember, in order for a function to write to any of its parameters, you must pass a pointer to that parameter:
void foo( T *ptr )
{
*ptr = new_T_value(); // writes a new value of type T to the thing ptr points to
}
void bar( void )
{
T var;
foo( &var ); // writes a new value to var
}
Thus, the following are true:
ptr == &var // T * == T *
*ptr == var // T == T
IOW, writing to the expression *ptr is the same as writing to var.
So, why not just create a pointer variable in bar and pass it to foo? If we wrote something like
void bar( void )
{
T *vptr;
foo( vptr );
}
the problem is that vptr doesn't point to anything meaningful - we have
ptr == vptr // T * == T *
*ptr == *vptr == ??? // T == T == ???
There's no object of type T for us to update. Now, we could create another object for vptr to point to:
void bar( void )
{
T var;
T *vptr = &var;
foo( vptr );
}
and that will work as expected, but the extra pointer variable is redundant in this case.
The same logic holds for pointer objects - let's replace T with a pointer type, P *. Then our code becomes:
void foo( P * *ptr ) // or P **ptr
{
*ptr = new_P_star_value(); // writes a new value of type P * to the thing ptr points to
}
void bar( void )
{
P * var; // or P *var
foo( &var ); // writes a new value to var
}
which gives us
ptr == &var
*ptr == var == some_P_star_value
**ptr == *var == *some_P_star_value == some_P_value
Same thing as above - we want to write a new value to var. To do that we pass a pointer to it, even though var is already a pointer type. This is why multiple indirection exists in the first place.
Most of the time when you see a function that has a pointer or pointer-to-pointer parameter, it's expecting you to pass the address of another object (obtained with the & operator), not a pointer variable as such.

If you want to change an object used as an argument in a function you need to pass it by reference.
In C passing by reference means passing an object indirectly through a pointer to it. Thus dereferencing the pointer a function can get a direct access to the original pointer.
Consider this demonstrative program and compare the result of calls of the two functions f and g.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void f( char *s )
{
s = malloc( 14 );
strcpy( s, "Hello World!" );
}
void g( char **ps )
{
*ps = malloc( 14 );
strcpy( *ps, "Hello World!" );
}
int main(void)
{
char *s = NULL;
f( s );
if ( s != NULL ) puts( s );
else puts( "s is a null pointer." );
g( &s );
if ( s != NULL ) puts( s );
else puts( "s is a null pointer." );
free( s );
return 0;
}
The program output is
s is a null pointer.
Hello World!
The function f accepts the pointer s declared in main by value. It means that the function deals with a copy of the value of the pointer s. Changing the copy does not affect the original pointer s. So the function produces a memory leak because it allocates dynamically a memory and the address of the allocated memory stored in the local variable (function parameter) s will be lost after exiting the function.
The function g accepts the pointer s by reference through a pointer to it. So dereferencing its parameter the function has a direct access to the original pointer s and can change it.

Related

How to understand secondary pointer?

i want to ask a question about pointer:
void fun(const char** a) {...}
1.
const char* b = nullptr;
fun(&b);
2.
const char** b = nullptr;
fun(b);
why use 1 but not 2?
1 is good, 2 donnot work
C uses pass by value for function call arguments. That means a function receives a copy of the passed value, and there's no way for the function to directly change a variable in the caller.
For example, if you write
void set_to_5(int x)
{
x = 5;
}
and then write
int i = 3;
set_to_5(i);
printf("%d\n", i);
it prints 3, not 5. Or, if you write
void set_string(char *str)
{
str = "world";
}
and then write
char *p = "hello";
set_string(p);
printf("%s\n", p);
it prints hello, not world.
In both cases, the function successfully changes its copy of the passed value — x or str — but that doesn't have any effect on the variable in the caller (i or p).
When you want to have a function that does change a variable in its caller, one way to do that is to have the function accept a pointer. That looks like this:
void set_to_5_via_pointer(int *x)
{
*x = 5;
}
void set_string_via_pointer(char **str)
{
*str = "world";
}
Then you can write:
int i = 3;
set_to_5_via_pointer(&i);
printf("%d\n", i);
char *p = "hello";
set_string_via_pointer(&p);
printf("%s\n", p);
Now it does print 5 and world, as desired.
Notice that in each case I made three changes:
I added a * to the parameter type expected by the function (int x to int *x, char *str to char **str)
I added a * before each use of the parameter in the function, that is, where I actually set the new value (5 and "world")
I added a & before the variables where I passed them to the function (set_to_5_via_pointer(&i) and set_string_via_pointer(&p)).
One more thing. When you see a function declaration like
void set_string_via_pointer(char **str);
this does not necessarily mean that the right way to call the function is
char **x;
set_string_via_pointer(x);
Yes, here x is of type char **, and function set_string_via_pointer expects an argument of type char **, so it's a match. But if you declare and call
char *y;
set_string_via_pointer(&y);
that's also a perfectly good way to pass an argument of type char **, and in this case, it's certainly they way that was expected.
const char* b = nullptr;
fun(&b);
This passes the pointer b by reference — or emulates pass by reference semantics — i.e. the memory address of where the variable is stored is passed instead of its value. This allows you to access the pointer originally declared in the calling function, and any changes made to that pointer in the called function would be visible in the calling function.
const char** b = nullptr;
fun(b);
Au contraire, this passes b by value. If you change the pointer to point to some other memory location in the called function, that change will not be reflected back in the calling function. Any attempt to dereference it while it is pointing to NULL would result in undefined behaviour as per the following clauses of C11:
If an invalid value has been assigned to the pointer, the behavior of
the unary * operator is undefined.
[...]
Among the invalid values for dereferencing a pointer by the unary *
operator are a null pointer, [...]
[6.5.3.2 Address and indirection operators, C11]
why use 1 but not 2?
Why use either of them?
1 is good, 2 donnot work
“There is nothing either good or bad, but thinking makes it so.” — They both serve different purposes. Though, the purpose of the second snippet is rather ambiguous here.
This code snippet
void fun(const char** a) {...}
1.
const char* b = nullptr;
fun(&b);
means passing the pointer b to the function fun by reference in the C meaning. So dereferencing the pointer to pointer a within the function you can change the original pointer b passed to the function indirectly through a pointer to it.
Here is a demonstration program.
#include <stdio.h>
void fun( const char **a )
{
*a = "Hello World!";
}
int main( void )
{
const char *b = NULL;
fun( &b );
puts( b );
}
The program output is
Hello World!
As you can see the pointer b is passed to the function indirectly through a pointer to it (by reference in the C meaning). Thus dereferencing the pointer to pointer a
*a = "Hello World!";
you can change the original pointer b defined in main.
In the second case
2.
const char** b = nullptr;
fun(b);
the pointer b is passed by value and if the function fun will dereference the null pointer then undefined behavior will be invoked.

Why sometimes pass pointer of pointer as parameters? [duplicate]

This question already has answers here:
Changing address contained by pointer using function
(5 answers)
Closed 2 years ago.
I'm new to C, sorry if my question sound dumb.
I see some function takes a "double" pointer as parameter, for example:
int main(int argc, char **argv)
{
...
}
I can understand the need for a double pointer such as argv, since argv is the pointer to the first element of argument array whose element is also a pointer to a char type. double pointer is needed in this case.
But for some functions like:
int getaddrinfo(const char *host, const char *service, const struct addrinfo *hints, struct addrinfo **result);
I don't understand why a double point is needed, since there is no array element involved, and I think getaddrinfo calls malloc internally, so it might be a reason to use double pointer, but we can have sth like this:
int main()
{
char *ptr_main;
test(ptr_main);
free(ptr_main);
}
void test(char *ptr)
{
ptr = malloc(10);
}
so the parameter is a single pointer char * ptr, no char **ptr is needed?
Passing a pointer, or address of an object allows the value stored at that location in memory to be changed by the function, so when the function returns, the information stored at that address now contains updated values. Likewise if a function argument accepts a pointer variable, and the pointer variable is to be changed, the address to that pointer must be passed, thus requiring the function prototype to be a pointer-to-pointer.
Addressing your question may illustrate: getaddrinfo question: I don't understand why a double point is needed, since there is no array element involved
Usage for this struct is typically:
struct addrinfo *result;//pointer variable
getaddrinfo("hostname", "servicename", NULL, &result);
// |_Passing address of a pointer variable.
// thus requiring prototype to accommodate
So as explained, for the function to receive updated struct information, it needs to send the address of the pointer variable, requiring it to be a double pointer:
int getaddrinfo(const char *host, ..., struct addrinfo **result);
Your code is incorrect. ptr is local to the scope of test. A pointer is just a number representing a memory address. You probably wouldn't expect test in the following code to affect the value of number_main, right?
int main()
{
int number_main;
test(number_main);
printf("%d\n", number_main);
}
void test(int number_main)
{
number_main = 10;
}
Maybe it helps if you internalize char * as some fictional type you'll call number_that_represents_memory_address, which you then think about as no different than int in this case?
This function
void test(char *ptr)
{
ptr = malloc(10);
}
that shall be declared before its usage in main deals with a copy of the value of the passed argument in main
char *ptr_main;
test(ptr_main);
You can imagine the function call and the function definition the following way
char *ptr_main;
test(ptr_main);
//...
void test( /* char *ptr */ )
{
char *ptr = ptr_main;
ptr = malloc(10);
}
So as you can see within the function it is the local variable ptr that is changed. The argument ptr_main that was not initialized in main and has indeterminate value stays unchanged.
To change the original pointer ptr_main you have to pass it to the function by reference.
In C the mechanism of passing by reference is implemented by passing an object indirectly through a pointer to it.
From the C Standard (6.2.5 Types)
— A pointer type may be derived from a function type or an object
type, called the referenced type. A pointer type describes an object
whose value provides a reference to an entity of the referenced
type. A pointer type derived from the referenced type T is sometimes
called ‘‘pointer to T’’. The construction of a pointer type from a
referenced type is called ‘‘pointer type derivation’’. A pointer type
is a complete object type.
So the function definition should look like
void test( char **ptr )
{
*ptr = malloc(10);
}
And called like
test( &ptr_main );
Thus dereferencing the pointer ptr in the function we get a direct access to the pointed object (pointer) ptr_main and it is the pointed (referenced) object that is changed now in the function
To make it more clear consider the following demonstrative program.
#include <stdio.h>
#include <stdlib.h>
void f1( int *px )
{
*px = 10;
}
void f2( int **ppx )
{
*ppx = malloc( sizeof( int ) );
**ppx = 20;
}
int main(void)
{
int x = 0;
printf( "Before calling f1 x = %d\n", x );
f1( &x );
printf( "After calling f1 x = %d\n", x );
int *px = &x;
printf( "Before calling f2 px = %p and *px = %d\n", ( void * )px, *px );
f2( &px );
printf( "After calling f2 px = %p and *px = %d\n", ( void * )px, *px );
free( px );
return 0;
}
The program output might look like
Before calling f1 x = 0
After calling f1 x = 10
Before calling f2 px = 0x7ffe79dd572c and *px = 10
After calling f2 px = 0x55db88f5d270 and *px = 20
To change the object x declared in main in the function f1 we need to pass it to the function by reference. Otherwise the function will deal with a copy of the value of the object.
The same situation takes place with the pointer px. Pointers are objects. If we want to change the pointer px declared in main in the function f2 we need to pass it to the function by reference. Otherwise the function will deal with a copy of the value of the object.
And as you can see from the program output the function f2 indeed changed the value of the pointer px declared in main.

warning: dereferencing ‘void *’ pointer [enabled by default] and error: void value not ignored as it ought to be help c

I'm confused about these errors. The str1 is a string that is being passed but I get the a warning at the compare and and error at the if statement
int stringcmp(void* str1, void* str2) {
int a = strlen(str1);
int b = strlen(str2);
int x;
if ( a < b ) {
x = a;
} else {
x = b;
}
int c = 0;
while ( c < x ) {
if (str1[c] < str2[c]) { //errors happen here
return 0;
}
if (str1[c] > str2[c]) {
return 1;
}
c++;
}
if ( a == x ) {
return 0;
}
return 1;
}
Your function receives void* arguments, so in the line you pointed you are dereferencing pointers to void and that is why you get the warnings. I am not sure why you are receiving the value not ignore as it ought to be because that means that you are assigning the value of a function that returns void, and that is not the case of strlen (which is the only one you are calling).
And you should also receive an error in the calls to strlen when passing to it a void* parameter.
So you have to change your function's signature to
int stringcmp(const char* str1, const char* str2);
to suppress the warnings and be able to call strlen on the strings.
Dereferencing void * makes no sense, ever. In C, void means "no type"/"nothing", if you have void *p;, what is *p supposed to be?
In C, void * is used as a generic pointer type, "pointer to anything". To use p above, you must cast it to the type of the object it is pointing at (passed in some other way, unbeknownst to the compiler). E.g.:
int i;
void *p = (void *) &i;
...
int j = *(int *)p;
You know what p points to, the compiler doesn't.
The type void * is often used to pass around opaque data, or to write generic functions (like qsort, it gets an array of unspecified elements and a function that compares them).
That said, as a (misguided) extension GCC allows pointer arithmetic on void *, so that p + 1 is like ((char *)p + 1), it points at the next char position.
Your error arises from the fact that you are attempting to use the array notation str1[c] on a void-pointer. To understand why this is wrong we need to look at what the array notation in C actually means.
Let us assume that we define char* str1. Here we have said that str1 is an address to some place in memory where there is a char.
To get the data that is stored on the address which str1 is referring to we can use *str1.
This is equivalent to saying: "Go to the address that str1 is holding and give me what is stored on that address".
When we use the array notation we can use str1[0], this will be a value fetched from a place in memory where there is an element of the same type that str1 was defined as. It is the same thing as saying *str1 (go to the address that str1 is pointing to and give me the value that is stored there).
An array is just a bunch of data stored in a sequence in memory and strings are just arrays of characters of exactly 1 byte in size stored immediately after one another.
When we say str1[1] we are telling the compiler to move by the size of the type that str1 was defined to be pointing at, in this case 1 byte(char), and then get us whatever is stored at that location. In the case of strings, this should be another char.
Now when we have defined str1 as void*, how would the compiler know how how much it should move in memory to get the next element in the array? Since void has no size it is impossible.
Hopefully you now understand what you need to change in this line to get rid of your errors
int stringcmp(void* str1, void* str2)

Why is this passing by value? [duplicate]

This question already has answers here:
Passing char pointer in C
(5 answers)
Closed 6 years ago.
I'm trying to understand the difference between pass by value and pass by reference, and I thought that I finally understand it, until I saw this code :
void change(char *p1)
{
p1="new";
}
int main()
{
char *txt1=(char*)malloc(200*sizeof(char));
txt1="original";
printf("before :%s\n",txt1);
change(txt1);
printf("after :%s\n",txt1);
}
Isn't it passing an address ? and then changing it and making it point to somewhere else ?
The output is :
before : original
after : original
Why is that?
There is no pass-by-reference in C. Function parameters are passed using pass-by-value. That is, p1 itself is passed by value. You can change the content of the memory address pointed to by p1 (*p1) from the called function, not p1 itself.
To clarify, for changing the content of a variable var, you need to pass the address of var, so, if you want to change a pointer itself, you need to pass the address of the pointer to the function.
That said, please see this discussion on why not to cast the return value of malloc() and family in C.
This is passing an address as value. To have the callee modify caller's local variable, pass address of what should be modified.
#include <stdio.h>
void change(char **p1)
{
*p1="new";
}
int main(void)
{
char *txt1; /* no causing memory leak */
txt1="original";
printf("before :%s\n",txt1);
change(&txt1);
printf("after :%s\n",txt1);
}
Pass By Reference and Pass By value.
Here's an example I've used before. Assume the following two functions:
void foo( T *p ) // where T is an arbitrary data type
{
*p = new_value(); // write new value to the thing p points to
}
void bar( void )
{
T var;
foo( &var ); // write new value to var
}
For function foo to update the contents of var (an object of type T) we must pass a pointer to var (type T *).
If we replace T with a pointer type P *, then the above code becomes
void foo( P **p )
{
*p = new_value(); // write new value to the thing p points to
}
void bar( void )
{
P *var;
foo( &var ); // write new value to var
}
The semantics are exactly the same; we want foo to write a new value to var, so we pass a pointer to var. It's just that in this case, var is already a pointer type, so we wind up passing a pointer to a pointer.
Basically, for foo to update the contents of var, you must pass the expression &var as the argument, meaning the type of the formal parameter p will always have one more level of indirection than the type of var.
Type of var Type of &var Type of p
----------- ------------ ---------
T T * T *
T * T ** T **
T ** T *** T ***
etc.
In function call change(txt1);, txt1 is passed by value. It is copied to the parameter p of function change. This makes pi points to the same string as txt1 pointing to. When new string is assigned to p1 then p1 changes to point to that string while txt1 pointing previous one.
this case is very similar to
char *s1 = "String";
char *s2 = s1; // Both s1 and s2 points to same string
s2 = "Another string" // Now s2 points to "Another string" while s1 pointing to "String".
Beside that your code is causing memory leak. It should be like
char *txt1=(char*)malloc(200*sizeof(char));
strcpy(txt1,"original");

Bad pointer type with a typedef

I'm having troubles when calling a function taking a pointer to a string as a parameter. I need to get an Element's name.
// method
void getStringFromCsv( char ** str );
Let me introduce the structures I'm working with (not written by me and part of a much bigger project, I can't modify them).
// typedefs
typedef char T_CHAR64[64];
typedef T_CHAR64 T_SYMBOL;
// generic element
typedef struct Element
{
T_SYMBOL name;
} T_Element;
// csv element
typedef struct CsvElement
{
Element * pElement;
int id;
} T_csvElement;
So, basically, I thought I would call the function like this :
T_Element * pData; // Not null, filled earlier
getStringFromCsv( &pData->pElement->name );
But this doesn't work (warning: passing argument 1 of ‘STR_toCsv’ from incompatible pointer type). I'm using gcc with NetBeans 6.8.
I tried many things...
T_SYMBOL foo = "foo";
T_SYMBOL * pFoo = &foo;
getStringFromCsv( pDef->name, &pFoo ); // error : passing from incompatible pointer type
T_CHAR * pBar = &foo; // error : init from incompatible pointer type
T_CHAR * pBaz = &(foo[0]); // OK
getStringFromCsv( pDef->name, &pBaz ); // OK
T_SYMBOL * pFooTest = &(foo[0]); // error : init from incompatible pointer type
...but ended up casting name to a char ** :
getStringFromCsv( (char**) &pData->pElement->name );
What is wrong with my code ?
Basically, SYMBOL = CHAR *, right ? Why is SYMBOL* != CHAR** ?
I'm pretty sure I'm missing something simple but right now... Nothing came.
EDIT
Here is getStringFromCsv :
void getStringFromCsv( char ** data )
{
// pDesc is defined and not null
csvDescriptorCat( pDesc, *data);
csvDescriptorCat( pDesc, "\t");
}
void csvDescriptorCat( CsvDescriptor * pDesc, char* str)
{
int len;
if( str != NULL)
{
len = strlen(str);
strcpy( &pDesc->line[pDesc->pos], str);
pDesc->pos += len;
}
}
If you wish to pass &pData->pElement->name to the function, the function must be declared as:
void getStringFromCsv(T_SYMBOL * str);
Alternatively you can use a temporary char * as Secure offered - but there's not much point in doing this, because any updates to that char *'s value can't be used - the ->name member can't be modified, as it's an array.
You might as well just declare the function as:
void getStringFromCsv( char * str );
...and call it as:
getStringFromCsv( pData->pElement->name );
(In this case, the function can still change the contents of the ->name array. What you can't do is to change the position of the array itself).
As well as Secure's option, there's another way if your compiler supports C99 compound literals:
getStringFromCsv( &(char *){ pData->pElement->name } );
name is an array of chars, so &name gives you a pointer to char[64], as Vicky already answered. But casting makes things worse, because it tells the compiler to treat the first chars of the array as a pointer to the real array.
See the C-FAQ: http://c-faq.com/aryptr/aryptr2.html
I think you can use a temporary char* here:
char *tmp = pData->pElement->name; // array decays to pointer
getStringFromCsv(&tmp);
If this is expected by the function. Expecting a char**, make sure that it doesn't try to reallocate the memory. For simply filling it, a char* would be enough.
Alas, one of the little secrets of C that people fail to tell you, an array is not the same thing as a pointer. if x is defined as int x[5] or whatever, &x == x. Try out this code below:
#include <stdio.h>
int main(int argc, const char *argv[])
{
char x [5];
char *y;
printf("%08x\n", x);
printf("%08x\n", &x);
printf("%08x\n", y);
printf("%08x\n", &y);
return 0;
}
Considering this : http://c-faq.com/decl/strlitinit.html
char a[4] = "hello";
char* p = "hello";
Aren't the same thing (even if they seem to be).
So my SYMBOL and CHAR* cannot be exchanged, right ?
Is there a workaround, or another solution ?
Yes, under the covers T_SYMBOL is handled like a char *. But you've declared it as a char[64], so you're passing in a pointer to a char[64] not a pointer to a pointer to a char. The compiler is keeping track of that for you.
Personally in this situation I would just cast it as you did at the end of your question.

Resources