临时为算术运算void *的正确方法是什么?

时间:2018-05-23 13:46:36

标签: c valgrind memcpy void-pointers pointer-arithmetic

我是C新手,但多年来一直是程序员,所以我试图通过遵循斯坦福大学的课程从2008年学习C并在C​​语言中对Vectors进行Assignment 3

它基本上只是一个通用数组,因此数据作为void *保存在结构中。编译器标志-Wpointer-arith已打开,因此我无法arithmetic(我理解原因)。

数据周围的结构必须不知道数据的类型,因此它对调用者来说是通用的。

为简化起见,我正在尝试以下代码:

#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 incorrectly

3 个答案:

答案 0 :(得分:10)

这是因为您向向量添加了8个元素,然后在尝试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函数来提取数据。

答案 1 :(得分:4)

你的代码有点令人困惑,可能是误解或两个隐藏在那里。

一些观察结果:

  1. 您无法更改 malloc()返回的指针,然后将新值传递给free()。传递给free()的每个值必须是其中一个分配函数返回的完全相同的值。
  2. 正如您所猜测的那样,复制最好由memcpy()完成,您必须强制转换为char *进行算术。
  3. 附加值的功能可以是:

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

    当然,这并不会处理向量溢出,因为长度没有存储(我不想假设它在10处被硬编码)。

    为每个对象更改data中的vector值非常奇怪,让事情更加混乱。只需在需要访问元素时添加所需的偏移量,这是非常便宜且非常直接的。

答案 2 :(得分:4)

如果您事先不知道数组的数据类型,则在首次初始化时必须假定一定量的内存,例如32字节或100字节。然后,如果内存不足,可以使用realloc进行扩展,并将以前的数据复制到新插槽。 C ++向量IIRC遵循x2或x2.2比率来重新分配,不确定。

接下来是你的free。你必须知道这里有一件大事。如果用户要向您发送自己的内存分配对象,该怎么办?例如,他们之前分配的char*?如果您只是删除向量的数据成员,那就不够了。如果数据类型需要特别注意作为要添加的输入,则需要请求函数指针。

最后,你在这一行上犯了一个大错:

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

你正在修改你的指针地址!你的初始地址在这里丢失了!你绝不能这样做。使用临时char*来保存您的初始数据地址并改为操作它。