0

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 ?

14
  • 2
    This is a lot to digest. For problems like this, it is much easier on the people helping if you can boil down the issue to a small example that shows/focuses on only the problematic code. Many times, this very act of narrowing down the problem makes it apparent to you as well, and the problem is solved. Commented May 12, 2013 at 20:18
  • 1
    to Carbonic Acid ( H2CO3 ) : my intention was that the second parameter was not to be a string const but usually a variable from somewhere else. Valid point however. Commented May 12, 2013 at 20:23
  • 1
    @Beta "At some point you should consider graduating to C++" - I think that is absolutely terrible advice. You make that recommendation having no background on what this application is or where it's going. Also, your use of the word "graduating" implies that C is somehow inferior, or elementary. Quite the opposite, in my opinion. I think C++ programmers should consider graduating to C. Commented May 12, 2013 at 20:24
  • 1
    @JonathonReinhart: strcpy is fine if you know that the target array is big enough to hold the string. strncpy is not just a "safer" strcpy; it will needlessly pad the target with extra null characters or, worse, leave the target without a null terminator (i.e., not a string). To do what you might expect strncpy to do, you can set the initial byte of the target to '\0' and then call strncat. See my rant on the topic. Commented May 12, 2013 at 20:25
  • 1
    @KeithThompson on your first point, you may "know" it now, but what happens when someone carelessly makes that literal a little bigger, and bigger... I'm aware of the intricacies of strncpy, which is why I mentioned passing 1 minus the allocated length. I forgot to mention the part about ensuring the '\0' is there. I appreciate the comment though, and the link, which I am reading now. Commented May 12, 2013 at 20:29

3 Answers 3

2

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 ) {
    ...
Sign up to request clarification or add additional context in comments.

4 Comments

If only the internet allowed me to pass a cold beer over to you. Perfect. Flawless. Yes that makes sense. I was making life more complex and you Sir, nailed the problem on the head in one shot.
@paullanken (so did I, 20 minutes earlier. I love how you appreciate that I put up with your, hm, basic questions and bothered explaining all this to you.)
I do .. in fact ... I am guessing that all that was needed was a more clear manner of expressing it. I was just in the process of thinking "first that the internet works" and secondly "there are great people out there making it work". You Sir are one of those people. I am only guessing that Carbonic Acid is in a bottle of Coca Cola .. but that is a guess.
@H2CO3 Of course, once you pushed the OP past his mental obstacles, the accepted answer goes to someone else :-) It only took "one shot" after your continued explanations.
2

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?

9 Comments

The issue is that I am dealing with a large pile of linked list nodes in which are pointers to other structs and then within them there are etc etc etc. A classic example is the kstat kernel stats chain within Solaris servers. In any case I have a node in hand and in scope and within that there is a struct tm member that I need to pull out ( or isolate ) and pass it to a function which will load in time data from some other variable of format YYYYMMDD. So I am sort of stuck looking at pointer syntax here. What fun.
@paullanken Isn't then str_to_tm(&node->struct_tm, "19940628"); a viable option?
I just did a quick manpage check on that. You had me there for a second. At the moment I have the function prototype as int timestr_to_tm ( struct tm **date_val, char *yyyymmdd ); and I call this like so : if ( timestr_to_tm ( &(t_head->date_one), date_yyyymmdd ) < 0 ) however this results in : line 1528: error: argument #1 is incompatible with prototype: prototype: pointer to pointer to struct tm : "sca23_tt.c", line 132 argument : pointer to struct tm
@paullanken That's what I'm talking about. &(t_head->date_one) is of type struct tm *, but your function takes struct tm **. Don't insist I'm wrong because I am not. Instead change your function as per I advised you to do so in this answer.
Why not pass a pointer to the struct tm ? I thought that was what I was doing here. Now I have to go back and really think. There is probably a dirt easy way to do this and I'm making it messy. Not on purpose either.
|
1

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
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.