2

I'm trying to create an instance of a struct with a const member.

Because the member is const, I'm creating a temporary value that has it initialized explicitly.

#include <string.h>
#include <stdlib.h>

typedef struct Thing {

    const char *value;

} Thing;

void createThing(const char *value) {
    
    // Create a temporary instance of Thing to initialize the const value
    Thing temporary = {
        .value = value
    };
    
    // Create a new thing
    Thing *thing = (Thing *)malloc(sizeof(Thing));
    if(thing == NULL)
        return NULL;

    // Copy the contents of temporary into the result
    memcpy(thing, &temporary, sizeof(Thing));

    // Return the result
    return thing;
}

Is it safe to use value this way, or should I create a new copy? For example:

const char *valueCopy = (char *)malloc(sizeof(char) * (strlen(value) + 1));
strcpy(valueCopy, value);

My reasoning is that the argument value will fall out of scope after the function call ends, and without copying, the value in the struct will become invalid.


As a side-note, I think I'm doing the right thing by copying from a temporary struct, but glad to be told otherwise.

Daniel Walker
  • 6,380
  • 5
  • 22
  • 45
Inigo Selwood
  • 822
  • 9
  • 20
  • 1
    `memcpy` will perform a shallow copy of the struct. That is, it will copy the value of the `value` pointer, not what it is pointing to. With all the consequences. Your second snippet is one of the possible ways to go (well, you will need to assign `valueCopy` to the struct field though). – Eugene Sh. May 25 '22 at 13:17
  • 1
    Just alloc struct and use `thing->value = strdup(value);` – Selvin May 25 '22 at 13:20
  • 6
    `const char *value` is not a `const` member. It is a *non*-`const` member pointing to `const` data. – John Bollinger May 25 '22 at 13:22
  • @Selvin But it's a const member, so surely can't be overwritten? – Inigo Selwood May 25 '22 at 13:22
  • 3
    A `const` member pointing to non-`const` data would be `char * const value`. – John Bollinger May 25 '22 at 13:24
  • 3
    Consider avoiding `const` structure members. They are a lot of trouble -- more, IMO, than they are worth. Use `const` instances of the whole structure where appropriate, or pointers to the `const`-qualified version of the structure type. – John Bollinger May 25 '22 at 13:29
  • Although the question exhibits a misunderstanding of `const` declarations, that is not what it is about, and it should not have been closed as a dupe of a question about that topic. Reopening. – John Bollinger May 25 '22 at 13:36
  • No need to use a temporary struct or memcpy. Just do `*thing = (Thing) { .value = value }` after the call to malloc. Also declare your function to return a `Thing *`, not `void`. – David Grayson May 25 '22 at 14:15
  • And don't forget to check the return of `strdup()` for NULL- – Goswin von Brederlow May 25 '22 at 18:15

1 Answers1

1

Leaving aside the side issue of constness, the question exhibits an essential misunderstanding. Here:

Is it safe to use value this way, or should I create a new copy? For example:

const char *valueCopy = (char *)malloc(sizeof(char) * (strlen(value) + 1));
strcpy(valueCopy, value);

My reasoning is that the argument value will fall out of scope after the function call ends, and without copying, the value in the struct will become invalid.

Although it is true that function parameter value goes out of scope when execution leaves the function, that's irrelevant. The first proposed alternative initializes a structure member from the value of value. The structure member is a separate object from the function parameter. The assignment makes that object refer to the same data that value does. It does not make the member an alias for value itself. The structure member has its own lifetime, separate from and (in this case) unrelated to the lifetime of the value parameter. In that sense, then, yes, it is safe to use value as you do in the first code snippet.

HOWEVER, there are other reasons why you might want to make a copy of the pointed-to data, as well as reasons why you might want to avoid doing that. It's unclear from the question which is appropriate in your particular case. But all this is independent of the constness of the member.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157