Creating a dynamic custom array of nested structs in C - c

I'm trying to create the following array:
"Fruits", 25, {
{"Apple", 2},
{"Grapes", 13},
{"Melon", 10}
}
"Meats", 40, {
{"Beef", 9},
{"Chicken", 27},
{"Pork", 4}
}
...
Feels like there's a more elegant way of doing what I got so far. Any feedback/samples on how to create this structure more efficient given the input struct would be appreciated.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct Product {
char *name;
int qty;
} Prods;
typedef struct Category {
char *name;
int qty;
int prods_count;
Prods *prod;
} Cats;
typedef struct Inventory {
Cats *cat;
int cats_count;
} Inv;
struct tmp_input {
char name[12];
int qty;
char cat[12];
};
// return index if found
int in_array(Inv *inv, char *k) {
int i;
if (inv->cats_count == 0)
return -1;
for (i = 0; i < inv->cats_count; i++) {
if (strcmp (k, inv->cat[i].name) == 0) return i;
}
return -1;
}
int main () {
int i, j, exists = 0;
// temp struct.
struct tmp_input items[] = {
{"Apple", 2, "Fruit"}, {"Coke", 10, "Drink"}, {"Pork", 4, "Meat"},
{"Beef", 9, "Meat"}, {"Chicken", 27, "Meat"}, {"Melon", 10, "Fruit"},
{"Tea", 3, "Drink"}, {"Coffee", 20, "Drink"}, {"Grapes", 13, "Fruit"}
};
size_t len = sizeof (items) / sizeof (struct tmp_input);
Inv *inven = malloc(sizeof(Inv));
inven->cats_count = 0;
inven->cat = calloc(1, sizeof(Cats));
for (i = 0; i < len; i++) {
exists = in_array(inven, items[i].cat);
// category does not exist
if (exists == -1) {
inven->cat = realloc(inven->cat, sizeof(Cats) * (inven->cats_count + 1));
inven->cat[inven->cats_count].name = strdup(items[i].cat);
inven->cat[inven->cats_count].qty += items[i].qty;
inven->cat[inven->cats_count].prods_count = 1;
inven->cat[inven->cats_count].prod = calloc (1, sizeof (Prods));
inven->cat[inven->cats_count].prod->name = strdup (items[i].name);
inven->cat[inven->cats_count].prod->qty = items[i].qty;
inven->cats_count++;
}
// category found
else {
inven->cat[exists].qty += items[i].qty;
int size = inven->cat[exists].prods_count + 1;
inven->cat[exists].prod = realloc(inven->cat[exists].prod, sizeof(Prods) * (size));
inven->cat[exists].prod[size - 1].name = strdup (items[i].name);
inven->cat[exists].prod[size - 1].qty= items[i].qty;
inven->cat[exists].prods_count++;
}
}
for (i = 0; i < inven->cats_count; i++) {
printf("%3d %s\n", inven->cat[i].qty, inven->cat[i].name);
for (j = 0; j < inven->cat[i].prods_count; j++) {
printf("%3d %s\n", inven->cat[i].prod[j].qty, inven->cat[i].prod[j].name);
}
}
return 0;
}

You aren't allocating any memory for the Prod array.
Something like
...
if (exists == -1) {
inven->cat = realloc(inven->cat, sizeof(Cats) * (inven->cats_count + 1));
inven->cat[inven->cats_count].name = items[i].cat;
inven->cat[inven->cats_count].qty += items[i].qty;
// Allocate memory for 1 product
inven->cat[inven->cats_count].prods_count = 1;
inven->cat[inven->cats_count].prod = malloc (sizeof (Prods));
// Now allocate space and copy the name
inven->cat[inven->cats_count].prod->name = strdup (items[i].name + 1);
inven->cats_count++;
}
...
I will leave it to you to handle the case where there are more than 1 product in a category, where you'll need to reallocate the memory again.
Another error is that you need to allocate and copy the category name
inven->cat[inven->cats_count].name = items[i].cat;
should be replaced by
inven->cat[inven->cats_count].name = strdup (items[i].cat);
This is because the items array does not exist outside of this function, so if you just do
inven->cat[inven->cats_count].name = items[i].cat;
then after you leave this function, invent->cat[inven->cats_count].name will point to garbage memory.
A final suggestion would be to split each structure into a function that handles creation of it, just to clean up the code.
--- edit to add comments on Abstract Data Types
Arrays are useful if you have data that you know you will be accessing via indices. If you don't know the index of the item you want (as in this case), an array is less useful.
Unlike other comments, I don't think using a Linked List really gives you anything useful. Linked Lists are useful when you need to walk sequentially through all the items, without really caring where they are in the list. It seems to me that the most common use case for a system like you are creating is searching: Do we have any Fruit in stock? Add 10 cases of Coke to the inventory... those sorts of things.
Also, you only want a single entry for each category/product. You don't want 3 Fruit categories in the data. Both arrays and linked lists don't really have any restrictions on adding the same structure multiple times. This means that every time you'll need to check the whole list to see if you need to add the new structure.
For that reason, I'd certainly make both the Categories and products arrays into hashtables (or called a dictionary in some languages) that map name -> structure. This will speed up your search as you don't have to search the entire dataset every time and will prevent you from adding the same structure multiple times.
Wikipedia article on Hashtables: http://en.wikipedia.org/wiki/Hashtable

Here's an example of how to set up the structures dynamically (simpler than a linked list, but not as flexible).
typedef struct Product {
char *name;
int qty;
} Prods;
typedef struct Category {
char *name;
int qty;
int prods_count;
Prods *prod; // dynamic array of Products
} Cats;
The structures as they were.
struct Category categ[10];
An arbitrary number of categories, for now take categ[0] for 'Fruits'.
Next dynamically create an array of 10 product structures:
Prods *prod_array = malloc(sizeof(Prods) * 10); // i.e. prod_array[0] to [9]
Now just store the array in the category structure:
categ[0].prod = prod_array;
categ[0].prods_count = 10;
If you need to access the product name, it's just: categ[i].prod[j].name
Now, if you need another 10 products, you can just use realloc to increase the size of the array, and update its count.
Put all these things in functions, and the code isn't too complex.

Related

Growing arrays. Refer to the elements by pointers, not indexes

Since the array address may change when memory is reallocated,
the main part of the program (in the body of the function main ()) should refer to the elements by
indexes, not pointers. Why?
Can you show an example of accessing items with pointers?
(Sorry for my English).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Nameval Nameval;
struct Nameval {
char *name;
int value;
};
struct NVtab {
int nval; /* current number of values */
int max; /* allocated number of values */
Nameval *nameval; /* array of name-value pairs */
};
enum {NVINIT = 1, NVGROW = 2};
/* addname: add new name and value to nvtab */
int addname(struct NVtab *nvtab, Nameval newname) {
Nameval *nvp;
if (nvtab->nameval == NULL) { /* first time */
nvtab->nameval = (Nameval *) malloc(NVINIT * sizeof(Nameval));
if (nvtab->nameval == NULL)
return -1;
nvtab->max = NVINIT;
nvtab->nval = 0;
} else if (nvtab->nval >= nvtab->max) { /* grow */
nvp = (Nameval *) realloc(nvtab->nameval,
(NVGROW*nvtab->max)*sizeof(Nameval));
if (nvp == NULL)
return -1;
nvtab->max *= NVGROW;
nvtab->nameval = nvp;
}
nvtab->nameval[nvtab->nval] = newname;
return nvtab->nval++;
}
int main(void) {
struct NVtab nvtab = {0, 0, NULL};
int curnum;
curnum = addname(&nvtab, (Nameval) {.name="Andy", .value=12});
printf("%d\n", curnum);
curnum = addname(&nvtab, (Nameval) {.name="Billy", .value=18});
printf("%d\n", curnum);
curnum = addname(&nvtab, (Nameval) {.name="Jack", .value=71});
printf("%d\n", curnum);
for (int i = 0; i < nvtab.nval; i++) {
printf("%s %d\n", nvtab.nameval[i].name,
nvtab.nameval[i].value);
}
}
For example, why can`t we show array like this:
for (int i = 0; i < nvtab.nval; i++)
printf("%s %d\n", nvtab.*(nameval+i).name, nvtab.*(nameval+i).value);
You are not supposed to assign a pointer calculated for a specific index to a variable with storage duration which could extend over an insert operation.
That pointer could become invalid, so the lesson behind that example is to always re-evaluate iterators on dynamic data structures.
E.g. what not to do:
auto *foo = &nvtab.nameval[i];
addname(&nvtab, (Nameval) {.name="Billy", .value=18});
printf("%s %d\n", foo->name, foo->value);
In the last line it can work or crash. Depending on whether realloc moved the allocation or resized in-place. Except that you can never know for sure until you execute it, as it isn't even fully deterministic.
This is not valid syntax:
nvtab. *(nameval+i).name
The member access operator . expects to be followed by the name of the member. What you want is:
(*(nvtab.nameval+i)).name

How to filter an array of structs by certain parameter?

I need to write a program that reads students' details from a file and stores them into an array of structures. After that, I need to filter the data by prompting user. My input file is as below:
Kristina
Science 30
Desmond
Geography 78
Fred
Science 87
Kristina
History 45
Desmond
Mathematics 34
I declare my struct to store the data as
typedef struct {
char name[102];
char subject[40];
int marks;
}Student;
I need to ask the user whether he wants to see the data by name or by subject. If he chooses name, then he will need to select whose data to be shown. For example, if he chooses the name Kristina, the program should output
Kristina
Science 30
History 45
I managed to read from file and store the data into array of structs, but I am stuck in writing the function to output the filtered data. My code for the function is
Student* filter_name(char choice[], Student* res, int index)
{
int i;
choice[strcspn(choice,"\n")]=0;
for(i=0;i<index;i++)
{
if (strcmp(choice,res[i].name)==0)
{
return res;
}
}
}
After I run my code, it is still the same array of structs that was read from the file previously. I need the filtered array to sort it later in my program.
I need a copy of the elements that match, because i will need the existing array for another purpose as well.
Anyone can tell me how to return the filtered array?
You have multiple problems to solve.
First, your filtering function is going to return multiple values - the filtered array itself and its size (length). As the linked question suggests, you need to use a struct or return the value(s) by pointer. For example of the latter:
Student* filter_by_name(char* name,Student* students,int students_size, int* filtered_size)
{
...
}
Second, the code itself. Since you want to copy the data for your existing students, you should allocate the resulting array:
Student* filtered = malloc(students_size * sizeof(Student));
Then do a loop over the students:
for (int i = 0; i < students_size; ++i)
{
...
}
Inside the loop, check whether the name matches. If yes, don't return but copy one element from the array students to the array filtered:
filtered[(*filtered_size)++] = students[i];
Note how I used post-increment of filtered_size to copy the new element to the tail of the new array. This keeps track of the size of the new array.
Assuming you just need to know which elements of the original array of structs match you then could just return an array of pointer pointing to the matching elements. The last entry in this pointer array would hold a NULL to indicate the end of matches.
So the filter function might look like this:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
Student const ** result_resize(Student const ** result, size_t size)
{
void * pv = realloc((void*) result, size * sizeof *result);
if (NULL == pv)
{
perror("realloc() failed");
exit(EXIT_FAILURE);
}
return pv;
}
Student const ** students_filter_by_name(const char * name, Student * pstudent, size_t size)
{
size_t matches = 0;
size_t size_result = 1;
Student const ** result = result_resize(NULL, size_result);
for (size_t i = 0; i < size; ++i)
{
if (!strcmp(name, pstudent[i].name))
{
if (size_result <= matches)
{
size_result *= 2;
result = result_resize(result, size_result);
}
result[matches] = &pstudent[i];
++matches;
}
}
if (size_result <= matches)
{
++size_result;
result = result_resize(result, size_result);
}
result[matches] = NULL; /* Mark end-of-array */
return result;
}
Use it like this:
Student const ** students_filter_by_name(
const char * name, Student * pstudent, size_t size);
#define STUDENTS_MAX (42)
int main(void)
{
Student students[STUDENTS_MAX] = {
{"jack", "foo", 1},
{"jill", "bar", 1},
{"alk", "foo", 1},
{"jill", "bar", 1},
{"jack", "foo", 1},
{"alk", "bar", 1},
};
/* Load data to array students here. */
{
const char * name = "alk";
Student const ** ppmatches = students_filter_by_name(
name, students, sizeof students / sizeof *students);
{
Student const ** pploop = ppmatches;
printf("%s's marks:\n", name);
while (pploop && *pploop)
{
printf("subject = '%s', mark = %d\n", (*pploop)->subject, (*pploop)->mark);
++pploop;
}
}
free(ppmatches);
}
}
To sort the result just sort the array of pointer created by the filter function.

Key Value Pair in C Language

I am new to C programming and I am trying to create a key value structure as in Perl Programming. I saw one solution like :-
struct key_value
{
int key;
char* value;
};
struct key_value kv;
kv.key = 1;
kv.value = "foo";
But I don't know how to access these values from this structure. Can someone enlight on this ?
Here is an example:
#include <stdio.h>
#include <stdlib.h>
struct key_value
{
int key;
char* value;
};
int main(void)
{
int number_of_keys = 2;
struct key_value *kv = malloc(sizeof(struct key_value) * number_of_keys);
if (kv == NULL) {
perror("Malloc");
exit(EXIT_FAILURE);
}
kv[0].key = 8;
kv[0].value = "Test 8 key!";
kv[1].key = 6;
kv[1].value = "Test 6 key!";
printf("Key = %d\nKey value = %s\n", kv[0].key, kv[0].value);
printf("Key = %d\nKey value = %s\n", kv[1].key, kv[1].value);
free(kv);
return 0;
}
What you are missing is a collection. Most languages have a data type called a dictionary or a map or an associative array or some variation thereof. C does not have a data structure of this type; in fact, the only collection type you have built in to C is the array. So, if you want something where you can supply a key and get the value, you have to roll your own or find one on the Internet. The latter is probably preferable because you are likely to make mistakes and produce a slow data structure if you roll your own (especially if you are a beginner).
To give you a flavour of what you'll end up with, here's a simple example:
You'll need something to represent the collection; call it a ListMap for now:
struct ListMap;
The above is called an incomplete type. For now, we are not concerned with what's in it. You can't do anything with it except pass pointers to instances around.
You need a function to insert items into your collection. Its prototype would look something like this:
bool listMapInsert(struct ListMap* collection, int key, const char* value);
// Returns true if insert is successful, false if the map is full in some way.
And you need a function to retrieve the value for any one key.
const char* listMapValueForKey(struct ListMap* collection, int key);
You also need a function to initialise the collection:
struct ListMap* newListMap();
and to throw it away:
void freeListMap(struct ListMap* listMap);
The hard bit is implementing how those functions do what they do. Anyway, here's how you would use them:
struct ListMap* myMap = newListMap();
listMapInsert(myMap, 1, "foo");
listMapInsert(myMap, 1729, "taxi");
listMapInsert(myMap, 28, "perfect");
char* value = listMapValueForKey(myMap, 28); // perfect
freeListMap(myMap);
Here's a simple implementation. This is just for illustration because I haven't tested it and searching for entries increases linearly with the number of entries (you can do much better than that with hash tables and other structures).
enum
{
listMapCapacity = 20
};
struct ListMap
{
struct key_value kvPairs[listMapCapacity];
size_t count;
};
struct ListMap* newListMap()
{
struct ListMap* ret = calloc(1, sizeof *ret);
ret->count = 0; // not strictly necessary because of calloc
return ret;
}
bool listMapInsert(struct ListMap* collection, int key, const char* value)
{
if (collection->count == listMapCapacity)
{
return false;
}
collection->kvPairs[count].key = key;
collection->kvPairs[count].value = strdup(value);
count++;
return true;
}
const char* listMapValueForKey(struct ListMap* collection, int key)
{
const char* ret = NULL;
for (size_t i = 0 ; i < collection->count && ret == NULL ; ++i)
{
if (collection->kvPairs[i].key == key)
{
ret = kvPairs[i].value;
}
}
return ret;
}
void freeListMap(struct ListMap* listMap)
{
if (listMap == NULL)
{
return;
}
for (size_t i = 0 ; i < listMap->count ; ++i)
{
free(listMap->kvPair[i].value);
}
free(listMap);
}
typedef struct key_value
{
int key;
char* value;
}List;
struct key_value k1;
struct key_value k2;
struct key_value k3;
k1.key = 1;
k1.value = "foo";
k2.key = 2;
k2.value = "sec";
k3.key = 3;
k3.value = "third";
You will need to create N times the struct and give them values the way you did the first one. Or create array with N structs and iterate assign it values with a loop.
Array:
List arr[29];
int i;
for(i = 0;i<=28;i++){
arr[i].key = i;
arr[i].value = "W/e it needs to be";
}
The functionality you are looking for needs your own implementation in C; e.g. an array of your struct-type.
Here is an example of how to read the value for a key, without knowing anything about at which array-index the key will be found.
I have the keys numbered backward in order to illustrate that.
Note that more sophisticated API definitions are needed for special cases such as non-existing key; I just blindly return the last entry to keep things easy here.
#include <stdio.h>
#define MAPSIZE 30
struct key_value
{
int key;
char* value;
};
struct key_value kvmap[MAPSIZE];
void initmap(void)
{
int i;
for(i=0; i<MAPSIZE; i++)
{
kvmap[i].key=MAPSIZE-i-1;
kvmap[i].value="unset";
}
kvmap[0].value="zero";
kvmap[1].value="one";
kvmap[2].value="two";
kvmap[3].value="three";
kvmap[4].value="four";
kvmap[5].value="five";
kvmap[6].value="six";
kvmap[7].value="seven";
kvmap[8].value="eight";
kvmap[24].value="find this"; // it has the key "5"
}
char* readmap(int key)
{
int i=0;
while ((i<MAPSIZE-1) && (kvmap[i].key!=key))
{ printf("Not in %d\n", i);
++i;}
// will return last entry if key is not present
return kvmap[i].value;
}
int main(void)
{
initmap();
printf("%s\n", readmap(5));
return 0;
}
"I have to store 30 key/value pair"
Create an array of struct e.g., key_value.
struct key_value
{
int key;
char* value;
};
struct key_value kv[30];
kv[0].key = 1;
kv[0].value = "foo";
printf("%s", kv[0].value);
You can loop through to assign values to keys and values.
Access to whatever is in kv is simple.
int i = kv[0].key`;// copy value of k[0].key to i
char *v = kv[0].value; // copy value of k[0].value to v;
Your code already have the method to acess the values.
kv.key = 1
kv.value = "foo"
To get the values assigned is simple
kv.key
kv.value
It is a simple struct, if you wanna something like python dict you will need to implement a hash struct which will be more complicated.

Memory allocation failure on last loop iteration

So... in the past I've been told that my questions aren't good... I believe mostly because I haven't isolated out problematic code well enough. I'll do my best to ask a pointed, concise, and to the point question in this post. I'm certainly open to suggestions about how my question could be asked better.
Thanks.
I'm working on a small project in C that will serve as a prototype for a larger, buggy program that I've been working on for some time. I'm trying to work out the details in a smaller program first. I have two structs:
struct list
{
char ownerName[20];
int ownerAge;
char sex;
}owner;
and
struct list2
{
char petName[20];
char owner[20];
char animal[4];
char breed[50];
char color[20];
}pets;
The program is supposed to fgets ownerName from user input and compare it to ".owner" in the pets struct. The ownerName and petName elements should then be copied into an array, and the name of the owner and his/her pets will be printed in a list. While I'm aware I don't need the owner struct to accomplish this, I'm using it to model the other program I'm writing.
I'm using
if (strcmp(pets[i].owner, name) == 0)
to compare the struct elements and seem to have this part down.
The variable j counts the number of records that meet this criteria, and the variable l = j + 1. I call the array using:
char *petsList[l];
The size of the array is dictated by l (j + 1) because I need j elements for the petNames + 1 element for the owner name.
I've also created a pointer to the petsList array via the following:
char *(*ptr)[l] = &petsList
The owner name is added to the array via the following command:
(*ptr)[0] = (char *)malloc(sizeof(name));
strcpy ( (*ptr)[0], name);
The petNames are added to the the array petsList using a for loop. I've initialized i = 1 to prevent petsList[0] from being overwritten and am trying to write petNames to the array via the following loop:
i = 1;
for (k=0; k < PETS; k++)
{
if (strcmp(pets[k].owner, name) == 0)
{
(*ptr)[i] = (char *)malloc(sizeof(pets[k].petName));
if (!*(ptr)[i])
{
puts("\nMemory Allocation Error");
exit (1);
}
strcpy( (*ptr)[i], pets[k].petName);
i++;
}
}
Let's say for a given input of name, I get three pets that match. The loop iterates the first two times just fine, but then on the third iteration of the loop, I get a memory allocation error. This happens on the last iteration of the loop consistently. For example, if I have 2 pets associated with the ownerName, the list will run the first iteration fine and fail on the second; if I have 4 pets associated with the ownerName, the loop will run fine the first 3 times and fail on the fourth, so it appears that the final iteration of the loop consistently fails. I've tried changing the code a number of times, but am now at a loss for how I can move forward with this program. Any help is greatly appreciated.
Thanks.
Don`t cast the malloc return value.
Since I can`t really check with a minimal example.
char *(*ptr)[l] = &petsList
why do you make such a complex construct? I am not even sure what it is supposed to accomplish. do you want all pets in the first index and the next index to contain the owner? this could be accomplished just with the petslist
what structure do you really need at the end?
is it something like:
array:
0 = owner
1 = pet 1
2 = pet 2
or something like
0,0 = owner 1,0 = owner 2 etc.
0,1 = pet 1 1,1 = pet 3
0,2 = pet 2 1,2 = pet 4
Ok here is a working example of what you want to do. you can easily extend it to do the second data arrangement. If you have any questions feel free to ask
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PETAMOUNT 40
struct list2
{
char petName[20];
char owner[20];
char animal[4];
char breed[50];
char color[20];
};
int main() {
struct list2 *pets; // list of all pets
char name[128]; // contain name of the owner, get from stdin
unsigned int i; // i and j are both counter variable
unsigned int j;
fgets(name, 128, stdin); // get string from stdin
name[strlen(name) - 1] = '\0'; // remove newline
pets = malloc(PETAMOUNT * sizeof(struct list2)); // allocate memory for the list of all pets
if (pets == NULL) {
printf("malloc err\n");
exit(1);
}
for (i = 0; i < PETAMOUNT; i++) { // initialize some pets and some owners
strcpy(pets[i].petName, "petname ");
strcpy(pets[i].owner, "owner ");
pets[i].petName[7] = i + '0'; // there are PETAMOUNT of petnames. petname0, petname1 etc
pets[i].owner[5] = (i / 4) + '0'; // there are PETAMOUNT / 4 owners. owner0 has petname0 to petname3, owner1 has petname4 to 7 etc
}
char ***petslist; // a list of list of strings or 3d char array
petslist = malloc(sizeof(char **)); // allocate pointer to contain a double array
petslist[0] = malloc(sizeof(char *)); // allocate a pointer to contain the name of the owner
if (petslist[0] == NULL) {
printf("malloc err\n");
exit(1);
}
petslist[0][0] = malloc(strlen(name) + 1); // allocate memory to contain the owner
if (petslist[0][0] == NULL) {
printf("malloc err\n");
exit(1);
}
strcpy(petslist[0][0], name); // copy owner into the first index
for (i = 0, j = 1; i < PETAMOUNT; i++) { // go through all pets
if (strcmp(pets[i].owner, name) == 0) { // if the owner of the current pet is the same as the inputted owner
petslist[0] = realloc(petslist[0], (j + 1) * sizeof(char *)); // allocate pointer for the next pet
petslist[0][j] = malloc(strlen(pets[i].petName) + 1); // allocate memory to contain the chars of the pet
if (petslist[0][j] == NULL) {
printf("malloc err\n");
exit(1);
}
strcpy(petslist[0][j], pets[i].petName); // copy the petname into the array
j++;
}
}
puts("petslist:"); // print it all out
for (i = 0; i < j; i++) {
printf("|%s|\n", petslist[0][i]);
}
exit(0);
}
currently I always write to the [0][0] but if you realloc you can make room for more after that
This is a little weird. Maybe:
if(!*(ptr)[i])
should be
if(!(*ptr)[i])
?

Mallocing array of structs within a struct (C)

I'm having trouble finding the answer to this problem; I've found similar examples of this online but none that address my problem.
I have a struct for the data for a company, Company, and a second struct for collections of companies, Consortium. The second struct will contain variable length arrays of the first struct, the company data struct. The number of elements of the variable length arrays will depend on the number of companies in a consortium.
I want to dynamically allocate whatever is required but I'm getting a bit lost. These are the structs:
typedef struct {
char code[];
double sharePrice;
int numShares;
double totalVal;
double totalDebts;
} Company;
typedef struct {
int numCore;
int numAssoc;
Company core[];
Company assoc[];
} Consortium;
There will be a number of core companies, and this number will be the size of the core array in the Consortium struct. Same goes for associate companies.
I came up with this expression but I'm not sure what I'm missing:
Consortium *consort=((Consortium*)malloc((numCore+numAssoc)*(sizeof(Consortium));
You'll need to use pointers and allocate the arrays separately:
typedef struct
{
char *code;
double sharePrice;
int numShares;
double totalVal;
double totalDebts;
} Company;
typedef struct
{
int numCore;
int numAssoc;
Company *core;
Company *assoc;
} Consortium;
Consortium *c = malloc(sizeof(*c)); // Error check
c->numCore = 4;
c->core = malloc(sizeof(*c->core) * c->numCore); // Error check
c->numAssoc = 3;
c->assoc = malloc(sizeof(*c->assoc) * c->numAssoc); // Error check
for (int i = 0; i < c->numCore; i++)
c->core[i].code = malloc(32); // Error check
for (int i = 0; i < c->numAssoc; i++)
c->assoc[i].code = malloc(32); // Error check
// Worry about other data member initializations!
It would be simpler and possibly better to modify the Company type to:
typedef struct
{
char code[32];
double sharePrice;
int numShares;
double totalVal;
double totalDebts;
} Company;
That saves the loops allocating the code elements.
You might consider that you make the Consortium struct a bit simpler. Since you have the counts for each type, core and assoc, you can have just a single array, the first part of which is for core and the second part of which is for assoc.
So your struct would look something like the following source (which has not been compiled and is just jotted down rather than tested so caveat emptor):
typedef struct {
int numCore; // number of core companies, first part of m_companies
int numAssoc; // number of assoc companies, second part of m_companies
Company m_companies[1];
} Consortium;
Then you would create your actual data structure by something like:
Consortium *makeConsortium (int numCore, int numAssoc) {
Consortium *pConsortium = malloc (sizeof(Consortium) + sizeof(Company) * (numCore, numAssoc));
if (pConsortium) {
pConsortium->numCore = numCore;
pConsortium->numAssoc = numAssoc;
}
return pConsortium;
}
After this you could fill it in by some functions which indicate success or not:
int addCompanyCore (Consortium *pConsortium, int index, Company *pCompany) {
int iRetStatus = 0;
if (pConsortium && index < pConsortium->numCore) {
pConsortium->m_companies[index] = *pCompany;
iRetStatus = 1;
}
return iRetStatus;
}
int addCompanyAssoc (Consortium *pConsortium, int index, Company *pCompany) {
int iRetStatus = 0;
if (pConsortium && index < pConsortium->numAssoc) {
index += pConsortium->numCore;
pConsortium->m_companies[index] = *pCompany;
iRetStatus = 1;
}
return iRetStatus;
}
And then you would access them with another set of helper functions.
Company *getCompanyCore (Consortium *pConsortium, int index) {
Company *pCompany = 0;
if (pConsortium && index < pConsortium->numCore) {
pCompany = pConsortium->m_companies + index;
}
return pCompany;
}
Company * getCompanyAssoc (Consortium *pConsortium, int index) {
Company *pCompany = 0;
if (pConsortium && index < pConsortium->numAssoc) {
index += pConsortium->numCore;
pCompany = pConsortium->m_companies + index;
}
return pCompany;
}

Resources