我提出并用作解决方案的方法基本上是将 HDD 的较小结构(在本例中为文件)加载到新的较大结构的数组中,然后重新排列内存块,以便可以正确访问每个字段。说明这一点的代码如下,是mcve。
#include <stdio.h>
#include <string.h>
typedef struct INNER_STRUCT_ {
int i_item1;
int i_item2;
char i_item3;
} INNER_STRUCT;
typedef struct SMALL_STRUCT_ {
int item1;
char item2;
INNER_STRUCT item3;
} SMALL_STRUCT;
typedef struct BIG_STRUCT_ {
int item1;
char item2;
INNER_STRUCT item3;
INNER_STRUCT item4;
/*
Note that the big struct is exactly the same as the small
struct with one extra field - Key to this method working
is the fact that the extension to the struct is appended
at the end, in an array of the structs will be placed one
after the other in memory with no gaps*/
} BIG_STRUCT;
void printSmallStruct (SMALL_STRUCT *inStruct, int count) {
// Print everything inside given small struct
printf("\n\n Small struct %d, item1: %d \n",count,inStruct->item1);
printf(" Small struct %d, item2: %c \n",count,inStruct->item2);
printf(" Small struct %d, item3.i_item1: %d \n",count,inStruct->item3.i_item1);
printf(" Small struct %d, item3.i_item2: %d \n",count,inStruct->item3.i_item2);
printf(" Small struct %d, item3.i_item3: %c \n",count,inStruct->item3.i_item3);
}
void printBigStruct (BIG_STRUCT *inStruct, int count) {
// Print everything inside given big struct
printf("\n\n Big struct %d, item1: %d \n",count,inStruct->item1);
printf(" Big struct %d, item2: %c \n",count,inStruct->item2);
printf(" Big struct %d, item3.i_item1: %d \n",count,inStruct->item3.i_item1);
printf(" Big struct %d, item3.i_item2: %d \n",count,inStruct->item3.i_item2);
printf(" Big struct %d, item3.i_item3: %c \n",count,inStruct->item3.i_item3);
printf(" Big struct %d, item4.i_item1: %d \n",count,inStruct->item4.i_item1);
printf(" Big struct %d, item4.i_item1: %d \n",count,inStruct->item4.i_item2);
printf(" Big struct %d, item4.i_item1: %c \n",count,inStruct->item4.i_item3);
}
int main() {
SMALL_STRUCT smallStructArray[5]; // The array of small structs that we will write to a file then read
BIG_STRUCT loadedBigStructArray[5]; // The large array of structs that we will read the data from the file into
int i; // Counter that we will use
FILE *pfile; // pointer to our file stream
void *secondary_ptr; // void pointer that we will use to 'chop' memory into the size we want
/* Fill the array of structs (smallStructArray) */
for (i = 0; i < 5; i++) {
/* We fill each field with different data do we can ID that the right data is in the right fields */
smallStructArray[i].item1 = 111;
smallStructArray[i].item2 = 'S';
INNER_STRUCT* temp = &smallStructArray[i].item3;
temp->i_item1 = 777;
temp->i_item2 = 999;
temp->i_item3 = 'I';
}
/* Write the contents of smallStructArray to binary file then display it */
pfile = fopen("test.dat","wb");
if (pfile!=NULL){
for (i = 0; i < 5; i++) {
fwrite(&smallStructArray[i],sizeof(SMALL_STRUCT),1,pfile);
}
fclose(pfile);
}
else{
printf("Unable to open file!");
return 1;
}
for (i = 0; i < 5; i++) {
printSmallStruct(&smallStructArray[i],i);
}
/* Clear array of big structs using memset */
memset(&loadedBigStructArray[0],0,sizeof(loadedBigStructArray));
/* Here we read from the smallStructArray that was aved to file into the loadedBigStructArray */
pfile = fopen("test.dat","rb");
if (pfile !=NULL){
/*
He we pass fread the following: size_t fread(void *args1, size_t args2, size_t args3, FILE *args4)
args1 - a pointer to the beginning of a block of memory, in our case the beginning of the
array loadedBigStructArray.
args2 - the size of the ammout of bytes we wish to read, in our case the size of a SMALL_STRUCT,
the size one of the elments in the array saved to the file.
args3 - the ammount of elements to read, in our case five (which is the number of elements the
array saved to the file has.
args4 - a pointer to a FILE that specifies our input stream.
Essentially what fread will do here is read a block of bytes the size of the array we saved to
the file (smallStructArray) into the array in memory loadedBigStructArray from the
beggining of loadedBigStructArray. Fig 1 illustrates what this will look like in memory.
*/
fread(&loadedBigStructArray,sizeof(SMALL_STRUCT),5,pfile);
fclose(pfile);
}
else{
printf("Unable to open file!");
return 1;
}
/*
Due to the way the array on the file has been read into the array in memory, if we try
to access the data in loadedBigStructArray only the first 5 values will be valid, due to
the memory not being in the order we want. We need to re-arrange the data in loadedBigStructArray
*/
/*
Here we use a void pointer to point to the beggining of the loadedBigStructArray.
we will use this pointer to 'chop' the data loadedBigStructArray into SMALL_STRUCT
sized 'chunks' we can read from.
Due to the way pointers and arrays work in C we can cast the void pointer to any type we want
and get a chunk of memory that size begginnig from the pointer and its off set.
E.g. : int temp = ((int *)void_ptr)[i];
This example above will give us an integer 'temp' that was taken from memory beggining from position
void_ptr in memory and its offset i. ((int *)void_ptr) casts the pointer to type int and [i] dereferances
the pointer to location i.
*/
secondary_ptr = &loadedBigStructArray;
/*
Not we are going through the array backwards so that we can rearange the data with out overwriting
data in a location that has data which we havent moved yet. As the bottom end of the loadedBigStructArray
is essentially empty we can shift data down that way.
*/
for (i = 5; i > -1; i=i-1) {
SMALL_STRUCT temp = ((SMALL_STRUCT *)secondary_ptr)[i]; // dereference pointer to SMALL_STRUCT [i] inside loadedBigStructArray call it 'temp'
/*
Now that we have dereferenced a pointer a given SMALL_STRUCT inside loadedBigStructArray called 'temp'
we can use temp to move the data inside temp to its corresponding position in loadedBigStructArray
which rearragnes the data.
*/
loadedBigStructArray[i].item1 = temp.item1;
loadedBigStructArray[i].item2 = temp.item2;
loadedBigStructArray[i].item3.i_item1 = temp.item3.i_item1;
loadedBigStructArray[i].item3.i_item2 = temp.item3.i_item2;
loadedBigStructArray[i].item3.i_item3 = temp.item3.i_item3;
/* We then fill the new field to be blank */
loadedBigStructArray[i].item4.i_item1 = 0;
loadedBigStructArray[i].item4.i_item2 = 0;
loadedBigStructArray[i].item4.i_item3 = '0';
}
/* Print our new structures */
for (i = 0; i < 5; i++) {
printBigStruct(&loadedBigStructArray[i],i);
}
return 0;
}
技术可视化:
当 fread 对保存在磁盘上的数组进行单次读取操作时,由于它较小,它将占用内存中数组的第一部分,但“底部”部分可以是任何东西,如果我们尝试使用我们对数据的当前句柄访问新数组中的数据,我们将获得不准确的信息或内存损坏。我们必须重新排列这些数据,然后才能对数组中的结构使用任何句柄。