我正在学习数据结构和算法考试。与动态内存分配相关的示例问题之一要求您创建一个传递字符串的函数,该函数将字符串复制到用户定义的char指针。问题提供了开始的结构体。 我做了这样的事情:
typedef struct smart_string {
char *word;
int length;
} smart_string;
smart_string* create_smart_string(char *str)
{
smart_string *s = (smart_string*)malloc(sizeof(smart_string));
s->length = strlen(str);
s->word = malloc(s->length);
strcpy(s->word, str);
return s;
}
但是答案是这样
typedef struct smart_string {
char *word;
int length;
} smart_string;
smart_string *create_smart_string(char *str)
{
smart_string *s = malloc(sizeof(smart_string));
s->length = strlen(str);
s->word = malloc(sizeof(char) * (s->length + 1));
strcpy(s->word, str);
return s;
}
我继续进行code:blocks并对其进行了测试,以查看是否有主要区别。据我所知,它们的输出是相同的。
我这样做是因为我想如果我们要为s->word
分配一个特定的内存块,那么它应该与s ->length
相同的字节数,因为那是我们要复制的字符串。
但是,下面的正确答案将sizeof(char)
(仅1个字节)乘以s->length + 1
。为什么需要将1
添加到s->length
?将s->length
乘以sizeof(char)
的重要性是什么?我在寻找答案时犯了什么错误?
答案 0 :(得分:4)
您的答案不正确,因为它不考虑终止的'\0'
字符。在C中,字符串以0
结尾。这样便可以确定其长度。 strlen()
的典型实现如下
size_t strlen(char const *str)
{
for (char const *p = str; *p; ++p); // as long as p doesn't point to 0 increment p
return p - str; // the length of the string is determined by the distance of
} // the '\0'-character to the beginning of the string.
但是,这两个“解决方案”都是愚蠢的。为什么要在自由存储区(“堆”)上分配一个由int
和指针组成的结构! smart_string::length
是int
是另一个wtf。
#include <stddef.h> // size_t
typedef struct smart_string_tag { // *)
char *word;
size_t length;
} smart_string_t;
#include <assert.h> // assert()
#include <string.h> // strlen(), strcpy()
#include <stdlib.h> // malloc()
smart_string_t create_smart_string(char const *str)
{
assert(str); // make sure str isn't NULL
smart_string_t new_smart_string;
new_smart_string.length = strlen(str);
new_smart_string.word = calloc(new_smart_string.length + 1, sizeof *new_smart_string.word);
if(!new_smart_string.word) {
new_smart_string.length = 0;
return new_smart_string;
}
strcpy(new_smart_string.word, str);
return new_smart_string;
}
答案 1 :(得分:3)
sizeof(char) == 1
的定义,所以没关系。
您不应该强制转换malloc的结果:Do I cast the result of malloc?
您唯一真正的区别是strlen
返回字符串的长度,不包括终止NUL('\0'
)字符,因此您需要在{解决方案中的缓冲区。
如果您在此处复制字符串,终止字符将不会被复制(或更糟糕的是,它将被复制到其他内存中),因此,任何处理字符串的函数(除非您使用诸如+ 1
)将遍历缓冲区并越过缓冲区,因为他们找不到终点。到那时,这是不确定的行为,一切都会发生,甚至可以按预期工作,但不能依靠它。
之所以能够正常工作,是因为缓冲区旁边的内存可能为0,因此被解释为终止字符。