【问题标题】:Manipulating structs with a void function in C在 C 中使用 void 函数操作结构
【发布时间】:2018-11-19 09:18:51
【问题描述】:

所以我的任务是创建一个仿字符串结构并在我的仿字符串结构上实现所有常用的字符串函数。我被困在名为 append 的 strcat 实现的测试中,第一个测试失败(segfault)是第 5 行。我创建新结构的函数应该没问题,因为它通过了所有测试,但我只是将其包含在内。

我已经能够成功地为我的人造字符串结构实现长度、获取、设置和复制功能。

结构:

struct text {
    int capacity;
    char *content;
};

typedef struct text text;

我创建新结构的函数:

text *newText(char *s) {
    printf("new Text from %s\n", s);
    int sizeNeeded = (strlen(s)+1);
    int sizeGot = 24;
    while (sizeNeeded > sizeGot) {
        sizeGot = sizeGot * 2;
      }
    text *out = malloc(sizeGot);
    char *c = malloc(sizeGot);
    strcpy(c, s);
    out->content = c;
    out->capacity = (sizeGot);
    printf("the capacity is %d\n", sizeGot);
    return out;
    free(c);
}

我的追加功能:

void append(text *t1, text *t2) {
  printf("t1 content is %s, t2 content is %d\n", t1->content, *t2->content);
  int sizeNeeded = (t1->capacity + t2->capacity);
  int sizeGot = 24;
  while (sizeNeeded > sizeGot) {
      sizeGot = sizeGot * 2;
    }
  char *stringy = calloc(sizeGot, 32);
  stringy = strcat(t1->content, t2->content);
  free(t1);
  t1 = newText(stringy);
}

最后是测试:

void testAppend() {
    text *t = newText("car");
    text *t2 = newText("pet");
    append(t, t2);
    assert(like(t, "carpet"));
    assert(t->capacity == 24);
    text *t3 = newText("789012345678901234");
    append(t, t3);
    assert(like(t, "carpet789012345678901234"));
    assert(t->capacity == 48);
    freeText(t);
    freeText(t2);
    freeText(t3);
}

【问题讨论】:

  • 这是错误的:text *out = malloc(sizeGot);out 只需要足够大以容纳 1 个整数和 1 个字符指针,然后该指针将指向实际的字符串。
  • free(c) 后面的 return out; 将不会被调用。为什么在newText中分配2个?
  • @Qubit 我将如何 malloc 它指向的字符串的大小? char t->content->s = malloc(sizeGot);
  • 老实说,我不明白为什么newText 首先必须是动态的结构。我将购买的内部字符串,但分配该结构的目的是什么? IE。返回一个简单的struct textnot 如何工作?显然你会通过地址将它传递给其他 manip 函数。
  • @Alex 你做得很好,c 是一个指向(希望)足够大小的字符数组的指针。你的新文本只需要存储整数和指针,指针指向你的内存。并且请考虑@WhozCraig 的建议,无需将out 作为指针返回,只需按值返回即可。

标签: c memory memory-management struct


【解决方案1】:

您以错误的方式分配内存。您可以通过使用 灵活的数组成员来解决这个问题,如下所示:

typedef struct {
  int capacity;
  char content[];
} text;

text *out = malloc(sizeof(text) + sizeof(something));
strcpy(out->content, str);
...

显然这样的代码是无稽之谈:

  return out;
  free(c);
}

启用编译器警告并听取它们。

【讨论】:

    【解决方案2】:

    哦,你有一些错误:

    1. text_new 内部,当sizeGot = 24 是一个常量值时,您使用text *out = malloc(sizeGot);text *out 分配内存。您应该为它分配sizeof(*out)sizeof(text) 字节的内存。
    2. 我不知道int sizeGot = 24; while (sizeNeeded > sizeGot) 内部的循环text_newappend 是干什么用的。我猜其目的是按照 24 的幂进行分配。而且看起来两个函数中的代码大多相同,看起来确实像代码重复,这是一件坏事。
    3. append 内部传递一个指向t1 的指针,而不是双指针,所以如果你修改t1 指针本身,修改将在函数范围之外不可见。 t1 = newText(stringy); 毫无意义并且会泄漏内存。你可以void append(text **t1, text *t2) 然后*t1 = newText(stringy)。但是您可以使用realloc 使用更好的方法 - 我希望追加“追加”字符串,而不是创建新对象。因此,首先使用realloc 调整缓冲区大小,然后使用strcat(&t1->content[oldcapacity - 1], string_to_copy_into_t1)
    4. int sizeNeeded = (t1->capacity + t2->capacity); 已关闭。您以 24 的幂分配容量,这实际上与字符串长度无关。字符串和空终止符都需要有 strlen(t1->content) + strlen(t2->content) + 1 字节。

    试试这个:

    size_t text_newsize(size_t sizeNeeded)
    {
          // I think this is just `return 24 << (sizeNeeded / 24);`, but not sure
          int sizeGot = 24;
          while (sizeNeeded > sizeGot) {
              sizeGot *= 2;
          }
          return sizeGot;
    }
    
    text *newText(char *s) {
        printf("new Text from %s\n", s);
        if (s == NULL) return NULL;
        int sizeNeeded = strlen(s) + 1;
    
        int sizeGot = text_newsize(sizeNeeded);
    
        text *out = malloc(sizeof(*out));
        if (out == NULL) {
             return NULL;
        }
    
        out->content = malloc(sizeGot);
        if (out->content == NULL) {
             free(out);
             return NULL;
        }
    
        strcpy(out->content, s);
        out->capacity = sizeGot;
    
        printf("the capacity is %d\n", sizeGot);
    
        return out;
    }
    

    还有这个:

    int append(text *t1, text *t2) {
       printf("t1 content is %s, t2 content is %s\n", t1->content, t2->content);
       int sizeNeeded = strlen(t1->content) + strlen(t2->content) + 1;
    
       if (t1->capacity < sizeNeeded) {
    
           // this could a text_resize(text*, size_t) function
           int sizeGot = text_newsize(sizeNeeded);
           void *tmp = realloc(t1->content, sizeGot);
           if (tmp == NULL) return -ENOMEM;
           t1->content = tmp;
           t1->capacity = sizeGot;
    
       }
    
       strcat(t1->content, t2->content);
    
       return 0;
    }
    

    一些备注:

    1. 尝试处理库中的错误。如果你有一个类似void append(text *t1, text *t2) 的函数,让它成为int append(text *t1, text *t2),成功时返回0,*alloc 错误时返回负数。
    2. 使用size_t 类型存储所有内容的大小。它在stddef.h 中定义,应该用来表示对象的大小。 strlen 返回 size_tsizeof 也返回 size_t
    3. 我喜欢将所有内容都放在一个“命名空间”中,为此我在函数前面加上 text_ 之类的字符串。
    4. 我有一些空闲时间并决定实施您的库。下面是一个简单的text 对象存储字符串的代码,我使用24 幻数作为分配块大小。

    // text.h file
    
    #ifndef TEXT_H_
    #define TEXT_H_
    
    #include <stddef.h>
    #include <stdbool.h>
    
    struct text;
    
    typedef struct text text;
    
    text *text_new(const char content[]);
    void text_free(text *t);
    int text_resize(text *t, size_t newsize);
    int text_append(text *to, const text *from);
    int text_append_mem(text *to, const void *from, size_t from_len);
    const char *text_get(const text *t);
    int text_append_str(text *to, const char *from);
    char *text_get_nonconst(text *t);
    size_t text_getCapacity(const text *t);
    bool text_equal(const text *t1, const text *t2);
    
    #endif // TEXT_H_
    
    // text.c file
    
    //#include "text.h"
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <string.h>
    #include <assert.h>
    
    struct text {
        size_t capacity;
        char *content;
    };
    
    text *text_new(const char content[])
    {
        text * const t = malloc(sizeof(*t));
        if (t == NULL) goto MALLOC_ERR;
        const struct text zero = {
            .capacity = 0,
            .content = NULL,
        };
        *t = zero;
        if (content != NULL) {
            const int ret = text_append_str(t, content);
            if (ret) {
                goto TEXT_APPEND_ERR;
            }
        }
        return t;
    TEXT_APPEND_ERR:
        free(t);
    MALLOC_ERR:
        return NULL;
    }
    
    void text_free(text *t)
    {
        assert(t != NULL);
        free(t->content);
        free(t);
    }
    
    int text_resize(text *t, size_t newcapacity)
    {
        // printf("%s %d -> %d\n", __func__, t->capacity, newcapacity);
        // we resize in chunks
        const size_t chunksize = 24;
        // clap the capacity into multiple of 24
        newcapacity = (newcapacity + chunksize - 1) / chunksize * chunksize;
        void * const tmp = realloc(t->content, newcapacity);
        if (tmp == NULL) return -ENOMEM;
        t->content = tmp;
        t->capacity = newcapacity;
        return 0;
    }
    
    int text_append_mem(text *to, const void *from, size_t from_len)
    {
        if (to == NULL || from == NULL) return -EINVAL;
        if (from_len == 0) return 0;
        const size_t oldcapacity = to->capacity == 0 ? 0 : strlen(to->content);
        const size_t newcapacity = oldcapacity + from_len + 1;
        int ret = text_resize(to, newcapacity);
        if (ret) return ret;
        memcpy(&to->content[newcapacity - from_len - 1], from, from_len);
        to->content[newcapacity - 1] = '\0';
            return 0;
    }
    
    int text_append_str(text *to, const char *from)
    {
        if (to == NULL || from == NULL) return -EINVAL;
        return text_append_mem(to, from, strlen(from));
    }
    
    int text_append(text *to, const text *from)
    {
        if (to == NULL || from == NULL) return -EINVAL;
        if (text_getCapacity(from) == 0) return 0;
        return text_append_str(to, text_get(from));
    }
    
    const char *text_get(const text *t)
    {
        return t->content;
    }
    
    const size_t text_strlen(const text *t)
    {
        return t->capacity == 0 ? 0 : strlen(t->content);
    }
    
    size_t text_getCapacity(const text *t)
    {
        return t->capacity;
    }
    
    bool text_equal_str(const text *t, const char *str)
    {
        assert(t != NULL);
        if (str == NULL && t->capacity == 0) return true;
        const size_t strlength = strlen(str);
        const size_t t_strlen = text_strlen(t);
        if (t_strlen != strlength) return false;
        if (memcmp(text_get(t), str, strlength) != 0) return false;
        return true;
    }
    
    // main.c file
    
    #include <stdio.h>
    
    int text_testAppend(void) {
        text *t = text_new("car");
        if (t == NULL) return -1;
        text *t2 = text_new("pet");
        if (t2 == NULL) return -1;
        if (text_append(t, t2)) return -1;
        assert(text_equal_str(t, "carpet"));
        assert(text_getCapacity(t) == 24);
        text *t3 = text_new("789012345678901234");
        if (t3 == NULL) return -1;
        if (text_append(t, t3)) return -1;
        assert(text_equal_str(t, "carpet789012345678901234"));
        assert(text_getCapacity(t) == 48);
        text_free(t);
        text_free(t2);
        text_free(t3);
        return 0;
    }
    
    int main()
    {
        text *t1 = text_new("abc");
        text_append_str(t1, "def");
        printf("%s\n", text_get(t1));
        text_free(t1);
    
        printf("text_testAppend = %d\n", text_testAppend());
    
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-20
      • 2012-02-13
      • 2012-11-08
      相关资源
      最近更新 更多