2

I think I have some issue understanding string and string literal.

This is what I learned from my class, when passing to a function, const char * indicates this is a string literal, while char * indicates a string.

Suppose I have a struct declaration:

struct node{
    char *name;
    struct node *next;
};

and a function, this is the function I want to implement:

void load_buffer(struct node *input_node, char *input_name);

This function is supposed to assign input_name to the struct's member name.

My confusion comes from here. Within the body of load_buffer, should I write:

input_node->name = input_name;

Or I should use strcpy/strncpy to do this?

strcpy(input_node->name, input_name);// Suppose it's safe in this case.

To summarize, I am not sure if I should use direct assignment or strcpy family functions to assign a string/string literal to the member of my struct.

Thanks for help. :)

DigitalSoul
  • 139
  • 1
  • 8
  • 1
    `input_node->name = input_name;` should be fine, given that the `input_name` does not change later on. – Sourav Ghosh Apr 17 '19 at 08:02
  • 1
    That depends on the semantics you want: Does the `struct` have to *own* the referenced string, and how does it have to be allocated? – Deduplicator Apr 17 '19 at 08:02
  • If you have a pointer, you need to make it point somewhere valid before using `strcpy`. As for which to use, that really depends. Will you *always* be using string literals? Do you need to modify the strings? Append to them? – Some programmer dude Apr 17 '19 at 08:03
  • No, I don't need to modify the string, the "string" that I will be using is read from a file, so does this indicate that I should use direct assignment instead of strcpy family functions? – DigitalSoul Apr 17 '19 at 08:11
  • "string" will be same for all the nodes? – kiran Biradar Apr 17 '19 at 08:12
  • No, the reason why I use double quotes to include string is that I don't know whether I should use string or string literal to describe this "string". The actual string / string literal is per line that is in my file, but that is not part of this question anyway. – DigitalSoul Apr 17 '19 at 08:16
  • If you read a string from a file you can't just do assignments, as the assignments will just make all pointers point to the very same buffer (the buffer you read). You need to allocate memory for each string. Either dynamically, or turn your pointers into arrays. – Some programmer dude Apr 17 '19 at 08:54

3 Answers3

3

In case of pointer assignment, pointers in each node will point to same location. Thus nodes will be always pointing to updated value. If you are intended to make each node to contain different input then this approach does not suit your requirement.

input_node->name = input_name;

In case of strcpy, pointers in each node will point to different location. Before to that you need to create memory for each pointer.

input_node->name = malloc(strlen(input_name)+1); //Allocate memory first.
strcpy(input_node->name, input_name);// Suppose it's safe in this case.

To visualize: enter image description here

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
kiran Biradar
  • 12,700
  • 3
  • 19
  • 44
3

... when passing to a function, const char * indicates this is a string literal, while char * indicates a string.

Not exactly. const char * declares that the function will not try to modify the string. So it is perfectly suited for string litteral because they cannot be modified.

For your question, the answer is it depends on your real requirements. But simply storing the passed pointer if dangerous if the struct can persist after the function and if the string can be changed in the caller. Let us look at the following code:

void load_buffer(struct node *input_node, const char *input_name) {
    input_node->name = name;
}

struct node nodes[2];
char buf[4];
const char *data[] = { "foo", "bar"};
for (int i=0; i<2; i++) {
    strcpy(buf, data[i]);    // suppose it is read from somewhere (file, stdin, socket)
    load_buffer(node + i, buf);
}

Both node objects will have their name member pointing to the string buf from caller and will both point to "bar" (the content of buf at the end of the loop)!

If you want to keep the value of the string at call time, you should copy it in allocated memory:

void load_buffer(struct node *input_node, const char *input_name) {
    input_node->name = strdup(name);  // allocation errors should be tested...
}

But you should then free the name member, when the node is no longer in use...

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • Yes, const means more that it's not allowable to change the value than that it's a magic constant. They're passed into functions fairly often, the function can't change them. – Alan Corey Jan 10 '22 at 19:42
1

First to address some terminology:

  • this is a string literal: "I am a string literal"
  • this is a type: char* (aka pointer to char)
  • this is also a type: const char* (aka pointer to constant char)
  • this is the declaration of a variable of type char*: char* str
  • this is the declaration of a variable of type const char*: const char* cstr

A pointer is not a string literal. A pointer can point to string literal, an array, just to a single element or can be null.

In C a string is a null-terminated char array.

In C you can assign a char* variable to a string literal, but modifying the string literal is illegal, so it is strongly advised to never do that. The reason why this is allowed are historical.

char* a = "asd"; // allowed, but frowned upon
// a points to a string literal, so we can say a is a string
// a is not a string literal

char b = 'x';
char* c = &b;
// c points to a single char
// we cannot say c is a string

char d[10] = "asd";
// d is a char array. Its content is a string, so we can say d is a string.
// d is not a string literal
// the string literal is copied into the array d

char* e = d; // or equivalent char* e = &d[0];
// e points to a string

char f[4] = {'a', 's', 'd', '\0'};
// f is an array. Its content is a string, so we can say f is a string

char* g = f;
// g points to a string. We can say g is a string

char h[3] = {'a', 's', 'd'};
// h is an array. Its content is NOT a string, because the char array is not null terminated

char* i = h;
// i is not a string

And now go through all the above once more, but not replace char with const char and all the comments still stand (except that `const char* a = "asd" is now ok).


And now to the problem at hand.

There are two scenarios:

  1. Each node has its own string and "owns" that string. Each node is responsible for allocating memory for the string and freeing that memory. The string lives as long as the node lives. In this case use malloc and strcpy to create a string for each node. Don't forget to free the string when the node is destroyed. This is the most common scenario and probably what you want.

  2. A node doesn't own its own string, but rather points to an external string. It is not allowed to neither allocate nor free memory for that string. There is another entity responsible for managing the lifetime of that string and making sure the string is alive at least as the node is alive. The string can outlive the node without a memory leak.

For example consider this scenario:

  • there is a list R of string resources. This list owns those resources. This list would use scenario 1
  • we need to keep two different sort orders of R. So we have two lists A and B that would use scenario 2: each node in A and B just points to a string of R.
bolov
  • 72,283
  • 15
  • 145
  • 224