Манипулирование структурами с функцией void в C - PullRequest
0 голосов
/ 19 ноября 2018

, поэтому мне поставили задачу создать структуру из искусственной строки и реализовать все обычные строковые функции в моей структуре из искусственной строки.Я застрял в тестах моей реализации strcat, названной append, с первым провалом теста (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);
}

Ответы [ 2 ]

0 голосов
/ 19 ноября 2018

Оч, у вас есть ошибки:

  1. Внутри text_new вы выделяете память для text *out, используя text *out = malloc(sizeGot);, когда sizeGot = 24 является постоянным значением. Вы должны выделить для него sizeof(*out) или sizeof(text) байт памяти.
  2. Я не знаю, для чего int sizeGot = 24; while (sizeNeeded > sizeGot) цикл внутри text_new и append. Я предполагаю, что намерение состоит в том, чтобы делать выделения в степени 24. Кроме того, в большинстве случаев похоже, что в обеих функциях используется один и тот же код, он выглядит как дублирование кода, что является плохой вещью.
  3. Внутри append Вы передаете указатель на t1, а не двойной указатель, поэтому, если вы измените сам указатель t1, модификация не будет видна за пределами области действия функции. t1 = newText(stringy); просто бессмысленно и пропускает память. Вы могли бы void append(text **t1, text *t2), а затем *t1 = newText(stringy). Но вы можете использовать более лучший подход, используя realloc - я ожидаю, что append «добавляет» строку, а не создает новый объект. Поэтому сначала измените размер буфера, используя 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_t и sizeof также возвращает 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;
}
0 голосов
/ 19 ноября 2018

Вы неправильно распределяете память.Вы можете исправить это с помощью гибкого элемента массива , например, так:

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

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

И, очевидно, такой код не имеет смысла:

  return out;
  free(c);
}

Включить предупреждения компилятора ислушай их.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...