首页 > 解决方案 > 在 C 中使用 void 函数操作结构

问题描述

所以我的任务是创建一个仿字符串结构并在我的仿字符串结构上实现所有常用的字符串函数。我被困在名为 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);
}

标签: cmemorymemory-managementstruct

解决方案


哦,你有一些错误:

  1. 在内部text_new分配内存以text *out使用text *out = malloc(sizeGot);whensizeGot = 24是一个常量值。您应该为它分配sizeof(*out)sizeof(text)字节的内存。
  2. 我不知道里面的int sizeGot = 24; while (sizeNeeded > sizeGot)循环是干什么用的。我猜目的是按照 24 的幂进行分配。而且看起来两个函数中的代码大多相同,看起来确实像代码重复,这是一件坏事。text_newappend
  3. 在里面append你传递一个指针t1,而不是一个双指针,所以如果你修改 t1 指针本身,修改将在函数范围之外不可见。t1 = newText(stringy);只是毫无意义并泄漏内存。你可以void append(text **t1, text *t2)然后*t1 = newText(stringy)。但是您可以使用更好的方法使用realloc- 我希望 append 来“附加”字符串,而不是创建新对象。realloc所以首先使用then调整缓冲区的大小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)let it be这样的函数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;
}

推荐阅读