将var赋值给var - 它会起作用吗?

时间:2011-12-26 06:38:48

标签: c string wrapper

我正在尝试为我正在制作的包装器做一些有用的事情。我正在为C编写标准字符串库的包装器,因为我不喜欢它。

我现在不想将C字符串引入我的包装器。我有一个名为`str的结构,它包含字符串的长度和缓冲区:

struct str
{
    char *buf;
    size_t len;
};

在我的标题文件中,我有这个:

typedef struct str str;

现在我已经实现了封装(我认为)。

所以我声明这样的字符串:

str *a_string = NULL;

我已经完成了我想做的所有事情,但我有一个问题。我想为一个函数添加这样的东西:

str.h:

extern str *str_str(str *, char *);

和str.c

str *str_str(str *buf, char *ns)
{
    buf->len = strlen(ns);
    buf->buf = (char *) malloc(buf->len + 1);
    strncpy(buf->buf, ns, buf->len);
    return buf;
}

并且测试效果很好:

str *s = str_new();
printf("%s\n", str_cstr(str_str(s, "hello")));

output: hello

但这会有效吗?

str_assign(s, str_str(s, "ok"));

我认为这基本上是将s分配给s。当我打印时,它什么都不打印!

我没有收到任何错误! 非常感谢所有帮助。我是C语言的新手。

str_assign()的来源:

void str_assign(str *s1, str *s2)
{
    s1->len = s2->len;
    s1->buf = (char *) malloc(s2->len + 1);
    strncpy(s1->buf, s2->buf, s2->len);
}

3 个答案:

答案 0 :(得分:2)

我知道这是C,但将它与C ++进行比较是有用的。在C ++中编写赋值运算符时,始终必须考虑自我赋值的情况。当您在C中执行相同的工作时也是如此。

因此,我认为你需要:

void str_assign(str *s1, const str *s2)
{
    if (s1 != s2)
    {
        free(s1->buf);  // Added!
        s1->len = s2->len;
        if ((s1->buf = (char *) malloc(s2->len + 1)) != 0)
            memcpy(s1->buf, s2->buf, s2->len + 1);
        else
            s1->len = 0;  // Is this sufficiently safe with a null buf?
    }
}

你可以使用memcpy()因为(a)字符串保证不相交,(b)你知道字符串有多长,所以你不需要检查每个字符串的结尾与strncpy()strcpy()一样的步骤。

实际上,有一种情况表明您永远不需要使用strcpy()strncpy()strcat()strncat();你应该总是知道源和目标字符串有多长(否则你不能确定不会有缓冲区溢出),所以你总是可以使用memmove()memcpy()代替。 / p>


我还注意到你将不得不担心内存泄漏。我刚刚修改了上面的赋值以释放旧字符串,然后用新的。

覆盖它

您还可以通过仅在新字符串长于旧字符串时分配新空间来优化操作。您也可以考虑使用realloc()代替malloc()。但是,您必须小心内存泄漏陷阱。这是使用realloc()的错误方法:

s1->buf = realloc(s1->buf, s1->len + 1);

如果realloc()失败,你只是用空值覆盖了你的观点 - 失去了你对旧空间的唯一引用。您应该始终将realloc()的结果存储在与第一个参数不同的变量中:

char *new_buf = realloc(s1->buf, s1->len + 1);
if (new_buf == 0)
    ...handle out of memory condition...

您最终可能决定在结构中保留两个长度 - 分配的空间和使用的空间。然后,您可以更有效地重用空间,仅在新字符串长于先前分配的空间时分配更多空间,但仍然允许您随时缩短字符串。

答案 1 :(得分:1)

您需要在str_str函数中使用return语句,因此请添加return buf;

如果您稍微考虑一下,您会发现str_assign函数中存在错误,因为更改s1->buf指针后您实际上正在更改s2->buf因为它们指向到相同的结构。这是一个应该更适合您的实现:

str * str_assign(str * dest, const str * src)
{
    char * old_buffer = dest->buf;
    if (src->buf)
    {
       char * buf = malloc(src->len+1);
       strncpy(buf, src->buf, src->len);
       dest->buf = buf;
       dest->len = src->len;
    }
    else
    {
       dest->buf = 0;
    }

    if (old_buffer)
    {
      free(old_buffer);
    }
    return dest;
}

答案 2 :(得分:0)

我在以下代码中看到了memory leak

void str_assign(str *s1, str *s2)
{
    s1->len = s2->len;
    s1->buf = (char *) malloc(s2->len + 1);
    strncpy(s1->buf, s2->buf, s2->len);
}

如果s1s2指向相同的内存位置,那么您将使用malloc分配新内存并将其分配给已指向有效内存位置的指针并包含有效的struct str数据! ..这可以通过执行以下

进行简单检查来避免
void str_assign(str *s1, str *s2)
{
    if (s1 == s2)
        return;

    /* 
    if s1 is pointing to any valid memory 
    then free it before making it point to 
    the memory that was allocated by malloc.
    */
    if (s1 != NULL)
        free(s1);

    if ((s1->buf = (char *) malloc(s2->len + 1)) == NULL) {
        printf("ERROR: unable to allocate memory\n");
        return;
    }

    s1->len = s2->len;
    strncpy(s1->buf, s2->buf, s2->len);
}