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.
Related
I have an assignment in C that basically asks for some sort of interface/database for a supposed animal shelter, and we were given these 2 structures:
typedef struct age
{
int years, months;
}age;
typedef struct pet
{
int id;
char* sex;
char* breed;
age* pet_age;
}pet;
The interface has to have several functions, like adding a new pet (in our case dogs specifically), removing based on ID, searching for all pets of the same breed and changing the name of a breed entirely, and it all has to be done dynamically using a pet* array as well as the malloc and realloc functions. The entries have to be written in a file and also read from it, but that's something I'll figure out after I figure out how to handle the functions regarding my dynamic array first.
To get to the point, I am having trouble understanding how to scan/reference an instance's pet_age. I've tried it a myriad different ways but I don't understand what's wrong, really. The program crashes/exits after I scan the months element.
Here is the insertion function I have implemented thus far. While not correct, the main source file still compiles.
void addPet(pet *p){
if(i=1){ //First time activation check.
p=malloc(k*sizeof(p));
if(!p){
printf("\nUnable to allocate memory...");
exit(0);
}
}
p[i].sex = malloc(sizeof(char)*1);
p[i].breed = malloc(sizeof(char)*20);
p[i].pet_age =malloc(sizeof(int)*2);
p[i].id = i; //Autogenerated ID
printf("\n%d\n", p[i].id);
printf("Insert pet's breed:"); //Scan breed
scanf("%s", p[i].breed);
printf("Insert pet's sex:"); //Scan sex
scanf("%s", p[i].sex);
printf("Insert pet's age in years:"); //Scan years
scanf("%d", p[i].pet_age->years);
printf("\n%d\n", p[i].pet_age->years);
printf("Insert pet's age in months:"); //Scan months
scanf("%d", p[i].pet_age->months);
printf("\n%d\n", p[i].pet_age->months);
i++; //Incrementing counter
if(i==k){
k+=10;
p=realloc(p, k*sizeof *p); //Size check
}
}
For now there is a basic initialization in the event that this is the first insertion. Then I allocate memory for each element of the structure (to the best of my understanding), and scan every element with a scanf (I pasted some printf checks to see what was actually scanned). Then at the end I increment the i counter, followed by a size check to allocate 10 more places for the array in the event that i==k.
For the sake of continuity, here is my main function as well (basic menu and all):
int i=1; //Counter
int k=10; //Default max entries
int main(int argc, char *argv[]) {
FILE *fp;
int choice;
pet *petarray;
//Menu that lists every option.
while(1){ //Endless loop that ends only if you choose to exit through the 5th option.
printf("\n\n Menu:");
printf("\n=========");
printf("\n1. Insert information for a new pet.");
printf("\n2. Delete a pet record based on pet's ID.");
printf("\n3. Search a pet record based on pet's breed.");
printf("\n4. Update pet's breed name.");
printf("\n5. Exit.\n\n");
scanf("%d", &choice);
switch(choice)
{
case 1:
addPet(petarray);
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
printf("Exiting program...");
exit(0);
}
}
return 0;
}
Apologies if this seems amateur, I'm quite the rookie and still learning. Thanks in advance.
It compiles, but don't you get a long list of warnings? If you don't, you should turn on warnings.
But let's have a look.
void addPet(pet *p)
{
if (i = 1)
You are not comparing (global!) i to 1 here. You are assigning to it. This if statement can only take the true path because of that. When you assign to i, the result of the assignment is the value you assign, so you are testing if (1) here. You want to take this path only if i is 1, I guess, so you should use if (i == 1).
{ //First time activation check.
p = malloc(k * sizeof(p));
Well, it is every time, but we have fixed that now. What do you want p to be, here? An array of k pets? That is not what you are allocating memory for. You are allocating space for k times sizeof(p) and since p is a pet *p, that means you are allocating space for k pointers to pets. Not pets. That, of course, is a problem since p is a pointer to pet and not a pet **. You have most likely allocated too little memory here.
This, unfortunately, is usually not something you will get a warning about. You can give malloc() any size, and it will give you that amount of memory. If you asked for the wrong amount, you get the wrong amount. I think you wanted malloc(k * sizeof *p) here. That allocates space for k of the kind of objects p points to, and that means you can use p as an array of k of that type. You do it the right way when you realloc() later, so this is probably just a quick mistake, but it can easily destroy everything at runtime.
p[i].sex = malloc(sizeof(char) * 1);
p[i].breed = malloc(sizeof(char) * 20);
Two issues here. First, are you sure that p has an entry i? If you fixed the allocation above, then the p you allocated the first time has room for k pets, but this could be any p we have called the function with, so we don't know about this one at all. There is absolutely no guarantee that it is valid to access p[i]. Your reliance on the two global variables will generally make this very dodgy; you simply cannot assume that the function is called with the specific pointer you allocated memory for a bit earlier.
Second, for the string allocation, there are a few red flags as well. sizeof(char) is always 1, so you don't need it. It isn't wrong, really, it just looks odd. And are you absolutely sure that you are allocating enough memory? For p[i].sex I find it highly unlikely. You are getting space for exactly one char. If you only want one char, then that is fine, but you you should probably declare sex a char instead of a char *. If you plan to put a string in p[i].sex, then it will have to be the empty string and nothing longer, because you have only room for the '\0' terminal in a buffer of length 1.
With
p[i].pet_age = malloc(sizeof(int) * 2);
it might technically work, but I don't think the standard guarantees it. You are allocating space for a struct age, and that struct holds two int. They will align the right way, so there shouldn't be any padding, and therefore it should work, but it is flaky as hell.
If you want to allocate space for a struct, then do that. malloc(sizeof(struct age)) gets the job done. Even better, gets the type from the variable you are allocating space for:
p[i].pet_age = malloc(sizeof *(p[i].pet_age));
If p[i].pet_age is a struct age *, then *(p[i].pet_age) is a struct age, and it is the size of that we want.
Then we read in the data.
printf("Insert pet's breed:"); //Scan breed
scanf("%s", p[i].breed);
Here we can have a buffer overflow.
printf("Insert pet's sex:"); //Scan sex
scanf("%s", p[i].sex);
Here we are guaranteed one, because we need to write the terminal zero into sex after we put the data there.
printf("Insert pet's age in years:"); //Scan years
scanf("%d", p[i].pet_age->years);
printf("\n%d\n", p[i].pet_age->years);
printf("Insert pet's age in months:"); //Scan months
scanf("%d", p[i].pet_age->months);
printf("\n%d\n", p[i].pet_age->months);
Since scanf needs to store the data it reads somewhere, it needs a pointer to where it should put it. You are providing integers. (Your compiler definitely should have warned you here). You should use &p[i].pet_age->years to store an integer in p[i].pet_age->years, and the same for months.
Then we get to what I think is probably the worst error in the code.
if (i == k)
{
k += 10;
p = realloc(p, k * sizeof *p); //Size check
}
I'm not going to comment on the global variables again, but rather the local variable. This realloc potentially destroys the memory that p pointed at. I don't care that it can return NULL and you don't check; I doubt that this is happening in your program, but someone called addPet with a pointer, and they have no way of knowing if that pointer is valid again after calling. They have to consider it lost. It won't be freed if addPet() doesn't free it (and it doesn't), and they cannot safely do it themselves. The new memory you allocate doesn't get back to the caller in any way. Assigning to the local variable in addPet() doesn't affect any caller's variable. This realloc() is dangerous. The caller will absolutely lose the existing memory and has no way of obtaining the new memory.
Any of these issues can be the cause of your current problem; the others can be the cause of future problems.
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.
I am trying to implement a priority queue based on binary heap using a static array (I will be later using a linked list, just wanted to test first with an array).
typedef struct n
{
int x;
int y;
int size;
double value;
} node;
node arr[100];
int total = 1;
void insertElement(int x, int y, int size, double value)
{
node n;
n.x = x;
n.y = y;
n.size = size;
n.value = value;
arr[total] = n;
if (total > 1)
insertArrange(total);
total += 1;
}
Now in the delete function i will just return the top most node and delete it, then re-arranging the whole heap. Problem is I can not free any memory. Suppose I use
free(&arr[1]);
I am getting pointer being freed was not allocated error. Is this the proper way of implementation? How to tackle memory issues?
I am using Xcode with Apple LLVM 4.2 compiler. This entire thing will be ultimately put into a bigger project in Objective-C but for now I do not want to use NSMutableArray. I want a simple solution in C.
You only need to call free() if you have used malloc() or calloc(). In fact, attempting to free anything else is undefined behaviour.
As it stands, your code will not be leaking any memory.
Why delete? You could just zero it out and write new data to it whenever you need to. Also My recommendation would be to remember which nodes you delete, so that later when you need to insert a new node, you will know beforehand where the free space is.
For example:
node arr[10];
indexes free_index[10];
//(delete the 6th member of nodes)
delete arr[5];
//remember which one you deleted
free_index[0] = 5;
//later when you add new node you can search the index and pick the first matching value
// zero it out so that it will not be used accidentally again like this
int i = free_index[0] // finding which one is free is task for loops
new_node(arr[i]);
free_index[i] = NULL;
This code example here is very incomplete you have to complete it depending on your own implementation. I just gave you the idea. watch out for free_index [0] = 0; it basically will never match as a valid index. If you zero out indexes with = NULL statement.
There is also a big assumption from my side that you do not wish to shrink the size of this array or grow it. Just empty some elements and then add new ones.
If you want to grow the array you have to calloc it first. I advise calloc because you can allocate array of structs with it.
Growing this is easy with realloc.
But with shrinking you need to create temporary array of nodes where you will store all active results, shrink the original array, put the active results from temporary array back into original and free temporary array.
calloc(numberofnodearrays,sizeof(node));
I am trying to delete an array of initialized structs e.g. reset the array
My struct:
struct entry{
char name[NAME_SIZE];
int mark;
};
typedef struct entry Acct;
Acct dism2A03[MAX_ENTRY];
Acct clear[0]; << temp struct to set original struct to null
My attempt:
entry_total keeps track of how many structs in the struct array dism2A03[x] have values set in them.
I tried to create an empty array of the same struct clear[0]. Looped through initialized arrays in dism2A03[x] and set them to clear[0]
for(m=0;m<entry_total;m++){
dism2A03[m]=clear[0];
}
break;
However, it is setting them to 0, i want them to become uninitialized e.g. no values in them
You cannot have memory with no value in it. It's physically impossible. It's due to the laws of physics of our universe :-)
Also, this:
Acct clear[0];
is wrong. You cannot have an array with zero elements. Some compilers will allow this as an extension, but it's not valid C. And for the compilers that allow this, it doesn't do what you think it does.
It would seem to me that what you want instead is to resize the array. To do that, you would need to copy the elements you want to keep into a new array, and then free() the old one. To do that, you need to create dism2A03 using dynamic memory:
Acct *dism2A03 = malloc(sizeof(Acct) * MAX_ENTRY);
if (dism2A03 == NULL) {
// Error: We're out of memory.
}
(malloc() returns NULL if there's no more free memory, and the code checks that. Usually all you can do if this happens is terminate the program.)
When you want a new array with some elements removed, then you should back up the starting address of the current one:
Acct* oldArray = dism2A03;
then create a new one with the new size you want:
dism2A03 = malloc(sizeof(Acct) * NEW_SIZE);
if (dism2A03 == NULL) {
// Error: We're out of memory.
}
copy the elements you want from the old array (oldArray) to the new one (dism2A03) - which is up to you, I don't know which ones you want to keep - and after than you must free the old array:
free(oldArray);
As a final note, you might actually not want to create a new array at all. Instead, you could keep having your original, statically allocated array ("statically allocated" means you're not using malloc()):
Acct dism2A03[MAX_ENTRY];
and have a index variable where you keep track of how many useful elements are actually in that array. At first, there are 0:
size_t dism2A03_size = 0;
As you add elements to that array, you do that at the position given by dism2A03_size:
dism2A03[dism2A03_size] = <something>
++dism2A03_size; // Now there's one more in there, so remember that.
While doing so, you need to make sure that dism2A03_size does not grow larger than the maximum capacity of the array, which is MAX_ENTRY in your case. So the above would become:
if (dism2A03_size < MAX_SIZE) {
dism2A03[dism2A03_size] = <something>
++dism2A03_size; // Now there's one more in there, so remember that.
} else {
// Error: the array is full.
}
As you can see, adding something to the end of the array is rather easy. Removing something from the end of the array is just as easy; you just decrement dism2A03_size by one. However, "removing" something from the middle of the array means copying all following elements by one position to the left:
for (size_t i = elem_to_remove + 1; i < dism2A03_size; ++i) {
dism2A03[i - 1] = dism2A03[i];
}
--dism2A03_size; // Remember the new size, since we removed one.
Note that you should not attempt to remove an element if the array is empty (meaning when dism2A03_size == 0.)
There's also the case of adding a new elements in the middle of the array rather than at the end. But I hope that now you can figure that out on your own, since it basically a reversed version of the element removal case.
Also note that instead of copying elements manually one by one in a for loop, you can use the memcpy() function instead, which will do the copying faster. But I went with the loop here so that the logic of it all is more obvious (hopefully.)
when you declare an array in this way Acct dism2A03[MAX_ENTRY]; the array is allocated in the stack, therefore it will be removed when the function will perform the return statement.
What you can do is to allocate the structure in the heap via malloc/calloc, and then you can free that memory area via the free function.
For example :
typedef struct entry Acct;
Acct * dism2A03 = calloc(MAX_ENTRY, sizeof( struct entry));
// ....
free(dism2A03);
I've a file which contains names and grades of students, and I'd like to write a program which can sort their grades (like midterm 1,midterm 2) according to user choice. I wrote as far as the choice part and opening the file, yet I don't know how to make program read only certain part of the file (like only Midterm 1 grades for example) and sort them only. Here's what I've wrote so far;
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int number;
char name[30];
char surname[30];
int midterm1,midterm2,midterm3;
} Student;
int main()
{
int choice,studentnumber,midterm1,midterm2,midterm3;
char surname;
FILE *cfPtr;
struct student *name;
name = malloc( 10 * sizeof(Student));
if ((cfPtr = fopen("grades.txt", "r")) == NULL)
printf("File cannot be opened.\n");
else {
const int STUDENTSMAX = 100;
Student students[STUDENTSMAX];
int i = 0;
while (!feof(cfPtr))
{
fscanf(cfPtr, "%d%s%s%d%d%d", &students[i].number, &students[i].name,&students[i].surname, &students[i].midterm1, &students[i].midterm2, &students[i].midterm3);
printf("%4d%15s%15s%10d%10d%10d\n", students[i].number, students[i].name,students[i].surname, students[i].midterm1, students[i].midterm2, students[i].midterm3);
i++;
}
printf("What would you like to do? \n"
"1- Sort according to midterm 1\n"
"2- Sort according to midterm 2\n"
"3- Sort according to midterm 3\n"
"4- Exit\n");
scanf("%d",&choice);
while (choice != 4);{
switch (choice) {
case 1:
qsort(students,10,sizeof(int),comp);
for (i=0; i<9; i++)
printf("%4d%15s%15s%10d%10d%10d\n", students[i].number, students[i].name,students[i].surname, students[i].midterm1);
fclose(cfPtr);
}
system("PAUSE");
return 0;
}
Given what might be a somewhat free form text file (based on the shown code), it probably makes sense just to read the entire file (somewhat like you are already doing) and only use the parts that you need. If the text file has a very specific format with fixed offsets, you could seek to certain locations in the file and read a specific column value, then seek to the next offset and read the column value from the next row. But that is probably more trouble than it is worth and would not be much more efficient (if at all).
Having said that, to sort the results, you probably need the entire file anyway. For example, if you just read and sort the "midterm 1" value, then the result would just be sorted grades without any associated name and student number. So without knowing more about the goal, you might consider creating a struct that can hold a single row (student number, name, surname, midterm1, etc.). Then create an array of those and read each row into an element of the array. If you know how many rows exist up front, you can allocate the array in one chunk, otherwise you might need to reallocate it as you go to grow it.
Once you have read the entire array, you could sort based on the desired value (e.g., with qsort.
Having mentioned that, there a few problems/issues with the existing shown code:
The second printf has fewer format specifiers (%s) than parameters.
The third printf with the "What would you like to do" question is missing the closing paren.
The fprintf is incorrect; it should have a file handle as the first parameter. I suspect, though, that it was maybe meant to be printf?
The final while loop has an extraneous semicolon (;) following its closing paren, which means that it has an empty body rather than the apparently intended printf and switch statement.
The switch statement is a bit odd as written. I assume that is the "unfinished" part. But including the fclose in it seems strange. It should probably be at the end of the main else.
Using system("PAUSE"); is maybe not the best choice. Perhaps using getch would make more sense to pause for input.
Edit Here is some additional information in response to your comment asking for more details. This sounds like homework to me, so it doesn't seem right just to give the answer. But here is one way to do it:
Define a struct with the 6 items that are in the file (basically put in the 6 variables that you currently have defined as local variables).
Declare a local variable (e.g., grades) as a pointer to struct that you defined.
Use malloc to allocate memory and assign it to the pointer just mentioned. The amount of memory is perhaps the trickiest part of this whole thing. The size parameter to malloc will be something like numRecs * sizeof( yourstruct ). The question is what numRecs should be. If this is an assignment and you were told how many records there would be (a maximum), then just use that. If, though, it is "unknown", then there are a couple of ways of dealing with that. One is to just guess at a number (e.g., 100) and then while reading them in a loop use realloc if you exceed 100. The other alternative (probably less efficient) would be to use two loops - read through them once without storing them but just count them and then allocate the known size and read them again. I would use the realloc version.
Replace the use of the local variables with the array (that was malloced). For example, instead of studentnumber you would use grades[arraypos].studentnumber. While you read them in, keep a counter of how many there are. You can then use qsort to sort the array.
Edit 2
Your struct definition looks correct except that the FILE *cfPtr; member should not be in it. It should still be a local variable.
For ease of use, you can define the struct as typedef struct { ... } Student;. That way you can just use Student instead of struct Student in the code. Note that I capitalized the name (personal preference in naming to make it not look like a variable name).
For the malloc, you are close. But as written, it is allocating space for a single record. You would need something like this: name = malloc( 50 * sizeof( struct student )); (or malloc( 50 * sizeof( Student )); if you change it to use the typedef. That assumes there would not be 50 or fewer records to read from the file.