字符串vs字符串文字:直接分配还是strcpy / strncpy?

时间:2019-04-17 07:59:19

标签: c struct strcpy

我认为我在理解字符串和字符串文字时遇到一些问题。

这是我从类中学到的,传递给函数时,const char *表示这是字符串文字,而char *表示字符串。

假设我有一个结构声明:

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

和一个函数,这是我要实现的函数:

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

该功能应该将input_name分配给结构的成员名称。

我的困惑来自这里。我应该在load_buffer主体内写:

input_node->name = input_name;

还是我应该使用strcpy / strncpy来做到这一点?

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

总而言之,我不确定是否应该使用直接赋值或strcpy族函数将字符串/字符串文字赋值给struct成员。

感谢您的帮助。 :)

3 个答案:

答案 0 :(得分:3)

在分配指针的情况下,每个节点中的指针将指向相同的位置。因此,节点将始终指向更新的值。如果要使每个节点包含不同的input,则此方法不适合您的要求。

input_node->name = input_name;

对于strcpy,每个节点中的指针将指向不同的位置。在此之前,您需要为每个指针创建内存。

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

要可视化: enter image description here

答案 1 :(得分:3)

  

...传递给函数时,const char *表示这是字符串文字,而char *则表示字符串。

不完全是。 const char *声明该函数将不会尝试修改字符串。因此,它非常适合用于乱抛垃圾,因为它们不能被修改。

对于您的问题,答案是这取决于您的实际要求。但是,如果结构可以在函数之后继续存在并且字符串可以在调用方中更改,则在危险的情况下,只需存储传递的指针即可。让我们看下面的代码:

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

两个node对象的name成员都指向调用者的字符串buf,并且都指向"bar"buf的内容位于循环结束)!

如果要在调用时保留字符串的值,则应将其复制到分配的内存中:

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

但是当该节点不再使用时,您应该释放name成员...

答案 2 :(得分:1)

首先要讲一些术语:

  • 这是字符串文字:"I am a string literal"
  • 这是一种类型:char*(也就是指向char的指针)
  • 这也是一种类型:const char*(也就是指向常量char的指针)
  • 这是char*类型的变量的声明:char* str
  • 这是const char*类型的变量的声明:const char* cstr

指针不是字符串文字。指针可以指向字符串文字,数组,仅指向单个元素或可以为null。

C中,字符串是一个以null结尾的char数组。

您可以在C中为字符串文字分配一个char*变量,但是修改字符串文字是非法的,因此强烈建议不要这样做。允许这样做的原因是历史性的。

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

现在再次进行以上所有操作,但不要用char替换const char,并且所有注释仍然有效(除了`const char * a =“ asd”现在可以了)。


现在要解决手头的问题。

有两种情况:

  1. 每个节点都有自己的字符串,并“拥有”该字符串。每个节点负责为字符串分配内存并释放该内存。只要节点存在,字符串就存在。在这种情况下,请使用mallocstrcpy为每个节点创建一个字符串。节点被销毁时,请不要忘记free字符串。这是最常见的情况,可能是您想要的。

  2. 节点不拥有自己的字符串,而是指向外部字符串。不允许为该字符串分配或释放内存。还有另一个实体负责管理该字符串的生存期,并至少在节点处于活动状态时确保该字符串处于活动状态。字符串可以使节点的寿命更长,而不会导致内存泄漏。

例如,请考虑以下情形:

  • 有一个字符串资源列表R。此列表拥有这些资源。此列表将使用方案1
  • 我们需要保留R的两个不同排序顺序。因此,我们有两个使用场景2的列表A和B:A和B中的每个节点都指向一个字符串R。