字符串 vs 字符串字面量:直接赋值还是 strcpy/strncpy?

String vs String Literal: Direct assignment or strcpy/strncpy?

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

这是我从class中学到的,当传递给函数时,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 系列函数来将 string/string 文字分配给我的结构成员。

感谢您的帮助。 :)

在指针分配的情况下,每个节点中的指针将指向相同的位置。因此节点将始终指向更新的值。如果您打算让每个节点包含不同的 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.

形象化:

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

不完全是。 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" ([=17= 的内容] 在循环的末尾)!

如果你想在调用时保留字符串的值,你应该将它复制到分配的内存中:

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

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

首先要解决一些术语:

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

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

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

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', '[=10=]'};
// 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. 每个节点都有自己的字符串和 "owns" 那个字符串。每个节点负责为字符串分配内存并释放该内存。只要节点存在,字符串就会存在。在这种情况下,使用 mallocstrcpy 为每个节点创建一个字符串。不要忘记在节点被销毁时 free 字符串。这是最常见的情况,可能也是您想要的。

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

例如考虑这种情况:

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