我有一个函数,它将二进制文件作为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以使其成为可能?
答案 0 :(得分:1)
我认为你不是在谈论数组,而是一块内存。
指针,无论是void *
,char *
还是int *
;当它指向一个内存地址(可能是虚拟的,主要是在堆上)时,区别仅在于它的解释方式。
假设你有16字节的内存块,byte[]
你得到16,int[]
(每32位)得到4,依此类推。将索引应用于它时,字节偏移量的增量取决于数据类型的大小。
最重要的是,内存块对数据类型的完整性。也就是说,您不应该访问超出内存块大小的位置。假设你有10个字节的内存而你的指针是int *a
,那么访问a[1]
只是访问冲突。
我可以将整个数组从* void重新转换为* int吗?
我相信void array
没有这样的事情。对于指针类型的转换,您可以在C中自由地执行此操作。
答案 1 :(得分: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;
}
然后,您想将更新的*data
转换为int *
?
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
没有按照我的想法行事,那么所有赌注都会被取消。
答案 2 :(得分: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
返回时,已分配的内存已被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
这两个恰好都有sizeof
的4,在这种情况下,让datasize == 4
不会告诉你它究竟是哪一个!那么,那是什么?
总而言之,听起来这个问题至少没有说明。
答案 3 :(得分:0)
我很难理解你想要什么,我想你也可能。看起来你有一个类似于read
或fread
的函数,它接受类型void *
的参数,用于存储它读取的数据的位置。 不意味着您将void *
类型的变量传递给它。而是将地址传递给您希望存储数据的对象。
在您的情况下,只需创建一个适当大小的int
数组,并将该数组的地址(或其第一个元素的地址)传递给执行读取的函数。例如(假设fread
):
int my_array[100];
fread(my_array, sizeof my_array, 1, f);
如果您事先不知道大小,或者它需要经过调用函数的返回,您可以使用malloc
为数组分配空间。
答案 4 :(得分:0)
for(i = 0; i < index_max; i++) {
printf("%d\n", ((int*)tempdata)[i]);
}
答案 5 :(得分: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)
要解决endian问题,或者更常见的是内存表示可能因机器而异的问题,您需要为二进制文件定义明确的格式(例如,始终使用little-endian编写内容)。一个好的方法是使用文本,(如果你需要的话,用zlib或类似的方法压缩)。另一种方法是使用序列化库(例如,Google的协议缓冲区)。或者你可以自己动手 - 这不是太难。