简体   繁体   中英

how to pass a pointer to a pointer to a struct member?

So the problem at hand is to convert a string of digits in the format YYYYMMDD to a struct tm type member within some other structure. Truth is, I really only care about getting a struct tm with reasonable values in it.

Consider the following struct :

typedef struct some_node {
    char somestring[64];
    char anotherstring[128];
    struct tm date_one;
    struct tm date_two;
    int some_val;
    struct some_node *prev;
    struct some_node *next;
} some_node_t;

Inside there I have two members of type struct tm from the time.h header. Seems very reasonable. Also there are pointer members in there to make a linked list however that isn't the issue.

So I create the first node in my yet to be created linked list like so :

/* begin the linked list of some_node_t */
struct some_node *t_head =
                calloc( (size_t) 1, sizeof( some_node_t ) );
if ( t_head == NULL ) {
    /*Memory allocation fault */
    printf ( " FAIL : Memory allocation fault at %s(%d)\n",
               __FILE__, __LINE__  );
    exit ( EXIT_FAILURE );
}

/* I used calloc above which zero fills memory so these 
 * next lines are not really needed. Better safe than sorry. */
t_head->some_val = 0;
t_head->prev = NULL;
t_head->next = NULL;

Then I can stuff char data into the two char members :

strcpy ( t_head->somestring, "birthday" );
strcpy ( t_head->anotherstring, "19981127" );

No problem there.

Messing with the conversion of a string to a struct tm seems reasonable within a function as I have to do it twice perhaps.

Therefore I write this :

int timestr_to_tm ( struct tm **date_val, char *yyyymmdd ) {
    /* assume 8 digits received in format YYYYMMDD */
    int j, date_status = -1;
    char yyyy[5]="0000";
    char mm[3]="00";
    char dd[3]="00";

    /* copy over the year digits first */
    for ( j=0; j<4; j++ )
        yyyy[j]=yyyymmdd[j];

    /* month digits */
    mm[0]=yyyymmdd[4];
    mm[1]=yyyymmdd[5];

    /* day digits */
    dd[0]=yyyymmdd[6];
    dd[1]=yyyymmdd[7];

    *(date_val)->tm_year = atoi(yyyy) - 1900;
    *(date_val)->tm_mon = atoi(mm) - 1;
    *(date_val)->tm_mday = atoi(dd);
    *(date_val)->tm_hour = 0;
    *(date_val)->tm_min = 0;
    *(date_val)->tm_sec = 0;
    *(date_val)->tm_isdst = -1;

    return 0;

}

So my hope here is that I can pass a pointer to a pointer to the member date_one within t_node to that function.

if ( timestr_to_tm ( &(t_node->date_one), "19981127" ) < 0 ) {
    /* deal with a bad date conversion */
}

Well my compiler has a fit here. Claiming :

error: argument #1 is incompatible with prototype:

Perhaps I should have &t_head->date_one but I think that the pointer dereference operator "->" takes precedence over the "address of" operator. Perhaps it is bad policy to even attempt to pass a pointer to a member within a struct?

Even worse, within the function timestr_to_tm() I get :

error: left operand of "->" must be pointer to struct/union

in those lines where I try to assign values into the struct tm variable.

I tried all this without passing pointers and the process works however upon return there is nothing in the struct tm member. So I am wondering, what am I missing here ?

I think that the pointer dereference operator -> takes precedence over the "address of" operator

It does, so does it over the dereference, * operator. So this, for example:

*(date_val)->tm_mday = atoi(dd);

should be

(*date_val)->tm_mday = atoi(dd);

But: Why would you do this? Why not pass a pointer to the struct tm , and use -> without one more level of indirection?

If you really want to pass a pointer to a pointer to a struct tm, you can, you just need to create a pointer variable to hold the pointer and pass a pointer to that:

struct tm *pointer = &t_node->date_one;
if ( timestr_to_tm ( &pointer, "19981127" ) < 0 ) {
    ...

The question is why? You don't need the extra level of indirection as you're not trying to change the pointer, you just want to fill in the struct tm within the node. So just use a single pointer:

int timestr_to_tm ( struct tm *date_val, char *yyyymmdd ) {
    :
    date_val->tm_year = atoi(yyyy) - 1900;
    :

then you can call it with a simple pointer and don't need to create an extra pointer variable:

if ( timestr_to_tm ( &t_node->date_one, "19981127" ) < 0 ) {
    ...

Since I'm too lazy to understand the question, I'll just add this unrelated example which hopefully helps to clear things up. This is very straightforward, if you use the correct syntax and level of indirection.

typedef struct _node node_t;
struct _node {
    node_t* next;
    int     a;
};

void fill_in_int(int* pointer_to_int) {
    *pointer_to_int = 42;
}

void populate_all_nodes(node_t* list, int a_value) {
    node_t* node;

    for (node=list; node; node = node->next) {
        fill_in_int( &(node->a) );       // Pass pointer to member a in node
    }
}

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