如何正确分配新的字符串值?

发布于 2024-09-07 10:44:15 字数 1218 浏览 9 评论 0原文

我试图了解如何以最干净/最安全的方式用 C 解决这个小问题。这是我的例子:

#include <stdio.h>

int main(int argc, char *argv[])
{
    typedef struct
    {
        char name[20];
        char surname[20];
        int unsigned age;
    } person;

    // Here I can pass strings as values...how does it work?
    person p = {"John", "Doe", 30};

    printf("Name: %s; Age: %d\n", p.name, p.age);

    // This works as expected...
    p.age = 25;

    //...but the same approach doesn't work with a string
    p.name = "Jane";

    printf("Name: %s; Age: %d\n", p.name, p.age);

    return 1;
}

编译器的错误是:

main.c:在函数“main”中:main.c:18: 错误:不兼容的类型 从类型分配给类型“char[20]” '字符*'

我知道 C(不是 C++)没有 String 类型,而是使用 char 数组,因此另一种方法是更改​​示例结构以保存 的指针chars:

#include <stdio.h>

int main(int argc, char *argv[])
{
    typedef struct
    {
        char *name;
        char *surname;
        int unsigned age;
    } person;

    person p = {"John", "Doe", 30};

    printf("Name: %s; Age: %d\n", p.name, p.age);

    p.age = 25;

    p.name = "Jane";

    printf("Name: %s; Age: %d\n", p.name, p.age);

    return 1;
}

这按预期工作,但我想知道是否有更好的方法来做到这一点。

I'm trying to understand how to solve this trivial problem in C, in the cleanest/safest way. Here's my example:

#include <stdio.h>

int main(int argc, char *argv[])
{
    typedef struct
    {
        char name[20];
        char surname[20];
        int unsigned age;
    } person;

    // Here I can pass strings as values...how does it work?
    person p = {"John", "Doe", 30};

    printf("Name: %s; Age: %d\n", p.name, p.age);

    // This works as expected...
    p.age = 25;

    //...but the same approach doesn't work with a string
    p.name = "Jane";

    printf("Name: %s; Age: %d\n", p.name, p.age);

    return 1;
}

The compiler's error is:

main.c: In function ‘main’: main.c:18:
error: incompatible types when
assigning to type ‘char[20]’ from type
‘char *’

I understand that C (not C++) doesn't have a String type and instead uses arrays of chars, so another way to do this was to alter the example struct to hold pointers of chars:

#include <stdio.h>

int main(int argc, char *argv[])
{
    typedef struct
    {
        char *name;
        char *surname;
        int unsigned age;
    } person;

    person p = {"John", "Doe", 30};

    printf("Name: %s; Age: %d\n", p.name, p.age);

    p.age = 25;

    p.name = "Jane";

    printf("Name: %s; Age: %d\n", p.name, p.age);

    return 1;
}

This works as expected, but I wonder if there a better way to do this.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

夏了南城 2024-09-14 10:44:15

第一个示例不起作用,因为您无法将值分配给数组 - 在这方面,数组的工作方式(有点)类似于 const 指针。不过,您可以做的是将新值复制到数组中:

strcpy(p.name, "Jane");

如果您事先知道字符串的最大大小,则可以使用 Char 数组,例如在第一个示例中,您 100% 确定名称适合 19 个字符(不是 20,因为始终需要一个字符来存储终止零值)。

相反,如果您不知道字符串可能的最大大小,和/或您想要优化内存使用,例如避免为名称“John”保留 512 个字符,则指针会更好。但是,使用指针,您需要动态分配它们指向的缓冲区,并在不再需要时释放它,以避免内存泄漏。

更新:动态分配缓冲区的示例(使用第二个示例中的结构定义):

char* firstName = "Johnnie";
char* surname = "B. Goode";
person p;

p.name = malloc(strlen(firstName) + 1);
p.surname = malloc(strlen(surname) + 1);

p.age = 25;
strcpy(p.name, firstName);
strcpy(p.surname, surname);

printf("Name: %s; Age: %d\n",p.name,p.age);

free(p.surname);
free(p.name);

The first example doesn't work because you can't assign values to arrays - arrays work (sort of) like const pointers in this respect. What you can do though is copy a new value into the array:

strcpy(p.name, "Jane");

Char arrays are fine to use if you know the maximum size of the string in advance, e.g. in the first example you are 100% sure that the name will fit into 19 characters (not 20 because one character is always needed to store the terminating zero value).

Conversely, pointers are better if you don't know the possible maximum size of your string, and/or you want to optimize your memory usage, e.g. avoid reserving 512 characters for the name "John". However, with pointers you need to dynamically allocate the buffer they point to, and free it when not needed anymore, to avoid memory leaks.

Update: example of dynamically allocated buffers (using the struct definition in your 2nd example):

char* firstName = "Johnnie";
char* surname = "B. Goode";
person p;

p.name = malloc(strlen(firstName) + 1);
p.surname = malloc(strlen(surname) + 1);

p.age = 25;
strcpy(p.name, firstName);
strcpy(p.surname, surname);

printf("Name: %s; Age: %d\n",p.name,p.age);

free(p.surname);
free(p.name);
咆哮 2024-09-14 10:44:15

将字符串视为抽象对象,将字符数组视为容器。字符串可以是任意大小,但容器必须至少比字符串长度多 1(以容纳空终止符)。

C 对字符串的语法支持非常少。没有字符串运算符(只有字符数组和字符指针运算符)。您无法分配字符串。

但你可以调用函数来帮助实现你想要的。

这里可以使用 strncpy() 函数。为了最大程度地保证安全,我建议遵循以下模式:

strncpy(p.name, "Jane", 19);
p.name[19] = '\0'; //add null terminator just in case

另请参阅 strncat()memcpy() 函数。

Think of strings as abstract objects, and char arrays as containers. The string can be any size but the container must be at least 1 more than the string length (to hold the null terminator).

C has very little syntactical support for strings. There are no string operators (only char-array and char-pointer operators). You can't assign strings.

But you can call functions to help achieve what you want.

The strncpy() function could be used here. For maximum safety I suggest following this pattern:

strncpy(p.name, "Jane", 19);
p.name[19] = '\0'; //add null terminator just in case

Also have a look at the strncat() and memcpy() functions.

不喜欢何必死缠烂打 2024-09-14 10:44:15

两者的结构不同。当初始化第一个结构体时,会分配大约 40 字节的内存。当初始化第二个结构体时,会分配大约 10 字节的内存。 (实际数量取决于体系结构)

您可以使用字符串文字(字符串常量)来初始化字符数组。这就是为什么

person p = {"John", "Doe",30};

在第一个示例中有效。

您不能在 C 中分配(传统意义上的)字符串。

您拥有的字符串文字(“John”)会在代码执行时加载到内存中。当您使用这些文字之一初始化数组时,该字符串将被复制到新的内存位置。在第二个示例中,您只是将指针复制到字符串文字(的位置)。执行以下操作

char* string = "Hello";
*string = 'C'

可能会导致编译或运行时错误(我不确定。)这是一个坏主意,因为您正在修改文字字符串“Hello”,例如在微控制器上,它可能位于只读内存中。

The two structs are different. When you initialize the first struct, about 40 bytes of memory are allocated. When you initialize the second struct, about 10 bytesof memory are allocated. (Actual amount is architecture dependent)

You can use the string literals (string constants) to initalize character arrays. This is why

person p = {"John", "Doe",30};

works in the first example.

You cannot assign (in the conventional sense) a string in C.

The string literals you have ("John") are loaded into memory when your code executes. When you initialize an array with one of these literals, then the string is copied into a new memory location. In your second example, you are merely copying the pointer to (location of) the string literal. Doing something like:

char* string = "Hello";
*string = 'C'

might cause compile or runtime errors (I am not sure.) It is a bad idea because you are modifying the literal string "Hello" which, for example on a microcontroler, could be located in read-only memory.

悲歌长辞 2024-09-14 10:44:15

第一个结构体是字符数组 [],第二个结构体是指向字符串的指针(对于 64 位机器,大小为 8 个字节)。根据 Stephen Kochan 的《Programming in C》一书,C 允许您分配常量字符串的唯一时间是在定义和初始化 char 数组时,甚至

char name[20] = { "John Doe" };

也不例外

char name[20];
name = { "John Doe" };

在 char *name; 的情况下 。 name 是一个字符指针,而不是一个数组。当你这样做时,

p.name = "Jane";

它指向另一个字符串对象。

person p = { .surname = "Doe", .name = "Johnny", .age = 30 };
printf("Ptr. value:\tp.name: 0x%p;\tp.surname: 0x%p\n", p.name, p.surname);
p.name = "Spy, watch out!";
printf("Ptr. value:\tp.name: 0x%p;\tp.surname: 0x%p\n", p.name, p.surname);

输出:

Ptr. value:     p.name: 0x00007FF726F7B16C;     p.surname: 0x00007FF726F7B174
Ptr. value:     p.name: 0x00007FF726F7ACE8;     p.surname: 0x00007FF726F7B174

但是,在字符数组[]的情况下,在更改

strcpy(p.name, "Jane");

其内容后,缓冲区p.name[]的地址永远不会改变。

C 和 Python 之间一个有趣的相似之处是
Python 的 String 是不可变的,类似于 C 的字符串指针,其中
字符串文字是只读的。
Python的List是可变的,类似于C的字符数组。

>>> name = "John"
>>> print(hex(id(name)))
0x261654235f0
>>> name = "Jane"
>>> print(hex(id(name)))
0x261654237b0
>>> type(name)
<class 'str'>
>>> name[1] = 'o'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> name = list(name)
>>> type(name)
<class 'list'>
>>> name
['J', 'a', 'n', 'e']
>>> name[1] = 'o'
>>> name
['J', 'o', 'n', 'e']
>>> name = ''.join(name)
>>> name
'Jone'
>>> type(name)
<class 'str'>
>>>

The first struct is a character array [] and the second struct is a pointer * to the character string (size 8 bytes for a 64-bit machine). According to Stephen Kochan's book "Programming in C", the only time that C lets you assign a constant string is when defining and initializing a char array as in

char name[20] = { "John Doe" };

not even with

char name[20];
name = { "John Doe" };

In the case of char *name; name is a character pointer, not an array. When you did

p.name = "Jane";

it points to another string object.

person p = { .surname = "Doe", .name = "Johnny", .age = 30 };
printf("Ptr. value:\tp.name: 0x%p;\tp.surname: 0x%p\n", p.name, p.surname);
p.name = "Spy, watch out!";
printf("Ptr. value:\tp.name: 0x%p;\tp.surname: 0x%p\n", p.name, p.surname);

output:

Ptr. value:     p.name: 0x00007FF726F7B16C;     p.surname: 0x00007FF726F7B174
Ptr. value:     p.name: 0x00007FF726F7ACE8;     p.surname: 0x00007FF726F7B174

However, in the character array [] case, after you do

strcpy(p.name, "Jane");

to change its content, the address of the buffer p.name[] never changes.

An interesting parallel between C and Python is that
Python's String is immutable and is similar to C's string pointer where
string literals are read-only.
Python's List is mutable and is similar to C's character array.

>>> name = "John"
>>> print(hex(id(name)))
0x261654235f0
>>> name = "Jane"
>>> print(hex(id(name)))
0x261654237b0
>>> type(name)
<class 'str'>
>>> name[1] = 'o'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> name = list(name)
>>> type(name)
<class 'list'>
>>> name
['J', 'a', 'n', 'e']
>>> name[1] = 'o'
>>> name
['J', 'o', 'n', 'e']
>>> name = ''.join(name)
>>> name
'Jone'
>>> type(name)
<class 'str'>
>>>
他是夢罘是命 2024-09-14 10:44:15

在这两种情况下,您都在编写:

p.age = 25;
p.name = "Jane";
  • 在第一种情况下,p.name 是一个数组,在第二种情况下,不可能在 C 中分配数组
  • p.name > 是一个 char*,它们可以分配给字符串文字,因为字符串文字是 char 的数组(数组可以转换为指针)

您可以使用诸如 <代码>strcpy、memcpy等。如其他响应中所示,但您也可以通过分配整个 struct 来规避此问题。

// compound literal, C99 feature
p = (person) {.age = 25, .name = "Jane", .surname = p.surname};

实际上,将字符串作为 char*size_t 捆绑在一个 struct 中非常有用,因此通常可以对单个字符串执行此操作,如下所示出色地。

In both cases, you are writing:

p.age = 25;
p.name = "Jane";
  • in the first case, p.name is an array, and it's not possible to assign arrays in C
  • in the second case, p.name is a char*, and those can be assigned to string literals, because string literals are arrays of char (arrays are convertible to pointers)

You can use functions such as strcpy, memcpy et al. as shown in other responses, but you can also circumvent this issue by assigning the whole struct.

// compound literal, C99 feature
p = (person) {.age = 25, .name = "Jane", .surname = p.surname};

In practice, it is useful to bundle strings as a char* and size_t in one struct, so this can often be done for individual strings as well.

小鸟爱天空丶 2024-09-14 10:44:15

下面是如何实现安全字符串赋值的示例。如果字符串比目标数组长,则断言失败并且程序退出。

#include <assert.h>
#include <stdio.h>
#include <string.h>

#define LEN(arr) (sizeof (arr) / sizeof (arr)[0])
#define APAR(arr) (arr), LEN(arr)

void Assign(char target[], int targetLen, const char source[], int sourceLen)
{   
    size_t srcStrLen;
    
    srcStrLen = strnlen(source, sourceLen);
    assert(targetLen > srcStrLen);
    memcpy(target, source, srcStrLen);
    target[srcStrLen] = '\0';
}

int main(void)
{
    typedef struct {
        char name[20];
        char surname[20];
        int unsigned age;
    } person;
    
    person p;
    
    Assign(APAR(p.name), APAR("Jane"));
    Assign(APAR(p.surname), APAR("Anderson"));
    p.age = 25;
    printf("Name: %s %s; Age: %d\n", p.name, p.surname, p.age);
    return 0;
}

Here is an example of how to implement safe string assignment. If a string is longer than the target array, an assertion fails and the program quits.

#include <assert.h>
#include <stdio.h>
#include <string.h>

#define LEN(arr) (sizeof (arr) / sizeof (arr)[0])
#define APAR(arr) (arr), LEN(arr)

void Assign(char target[], int targetLen, const char source[], int sourceLen)
{   
    size_t srcStrLen;
    
    srcStrLen = strnlen(source, sourceLen);
    assert(targetLen > srcStrLen);
    memcpy(target, source, srcStrLen);
    target[srcStrLen] = '\0';
}

int main(void)
{
    typedef struct {
        char name[20];
        char surname[20];
        int unsigned age;
    } person;
    
    person p;
    
    Assign(APAR(p.name), APAR("Jane"));
    Assign(APAR(p.surname), APAR("Anderson"));
    p.age = 25;
    printf("Name: %s %s; Age: %d\n", p.name, p.surname, p.age);
    return 0;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文