简体   繁体   中英

Initializing local pointers by passing the address of a pointer

I see the following way of initializing a local pointer in almost every part of my code. want to understand the reason and intricacies in doing so.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void initialize_p(char **p)
{
        char *p_local = NULL;
        p_local=malloc(6);
        strcpy(p_local,"sandy");
        *p = p_local;
}
int main(void)
{
        char *p = NULL;
        initialize_p(&p);
        printf("Name : %s\n",p);
        return 0;
}

It is just that, i am showing here with simple string. And in my actual code, it is being done using structures.

I kind of understand the above logic and also I don't. Please clear the concept involved in the above style of implementing. Also, let me know if there is any other better way of doing the same.

Please Enlighten .. :)

I'd probably return the newly allocated string instead of passing a pointer to a pointer as an argument:

char *initialize(void) {
    char *init = "sandy"; 
    char *ret = malloc(sizeof(init)+1);
    if (ret != NULL)
        strcpy(ret, init);
    return ret;
}

int main() {
    char *p = initialize();
    printf("Name: %s\n", p);
    free(p);
    return 0;
}

it's an out-parameter. the function produces a result for you, and saves it to the parameter you pass. this is often used when the size of the result may vary, or if the type of the parameter is Opaque.

Personally, I think returning the result is much clearer, and is less error-prone when a dynamic allocation is required (as seen in JerryCoffin's answer +1).

when a dynamic allocation is not required, then pass it by reference (as a non-const parameter) if it is not trivially small:

struct t_struct { int a[100]; };

void InitStruct(struct t_struct* pStruct) {
  pStruct->a[0] = 11;
  ...
}

struct t_struct s;
void InitStruct(&s);

and if it is trivially small, you may consider returning by value.

In initialize_p a chunk of memory is allocated and some string is copied into the memory. In order for the caller of initializer to get the address of this new chunk of memory the address of the pointer p is passed to initialize_p:

    char **p

         +---+---+---+---+---+----+
   *p -> | s | a | n | d | y | \0 |
         +---+---+---+---+---+----+

if only the *p would have been passed then setting the pointer inside the function would be the equivalent of:

void foo(int a)
{
  a=3;
  ...
}

a better way would be to use strdup which does the same thing as you do in your function

char* initialize_p()
{
   return strdup("sandy");
}

also make sure you free the string that is returned to avoid memory leak.

I'd suggest you to create allocation and deallocation function pair

char *createP()
{
    char *p = NULL;
    p=malloc(6);
    strcpy(p,"sandy");
    return p;
}
void freeP(char *p)
{
    if (p) free(p);
}
int main(void)
{
    char *p = createP();
    printf("Name : %s\n",p);
    freeP(p);
    return 0;
}

Clearing the concept? Well, in such a simple case I don't see the point in operating with byref output parameters - for me, object-oriented-like structure constructor functions are easier to understand if they work like this:

struct foo *bar = myobj_new(); // uses malloc and returns a pointer
// do something with bar
myobj_destroy(bar); // uses free

Some agree that this design is good because then the return value of the function can be used as an error/success code (have you seen SQLite3?), but I disagree. I think the primary, the most important result of a function should go through the return value, and not some auxiliary stuff. (I tend to write libraries in which failure is indicated by returning NULL and setting a byref-passed error code).

The only valid scenario I can think of is when it's more logical or symmetrical to pass arguments like this. For example, imagining a very strange sort function which is similar to the C stdlib function qsort() , but requires its comparison function itself to make the swapping of two elements when needed. The comparison function obviously needs byref access to its two parameters in order to exchange them, but it may also be useful to return the originally encountered order to the caller sort function in order to optimize the algorithm. So this comparison function could be something like:

int compare(void *a, void *b)
{
    int x = *(int *)a;
    int y = *(int *)b;

    if (x > y)
    {
        *(int *)a = y;
        *(int *)b = x;
        return +1;
    } else if (x < x) {
        return -1;
    }

    return 0;
}

Well, pretty much that's it about my opinion...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM