Moving array of smaller structs into array of larger structs in C - c

Today I was working on a problem of moving an array of smaller structs directly into an array of larger structs (arrayNew) (essentially upgrading the smaller structs to store more information). The smaller structs needed to be read from a HDD in one single read operation into the array of new 'upgraded' larger structs, a function would be called to do the 'upgrading'. Also all the new fields in the structs that were read from the hard drive would be set to '0'.
Other more simple solutions that I tried were:
Creating a local array of the old structures (arrayOld), loading the structures from the HDD into it then simply looping through the empty array of the new structures (arrayNew) and manually moving each structs contents from arrayOld into arrayNew. (e.g. arrayNew[i].x = arrayOld[i].x; )
The problem with this is that in my case the arrays I was working with were very large and too large for the stack ( about 1mb for each array) causing a segmentation fault the instant the upgrading function was called.
Another viable solution was to create a dynamic array of the old structures (arrayDy) and load the old structures into arrayDy and then again manually moving each structs contents from arrayDy into arrayNew. (e.g. arrayNew[i].y = arrayDy[i].y; ) This addressed the issue of running out of stack memory.
After implementing the second solution. I decided to experiment and develop a solution that uses no dynamically allocated memory and loads the array of old structures from the HHD directly into the larger array of larger structs arrayNew in one read operation and manipulate the contents of arrayNew in memory to pad out the missing values that are there due to the array being bigger.
I will post my solution below in a scaled down version of what I implemented, using the following structs for my example:
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;
} BIG_STRUCT;

Yes, this is possible - you can use union for that. C99 standard makes a special guarantee that can be used to implement your requirement:
6.5.2.3-5: One special guarantee is made in order to simplify the use of unions: if a union contains several structures that share a common initial sequence (see below), and if the union object currently contains one of these structures, it is permitted to inspect the common initial part of any of them anywhere that a declaration of the complete type of the union is visible.
Your structA_ and structB_ do share a common initial sequence, so creating a union and accessing the structs through it would do the trick:
union {
structA a;
structB b;
} u;
memset(&u.b, 0, sizeof(structB)); // Zero out the bigger structB
loadFromHdd(&u.a); // Load structA part into the union
// At this point, u.b is valid, with its structA portion filled in
// and structB part zeroed out.
Note that you cannot do it to an array (unless, of course, you make an array of unions). Each structA needs to be loaded individually into the union, from which it could then be read as structB.

The method I propose and used as a solution basically loads the smaller structs for the HDD ( a file in this case) into the array of new larger structs and then rearranges the block of memory so that each field can be accessed properly. The code to illustrate this is below, and is an 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;
}
Visualization of technique:
When fread does the single read operation of the array saved on disk into the array in memory due to it being smaller it will take up the first potion of the array in memory but the 'bottom' section could be anything, if we try to access the data in the new array with the current handles we have on the data we will either get inaccurate information or a bad piece of memory. We have to rearrange this data before we can use any of our handles on the structs in the array.

Related

How can I write an array of struct of 20 structs despite not being entirely populated?

This is my struct:
typedef struct file {
char name[20];
int size;
int offset;
} file;
So basically I'm writing
4bytes for num of files
array of structs --> each 28 bytes
contents of files
// writing num of files
fwrite(&numFiles, sizeof(numFiles), 1, binFile);
size_t bytesW = 0;
for (int i = 0; i < MAX; i++) {
// writing structs of information
bytesW += fwrite(list, sizeof(file), 1, binFile);
}
printf("bytes written: %d\n", bytesW);
fclose(binFile);
Is it possible to write the entire array, if I only have a couple struct elements filled? I want to do this so that whenever I want to add new elements I can fseek into the end of dir with sizeof(file) * numFiles.
EDIT This is for a binary file
Yes, it is possible to write your entire array to a file even if only some of it is filled with meaningful data.
To avoid undefined behavior, I recommend that you at least initialize everything in the array before writing it to the file. You can simply run memset after allocating the memory for the array in order to initialize everything to 0, or if your array is allocated as a global variable then it will get initialized to 0 automatically.
The code you provided looks like it just writes the same element every time. Maybe you meant to use &list[i] instead of list.

Stored data is not same which I can access in file read loop

I have to read a txt with data in it. I can read it and store the data, but I don't know why, some stored data is not good after the read method.
Here is my output:
I write out these data with exactly the same code, except that the first is inside the loop and the second is outside of the loop.
I store these data in their own struct arrays. So as you can see, my problem is that I can't access my data outside that loop. What could be wrong?
Here is the full code: https://pastebin.com/wzEJqcZG
And the test data: https://pastebin.com/L7J133mz
This is inside the file read loop:
printf("%c %i - ", sorok[i].futarkod, sorok[i].datum);
for(j=0;j<sorok[i].rendelesCount;j++) {
printf("%i%c", sorok[i].rendelesek[j].db, sorok[i].rendelesek[j].fajta);
}
printf("\n");
And this is outside of the file read loop:
for(i=0;i<5;i++) {
printf("%c %i - ", sorok[i].futarkod, sorok[i].datum);
for(j=0;j<sorok[i].rendelesCount;j++) {
printf("%i%c ", sorok[i].rendelesek[j].db, sorok[i].rendelesek[j].fajta);
}
printf("\n");
}
In the output the first two columns are good, just the text after the dash is not.
test.c:65:14: warning: 'sor' may not be used as an array element due to flexible array member
[-Wflexible-array-extensions]
sor sorok[32];
^
rendeles rendelesek[]; is a flexible array member meaning since it's at the end of the struct you can, in theory, allocate as much memory for the array as you like. However this means the size of any given sor will vary.
Each element of an array in C must be of a fixed size, going from one element to another is simply start-of-array-memory + (i * sizeof(element)). Since sor can be of different sizes it can't be put into an array.
You could use an array of pointers to sor, or you can change sor to contain a pointer to rendeles **rendelesek;. Or both, getting used to working with pointers is good.
The real problem is sor.rendelesek is never allocated. Whichever you choose, you still have to allocate memory to sor.rendelesek else you're writing into someone else's memory. As a flexible array member, you have to use a pointer array and allocate sufficient memory as part of sor.
typedef struct {
char futarkod;
int datum;
int rendelesCount;
rendeles rendelesek[];
} sor;
sor *sorok[32];
for( size_t i = 0; i < 32; i++) {
sorok[i] = malloc(sizeof(sor) + (sizeof(rendeles) * 32));
}
Or you can use a rendelesek ** instead and allocate that directly. Combining both is probably the best option.
typedef struct {
char futarkod;
int datum;
int rendelesCount;
rendeles *rendelesek;
} sor;
sor *new_sor(const size_t num_rendeles) {
sor *new = malloc(sizeof(sor));
new->rendelesek = malloc(sizeof(rendeles) * num_rendeles);
return new;
}
int main()
{
sor *sorok[32];
for( size_t i = 0; i < 32; i++) {
sorok[i] = new_sor(32);
}
Reading inputs into statically allocated structures like this is risky and wasteful because you have to allocate what you think is the most possible elements. It's very easy to allocate way too much or not enough. Instead they should be dynamically allocated as needed, but that's another thing.

C unknown number of structures in file

Similar with this. But what if MAX_BOOKS would be unknown as well?
I want to get number of structures from a file.
My structure:
typedef struct material {
int mat_cislo;
char oznaceni[MAX_TEXT];
char mat_dodavatel[MAX_TEXT];
char dodavatel[MAX_TEXT];
float cena;
int mat_kusovnik;
} MATERIAL;
My code:
void nacist_material() {
FILE* pSoubor;
MATERIAL materialy_pocitadlo;
int i;
int b;
if((pSoubor = fopen(SOUBOR_MATERIAL, "rb")) == NULL ) {
printf("\nChyba při čtení souboru");
return;
}
pocet_zaznamu_materialu = 3;
printf("\n\n===>%d", pocet_zaznamu_materialu);
if(pocet_zaznamu_materialu > 0) {
printf("\nExistuje %d materialu", pocet_zaznamu_materialu);
free(pMaterialy);
pMaterialy = (MATERIAL *) malloc(pocet_zaznamu_materialu * sizeof(MATERIAL));
for(i = 0; i < pocet_zaznamu_materialu; i++) {
b = fread(&pMaterialy[i], sizeof(MATERIAL), 1, pSoubor);
}
printf("\n otrava %d", b);
}
else {
printf("\nNeexistuje předchozí záznam materialu");
}
fclose(pSoubor);
return;
}
Right now pocet_zaznamu_materialu is hard code to 3, because there are 3 structures in a file and it all works correctly. But what if number of structures in file changes?
Problem: I need to know - number of structures in file, how to a do it?
Thanks, sorry for eng
If the file is composed of nothing but a list of your desired struct stored contiguously, then the file's size, in bytes, will be a multiple of the size of your struct, and you can obtain the file size and then the number of structs in the file like so:
size_t len_file, num_structs;
fseek(fp, 0, SEEK_END);
len_file = ftell(fp);
rewind(fp);
num_structs = len_file/sizeof(MYSTRUCT);
This can be a real problem when you read from a dynamic file (another program writes at the end of file while you read it), a pipe or a network socket. In that case, you really have no way to guess the number of structs.
In that case, a common idiom is to use a dynamicaly allocated array of structs of an arbitrary size and then make it grow with realloc each time the currently allocated array is full. You could for example make the new size be twice the previous one.
That is the way C++ vectors manage their underlying array under the hood.
Have you considered adding a header to the file?
That is, place a special structure at the start of the file that tells you some information about the file. Something like ...
struct file_header {
char id[32]; /* Let this contain a special identifying string */
uint32_t version; /* version number in case the file structure changes */
uint32_t num_material; /* number of material structures in file */
};
Not only does this give you a relatively quick way to determine how many material structures you have in your file, it is also extensible. Perhaps you will want to store other structures in this file, and you want to know how many of each are in there--just add a new field and update the version.
If you want, you can even throw in some error checking.

Using pointer to point to array of pointer that points to struct

I am having some trouble starting my program. I'm new to this. I have done some research and found some resources, but I have trouble applying it to the code. It is mostly based on pointers and structures.
I mainly need help with learning how to store the data in the struct, and how to initialize everything.
The program is supposed to find the frequency of characters in a file. I need to use dynamic memory allocation. And use a dynamically allocated array of pointers to store the characters and frequencies. I am supposed to use malloc() to allocate the array and realloc() to increase the size of the array to insert more elements. But I have no idea how to do this.
The program uses these functions-
• charInCFStruct:which returns the index of charfreq struct which has the char c stored in its member variable next. If none of the charfreq structs contains c then it has to return -1.
• printCFStruct: Prints the contents of all of the charfreq structs.
• freeCFStruct: Frees all of the charfreq structs and then frees the pointer
to the structs.
Down below is what I know is right so far. I thought it would be easier to start again from there. I am not asking for code exactly, just some help with the topics I need to do this, and a push in the right direction. Thank you!
#include <stdio.h>
#include <stdlib.h>/*
* struct for storing a char and the number of times it appears in a provided text */
struct charfreq
{
int count;
char next;
};
typedef struct charfreq charfreq;
/*
* Returns the index of charfreq struct which has the char c stored in its member variable next.
* If none of the charfreq structs contains c then it returns -1.
*/
int charInCFStruct(charfreq **cfarray, int size, char c){
}
/*
* Prints the contents of all of the charfreq structs.
*/
void printCFStruct(charfreq **cfarray, int size){
}
/*
* Frees all of the charfreq structs and then frees the pointer to the structs.
*/
void freeCFStruct(charfreq **cfarray, int size){
}
int main(void)
{
charfreq **cfarray;
FILE *inputfile;
char next;
int size = 10; /* used initial value of 10 but any positive number should work */
int i = 0;
int pos;
/* open file to read from */
inputfile = fopen("chars.txt", "r");
if(inputfile == NULL)
printf("chars.txt could not be opened. Check that the file is in the same directory as you are running this code. Ensure that its name is chars.txt.\n\n");
/* allocate space for pointers to char frequency structs */
cfarray = (charfreq**)malloc(size*sizeof(charfreq*));
/* read in chars until the end of file is reached */
while(fscanf(inputfile, "%c", &next) != EOF){
/* fill in code to fill structs with data being read in */
/* call to increase space after changing size */
cfarray = realloc(cfarray,size*sizeof(charfreq*));
}
/* print out char frequency structs */
printCFStruct(cfarray,i);
/* free all char frequency structs */
freeCFStruct(cfarray,i);
/* close the file we opened earlier */
fclose(inputfile);
return 0;
}

Array of struct implementation in C

I need some ideas on my array of struct implementation. This is what I have in my structs. I plan on making SHAPES an array because I will have multiple SHAPES. Each shape will have multiple x and y coordinates. Because of this I'm not sure if should make a linked list or not. The purpose of the start and finish is I will eventually be running a search algorithm after I get my shapes right.
struct START
{
int x;
int y;
};
struct END
{
int x;
int y;
};
struct SHAPES
{
int x [100];
int y [100];
int *link;
};
struct DISTANCE
{
int distance_traveled;
int distance_to_finish;
};
I was reading this and was wondering if I needed to malloc or calloc my structs as I create them. If so why and if not why? Malloc and calloc are always confusing to me.
How do you make an array of structs in C?
int main(int argc, char *argv[])
{
//Is it better to make these pointers?
struct START start;
struct END end;
struct SHAPES shapes[100];
while(fgets(line, 80, fp) != NULL)
{
//I have a very annoying issue here, I don't know the size of the lines.
//If there are more values I want to increment the x[] and y[] array but NOT the
//shapes array. I can only think of one way to deal with this by putting a bunch of
//variables in the sscanf. I discovered putting sscanf on the next line didn't do what
//I was hoping for.
sscanf(line, "%d%*c %d%*c ", &shapes[i].x[i] , &shapes[i].y[i] );
printf(" line is: %s \n", line);
sscanf(line, "%d%*c %d%*c ", &x1_main , &y1_main );
printf(" line is: %s \n", line);
printf(" shapes[i].x[i] is: %d \n", shapes[i].x[i]);
printf(" shapes[i].y[i] is: %d \n", shapes[i].y[i]);
printf(" x1_main is: %d \n", x1_main);
printf(" y1_main is: %d \n", y1_main);
i++;
memset(line, 0, 80);
}
}
This is what my file looks like. Adding the %*c seemed to handle the commas and semicolons appropriately.
10, 4
22, 37
22, 8; 2, 0; 3, 6; 7, 8; 5, 10; 25, 2
1, 2
I got that idea from here.
https://www.daniweb.com/software-development/c/threads/334515/reading-comma-separated-values-with-fscanf
First of all, you might want to consider something like this:
struct point {
int x;
int y;
};
so you can use a struct point data structure (array) instead of two separate data structures for x and y. Using it like this should also speed up access to the points, since their coordinates are next to each other in memory. Otherwise you will have x somewhere in the x array and y somewhere in the y array.
The choice of the data structure to store the points depends on your usage. If you need to address points directly, a linked list may be a bad choice. If you always access all points in a linear order, it is fine. However, consider that a singly linked list will add 8 bytes per point for the next pointer. A doubly linked list will use another 8 bytes for prev (assuming 64-bit arch that is; sizeof(pointer) in general). I assume, that you create x[100] and y[100] to make sure you have enough space. You might be better off using a dynamic array (the ADT) e.g. glib's GArray after all. It will grow as big as you need it without you doing anything.
For malloc vs calloc: it doesn't really matter. A call to calloc is basically a malloc followed by a
memset(ptr, 0, sizeof(mallocd area);
i.e. the memory is zeroed; cf manpage calloc. If you initialize the memory directly you may not need to do this.
A struct with no pointer members
If your struct has no pointer members, such as:
typedef struct {
int a;
int b;
} DEMO;
Then you can simply declare an array instance of a typedef struct like this:
DEMO demo[10];// instance of array of 10 DEMO
Example, struct with Pointer members
If you have a pointer in the list of members:
#define SIZE_STR 20
typedef struct {
int a;
int b;
char *str;//pointer, will require memory allocation
} DEMO;
DEMO demo[10];// instance of array of 10 DEMO
int main(void)
{
int i;
for(i=0;i<10;i++)//create memory for each instance of char * in array of DEMO
{
demo[i].str = malloc(SIZE_STR);
}
return 0;
}
Don't forget to free() all instances of malloc'ed memory.
Dynamically allocate array of struct
If you need to dynamically allocate memory for a struct:
typedef struct {
int a;
int b;
} DEMO;
DEMO demo, *pDemo;// create a pointer to DEMO
In a function, main() for example:
int main(void)
{
pDemo = &demo;
pDemo = malloc(sizeof(DEMO)*10);//provides an array of 10 DEMO
return 0;
}
Again, don't forget to free() all instances of malloc'ed memory.

Resources