Как правильно временно разыграть void * для арифметики? - PullRequest
0 голосов
/ 23 мая 2018

Я новичок в C, но несколько лет был программистом, поэтому я пытаюсь выучить C, следуя курсам Стэнфорда с 2008 года и выполняя Задание 3 по Векторам в C.

В основном это просто общий массив, поэтому данные хранятся внутри структуры как void *.Флаг компилятора -Wpointer-arith включен, поэтому я не могу сделать арифметику (и я понимаю причины этого).

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

Чтобы упростить задачу, я пробую следующий код:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    void *data;
    int aindex;
    int elemSize;
} trial;

void init(trial *vector, int elemSize)
{
    vector->aindex = 0;
    vector->elemSize = elemSize;
    vector->data = malloc(10 * elemSize);
}

void add(trial *vector, const void *elemAddr)
{
    if (vector->aindex != 0)
        vector->data = (char *)vector->data + vector->elemSize;

    vector->aindex++;
    memcpy(vector->data, elemAddr, sizeof(int));

}

int main()
{
    trial vector;
    init(&vector, sizeof(int));

    for (int i = 0; i < 8; i++)
        {add(&vector, &i);}

    vector.data = (char *)vector.data - ( 5 * vector.elemSize);
    printf("%d\n", *(int *)vector.data);
    printf("%s\n", "done..");

    free(vector.data);
    return 0;
}

Однако я получаю ошибку бесплатно сfree(): invalid pointer.Поэтому я запустил valgrind на нем и получил следующее:

==21006==  Address 0x51f0048 is 8 bytes inside a block of size 40 alloc'd
==21006==    at 0x4C2CEDF: malloc (vg_replace_malloc.c:299)
==21006==    by 0x1087AA: init (pointer_arithm.c:13)
==21006==    by 0x108826: main (pointer_arithm.c:29)

На данный момент я предполагаю, что я либо неправильно выполняю char*, либо, возможно, неправильно использую memcpy

Ответы [ 3 ]

0 голосов
/ 23 мая 2018

Если вы заранее не знаете тип данных массива, при первой его инициализации вы должны использовать определенный объем памяти, например, 32 байта или 100 байтов.Затем, если вам не хватает памяти, вы можете расширить с помощью realloc и скопировать ваши предыдущие данные в новый слот.Вектор IIRC C ++ соответствует соотношению x2 или x2.2 к перераспределению, не уверен.

Далее ваш free.Есть большая вещь, которую вы должны знать здесь.Что если пользователь отправит вам выделенный памяти объект самостоятельно?Например char*, что они выделяли ранее?Если вы просто удалите элемент данных вашего вектора, этого будет недостаточно.Вам нужно запросить указатель на функцию, если тип данных требует особого внимания при добавлении ввода.

Наконец, вы делаете здесь большую ошибку:

if (vector->aindex != 0)
    vector->data = (char *)vector->data + vector->elemSize;

Вы модифицируете свой адрес указателя !!!Ваш начальный адрес здесь потерян!Вы никогда не должны делать это.Используйте временный char* для хранения вашего начального адреса данных и манипулирования им.

0 голосов
/ 23 мая 2018

Это происходит потому, что вы добавляете восемь элементов к вектору, а затем «откатываете» указатель всего на пять шагов, прежде чем пытаться выполнить free.Вы можете легко исправить это, используя vector->aindex, чтобы решить, на сколько нужно развернуть индекс.

Однако основная причина проблемы заключается в том, что вы изменили vector->data.Во-первых, вы должны избегать его изменения, полагаясь на временный указатель внутри вашей функции add:

void add(trial *vector, const void *elemAddr, size_t sz) {
    char *base = vector->data;
    memcpy(base + vector->aindex*sz, elemAddr, sz);
    vector->aindex++;
}

Обратите внимание на использование sz, вам нужно передать ему sizeof(int).

Другая проблема в вашем коде - это когда вы печатаете, переводя vector.data в int*.Возможно, это сработает, но лучшим подходом было бы написать аналогичную функцию read для извлечения данных.

0 голосов
/ 23 мая 2018

Ваш код несколько сбивает с толку, возможно, там есть неправильное понимание или два.

Несколько замечаний:

  1. Вы не можете изменить указатель, возвращаемый malloc(), а затем передать новое значение в free().Каждое значение, переданное в free(), должно быть точно таким же значением, которое возвращает одна из функций распределения.
  2. Как вы уже догадались, лучше всего скопировать memcpy(), и вам нужно привести к char * для арифметики.

Функция добавления значения может быть:

void add(trial *vector, const void *element)
{
  memcpy((char *) vector->data + vector->aindex * vector->elemSize, element);
  ++vector->aindex;
}

Конечно, это не обрабатывает переполнение вектора, так как длина не сохраняется (Я не хотел предполагать, что это было жестко запрограммировано в 10).

Изменение значения data в vector для каждого объекта очень странно и делает вещи более запутанными.Просто добавьте необходимое смещение, когда вам нужно получить доступ к элементу, это очень дешево и очень просто.

...