0

我有一个函数可以将二进制文件作为 void * 类型读入内存。文件头中的信息指示所需的内存量和实际数据类型(以每个数字的字节数为单位 - 例如,如果应该将其解释为“长”,则为 8。

我的问题是,main 不知道所需的数据类型或内存。所以我这样调用函数:

long myfread(char *infile, void **tempdata,*datasize) 

char *infile="data.bin"; // name of the input file
void *tempdata=NULL; // where the data will be stored, initially 
long n; // total numbers read, returned by the function 
size_t datasize; // modified appropriately by the function 

n = myfread(infile,&tempdata,&datasize);

到目前为止一切顺利 - main 可以读取“tempdata”中的字节 - 但不能读取(比如说)整数或浮点数。我的问题是,有没有一种简单的方法可以重铸 tempdata 来实现这一点?

4

6 回答 6

1

我认为您不是在谈论数组,而是在谈论一块内存。

一个指针,不管它是void *,char *还是int *; 当它指向内存地址(可能是虚拟的,主要在堆上)时,区别仅在于它的解释方式。

假设你有 16 个字节的内存块,byte[]你有 16 个,对于int[](每 32 位)你有 4 个,依此类推。当您对其应用索引时,字节偏移量的增量取决于数据类型的大小。

最重要的是,内存块对您的数据类型的完整性。也就是说,您不应该访问超过内存块大小的位置。假设您有 10 个字节的内存并且您的指针是int *a,那么访问 ofa[1]只是访问冲突。

我可以将整个数组从 *void 重新转换为 *int 吗?

我相信没有a这样的东西void array。对于指针类型的转换,您可以在 C 中自由地进行。

于 2013-06-07T23:16:06.427 回答
1

好的,myfread看起来像这样:

long myfread(char *infile, void **data, size_t *datasize)
{
   FILE *f = fopen(infile, "rb");   // Or some such.  
   ... 

   *datasize = ... // some calculation of some sort, e.g. seek to end of file?

   *data = malloc(*datasize ... );   // Maybe more calculation? 

   res = fread(f, data, datasize); 

   fclose(f);

   return res;
}

然后稍后,您想将更新的内容转换*dataint *?

int *my_int_array; 

n = myfread(infile,&tempdata,&datasize);

my_int_array = tempdata;   // If a C++ compiler, you need a cast to (int *)

for(int i = 0; i < datasize; i++)
{
   printf("%d\n", my_int_array[i]); 
}

当然,如果myfredad不做我认为的事情,那么所有的赌注都没有了。

于 2013-06-07T23:21:16.313 回答
1

根据您编辑的问题,我可以猜测是什么myfread样子。大大简化,它做了这样的事情:

long myfread(const char *path, void **pmem, size_t *datasize) {
    long magically_found = 42;
    int *mem;
    int i;

    mem = malloc(magically_found * sizeof(int)); /* and we assume it works */
    *datasize = 12345;
    for (i = 0; i < magically_found; i++)
        mem[i] = i;
    *pmem = mem;
    return magically_found;
}

现在,在您的 中main,您必须以某种方式知道,如果datasize == 12345返回时,分配的内存已被 s 填充int。知道这一点后,您只需编写:

    int *ip;
    ... /* your code from above, more or less */
    if (datasize != 12345) {
        panic("memory was not filled with ints");
        /* NOTREACHED */
    }
    ip = tempdata;

从这里开始,您可以访问ip[i]任何有效i的 , (至少 0 且小于n)。

更棘手的问题是,你怎么知道 12345 的含义int,如果不是12345,你会怎么做?而且,可能 12345 并不意味着int无论如何。也许 4 意味着int or float两者恰好都有一个sizeof4,在这种情况下, havedatasize == 4并不能告诉你它到底是哪一个!那么,然后呢?

总而言之,听起来这个问题至少没有具体说明。

于 2013-06-07T23:24:10.943 回答
0

我很难理解你想要什么,我想你可能也是。看起来你有一个类似于readfread接受类型参数的函数void *来存储它读取的数据。这并不意味着您将类型的变量void *传递给它。相反,您传递要存储数据的对象的地址。

在您的情况下,只需创建一个int适当大小的数组并将该数组的地址(或其第一个元素的地址)传递给进行读取的函数。例如(假设fread):

int my_array[100];
fread(my_array, sizeof my_array, 1, f);

如果您事先不知道大小,或者如果它需要超过调用函数的返回,您可以使用malloc.

于 2013-06-07T22:30:37.017 回答
0
for(i = 0; i < index_max; i++) {
    printf("%d\n", ((int*)tempdata)[i]);
}
于 2013-06-08T00:03:31.557 回答
0

是的,您可以将指针强制转换为另一种类型,但如果这样做,很难避免未定义的行为。例如,您必须确保您正在转换的二进制数据正确对齐,并且写入数据的代码中的内存表示与读取它的代码的内存表示相同。这不仅仅是一个学术问题,因为您可能会发现架构之间的字节序差异,例如,双打必须在 ARM 机器上仔细对齐。

您可以通过使用 memcpy 编写访问内存的函数来解决对齐问题,就像它是一个类型化数组一样。例如,

int get_int(const char *array, int idx) {
    int result;
    memcpy(&result, array + idx * sizeof(int), sizeof(int));
    return result;
}

为避免将其写出 N 次,您可以对其进行宏化。

#define MAKE_GET(T) T get_##T (const char *array, int idx) { \
    T result; \
    memcpy(&result, array + idx * sizeof(T), sizeof(T)); \
    return result; \
}

MAKE_GET(int)
MAKE_GET(float)
MAKE_GET(double)

要解决字节序问题,或者更一般地说,内存表示在机器之间可能不同的问题,您需要为二进制文件定义明确的格式(例如,始终写入 ints little-endian)。一种好的方法是使用文本(如果您需要它,可以使用 zlib 或类似的压缩)。另一种是使用序列化库(例如,Google 的协议缓冲区)。或者你可以自己动手——这并不难。

于 2013-06-08T08:35:35.437 回答