How to delete structure entries and use realloc to free memory - c

I am trying to delete structure entries and then use realloc to free the memory. My structure is
typedef struct person {
char fname[20];
char lname[20];
int number[10];
}person;
Im using a function to delete inputted entries
void delInfo(int*,person*);
The way the delete function is supposed to work is by locating said entry, shift back all records after that, then freeing last record with realloc.
The code for it so far looks like this
void delInfo(int *num_entries,person*contacts){
char delfirst[20];
char dellast[20];
printf("\n First Name: ");
scanf("%s",delfirst);
printf(" Last Name: ");
scanf("%s",dellast);
int i=0;
for (i=0; i<*num_entries;i++){
if (delfirst==contacts[i].fname && dellast==contacts[i].lname){
}
}
}
So far I can search for a match with the first and last name, but dont know what happens if I move every entry "down one." If matching entry is contacts[i] something like
int c;
for (c=i+1;c<*num_entries;c++){
contacts[i].fname=contacts[c].fname;
contacts[i].lname=contacts[c].lname;
contacts[i].number=contacts[c].number;
}
contacts=(person*)realloc(contacts,sizeof(contacts)*(num_entries-1));
If I do that to all entries AFTER the one I plan on deleting, do they just "overwrite" the entry I want to delete, then I can realloc to shorten the structure array and essentially free the memory?

If i is the index of the entry that you want to delete, all you really need to do is:
for (c = i; c < *num_entries - 1; c++)
contacts[c] = contacts[c+1];
Then you can reallocate for the smaller dynamic array. Structs can be assigned to compatible structs, so you don't need to assign the fields of the structs individually.

Related

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.

Sorting Customer Struct In C

I have structure with most frequent words in huge text file, the field pointer array to char are words, and field count are their frequencies. My question is how to sort them from the longest word length to lowest - to nicely display it to user? Code:
typedef struct pair {
char * a[20000];
int count[32000];
} Pair;
Example print:
printf("%d, %d, %d\n", bag.count[0], bag.count[1], bag.count[2]); // -> 8, 7, 3
printf("%s, %s, %s\n", bag.a[0], bag.a[1], bag.a[2]); // -> abbes, abbey, abhor
I'd suggest to turn the structure/array inside-out.
Having your arrays inside the struct does not feel right. Because you primarily have a pair of things, and secondarily you want one array of these things. Do you see what I mean?
It would look like this:
typedef struct pair
{
char* word;
int count;
} Pair;
Pair pairs[32000];
You'd also need to know how many pairs are filled. (You would have needed this anyway.):
int index; // Index of next free pair.
Then use C standard qsort():
#include <stdlib.h>
...
int comparePairs(const void *pairA, const void *pairB)
{
Pair* a = (Pair*)pairA;
Pair* b = (Pair*)pairB;
return strlen(a->word) - strlen(b->word);
}
qsort(pairs, index, sizeof(Pair), comparePairs);
The index would start at 0, which indicates the next free Pair is at that index. Adding an element would be:
pairs[index].word = someWord; // someWord must be allocated elsewhere!
pairs[index].count = 1;
index++;
Note that, because your structure only has a char pointer, that the someWord must be allocated elsewhere. Without automatic memory management this is going to be rather cumbersome. A better alternative would be to strcpy() the word in by using the following structure:
typedef struct pair
{
char word[50]; // Assumes a word is NEVER longer than 49 characters.
int count;
} Pair;
Adding a new element would then become:
strncpy(pairs[index].word, someWord, 50 - 1);
pairs[index].count = 1;
index++;
The strncpy() above copies at most 49 characters. You need to make sure you chose this 50 or whatever size wisely to make sure strncpy() never starts chopping off ends of your very long words.
But of course to know if you have to add a new or simply increment the count of an existing one, you'd first need to search through the existing Pairs with a simple loop.

Make a new struct variable when users needs it

I'm just getting started with C, and having issues with struct. For instance I have:
struct student {
int id;
char name[25]
};
I want the user to add as many students as he needs:
int count = 0;
while (stop == 0){
struct student count
scanf("%d", count.id);
scanf("%s", count.name);
scanf("%d", stop);
}
It looks like I've to create struct student count (where count is a number) and keep creating these. So, I would like to create something like struct student 0, then struct student 1 and so on, so I can reference each student by it's count or number.
How would I get something like this to work?
This will automatically allocate memory when user requests it. It starts from a dimension of 1, up to virtually infinite (actually, up to the space available in RAM).
Of course if you want, you can change the initial size of size, as well as the growth rate of the array.
// Example program
#include <stdio.h> /* printf, scanf */
#include <stdlib.h> /* for realloc, malloc */
// Supposing student is defined like this:
struct student
{
int id;
char name[25];
};
int main()
{
int stop = 0;
int count = 0;
int size = 0;
// Using an array of students
size = 1;
struct student* students = malloc(size * sizeof(struct student));
while (stop == 0)
{
if(count >= size)
{
size ++;
students = realloc (students, size * sizeof(struct student));
if (students == NULL)
{
printf ("Failed to allocate more memory.\n");
return 0;
}
}
scanf("%d", &(students[count].id));
scanf(" %24[0-9a-zA-Z ]", &(students[count].name));
scanf("%d", &stop);
count = count + 1;
}
int i = 0;
for (i = 0; i < count; ++i)
printf("%d => %d %s\n", i, students[i].id, students[i].name);
}
I think you would like to create multiple instances of the struct in your first code sample for each user that is entered in the console, handled by the while-loop.
The easiest way to achieve this, is to use an array. I suggest you to first use an array with a fixed size, that means that you specify the array size in your code. This array will allow you to add as many student instances into it as the array size you've specified.
A simple example would be something like this:
// Define the student struct
struct student {
int id;
char name[25];
};
// ...
// Create an array with a fixed size to put the students in, and define a counter
struct student students[128];
int count = 0;
while(stop == 0){
// Create the struct to fill
struct student newStudent;
// Fill the struct with the user supplied data
scanf("%d", newStudent.id);
scanf("%s", newStudent.name);
scanf("%d", stop);
// Add the struct to the array, and increase the count afterwards
students[count++] = newStudent;
}
In the above example, I've added an array with a fixed size of 128, which you can change to whatever size you'd like. In the while-loop, an instance of a new struct is made, which is similar to before. This struct is being filled afterwards with data fed from the console. At the end of the while-loop the struct instance is added to the array of students. This will give you an array of all the students you've entered.
There is a downside to this solution however, and that's that in most cases, much more memory is consumed than is really used. This is because for the computer, it feels like 128 whole instances (or any other array size if specified) are stored in RAM, this can be quite expensive if only two instances will be really used. Also, like I said earlier, make sure to keep in mind that fixing the array size limits the amount of entries, this can also have a negative effect on your code. If you don't want to have these consequences, you may want to take a look at the solution described bellow.
You can make the size of the array dynamic, this is a little more advanced. If you'd like to achieve something like this, make sure to take a look at memory allocation functions, like Sourav Ghosh pointed out in a comment. You may also want to take a look at the code-example Michael made.
I hope this helps to solve the problem you're having. Happy coding!

Implementing arraylist in C

I have created a program to add 2 types of items in to a system. I have created 2 structures for the 2 different items. Currently i have created a method to add items to the system and i store each item in an array.
However i encountered a problem when i was gonna implement the delete feature, the problem is if i have a record at array memory index 2, if i delete it there will be an unused space, between memory index 1 and 3. How can i overcome this ? In java , there is arraylist which dynamically allocates space. In C i know that there is dynamic memory allocation , but how can i implement it work with the delete feature ?
Here is what i have done so far :-
#include <stdio.h>
#include <string.h>
struct routers
{
int Device_No;
char Device_Name[30];
int No_of_items;
int Price;
char Description[30];
};
/**declared an of struct routers to store the structure objects
**/
struct routers routerlist[5];
struct controllers
{
int Device_No;
char Device_Name;
int No_of_items;
int Price;
char Description[30];
};
void AddNewItem();
int main()
{
AddNewItem();
return 0;
}
void AddNewItem(){
int item;
int choice=0;
int arraysize=0;
do{
printf("Press 1 to add a Router \nPress 2 to add a controller \n");
scanf("%d",&item);
printf("%d",item);
if(item==1){
printf("\nEnter Device No:\n");
scanf("%d",&routerlist[arraysize].Device_No);
printf("Enter Device Name\n");
fflush(stdin); //flush the buffer
gets(routerlist[arraysize].Device_Name);
printf("Enter Number of Items\n");
scanf("%d",&routerlist[arraysize].No_of_items);
printf("Enter price\n");
scanf("%d",&routerlist[arraysize].Price);
printf("Enter description\n");
fflush(stdin);
gets(routerlist[arraysize].Description);
}
arraysize++;
printf("Do you want to add another item? \nPress 1 to add \nPress 2 to Cancel\n");
scanf("%d",&choice);
}while(choice==1);
}
Thank you for your time.
Depending on your time complexity requirements, there are basically two approaches:
Use a list. A list is a data structure where every item knows where the next item is stored. This is usually implemented by the data structure holding a pointer to two objects of its own kind (the previous one and the next one). That way, when an item is deleted, the pointers of the next and previous items can be adjusted so that the gap is closed.
This means, deleting an element is very fast, but accessing an element by position requires searching for the element from the beginning, which is very slow.
Use an array. An array is a data structure where items are stored consecutively. When an item is deleted, the gap is filled by shifting the following elements.
This means, accessing an element by position is very fast, because only arithmetic operations are involved, but deleting an element is very slow, because a possibly large number of elments have to be copied.
Deleting a gap means moving what's after it, so if you delete index 2 the item at index 3 is moved to index 2, then item at index 4 is moved to index 3, and so on.
In C you can use the memmove function for this, or write a loop.
To implement the similar behavior like in arrayList you need to dynamically allocate memory each time the array is full. Then copy all the members of the current list to that memory
In case of deletion you will have to move the elements one space to the deleted element location. This is similar to how vector are implemented in c++. Please refer this answer of mine Vector implementation, this implementation is in c++. The concept you have to look into this code is how a memory is allocated dynamically when required.
So your approach should be as follows:
Initially allocate some memory spaces
Use them until you reach the final allocated block
In case more memory is required allocate new space . Please use realloc here you will find the advantage of it
free the previously allocated memory and let the previous pointer point to this new location.
Similarly when deleting the elements just move all the elements one space from end towards that element.

storing data in addresses and changing the address of a variable in C?

Newbie here,
I have a struct for a word, which contains a char array for the words themselves(the struct has other functions, which are unrelated to my question) and I'm trying to store it in a hashmap, which is an array of word struct pointers. In my program, every time I see a new word, I create a new word struct and malloc the char-array to create it. However, after a few run through of the loop, it changes the old word to a new word, even though it's at different hashmap locations.
What I'm wondering is if it's possible to have the loop in which I create the new word struct point to a new address?
struct words add;
int b;
for(b = 0; b < strlen(LowerCaseCopy); b++)
{
add.word[b] = '\0';
}
for(b=0;b< strlen(LowerCaseCopy);b++)
{
add.word[b] = LowerCaseCopy[b];
}
hashmap[hashf] = &add;
This is the code in question.
An example of my problem:
the first runthrough of the loop, I set add.word to apple, which is stored at a specific hashmap slot.
the next runthrough of the loop, I set add.word to orange, which is stored at a different slot. The problem is that at the first slot, it no longer stores apple, it instead stores orange, so I have 2 slots that store orange, which is not what I want. How do I fix this?
A simple solution (I think) would be to put the functionality to add entries to the hashmap in a separate function. This function allocates a new words structure and puts that in the hashmap:
void add_to_hashmap(struct something *hashmap, char *lower_case_word)
{
/* Using "calloc" we don't have to manually clear the structure */
struct words *words = calloc(1, sizeof(struct words));
/* Copy +1 to include the terminating '\0' */
memcpy(words->word, lower_case_word, strlen(lower_case_word) + 1);
/* Replace this with whatever you use to calculate the hash */
int hashf = calculate_hash(lower_case_word);
hashmap[hashf] = words;
}
If you remove an entry (i.e. setting it to NULL) you have to remember to free it first.

Resources