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!
Related
I have to make a program that reserves a seats in a few buses (every bus have a different number of seats). I have to use malloc or calloc. It has to store info about name and surname and assign it to a seat. When seat are already occupied it has to give an info "seat already taken".
It also has to error handling.
I am begginer and I don't know what to do.
#include <stdlib.h>
#define max_name 32
int main()
{
int n_buses,n_seats,zm;
char*** tab;
printf("Number of buses:");
scanf("%d", &n_buses);
tab=calloc(n_buses,sizeof(char**));
for(int i=0;i<n_buses;i++)
{
printf("Number of seats in bus %d: ", i+1);
scanf("%d", &zm);
tab[i]=calloc(zm,sizeof(char*));
}
for(int i=0;i<n_buses;i++)
{
for(j=0;j<max_name;j++)
{
tab[i][j]=calloc(max_name,sizeof(char));
}
}
for(i=0;i<n_buses;i++)
{
if(tab[i]) free(tab[i]);
}
if(tab) free(tab);
return 0;
}```
Take a step back away from this pointer-to-pointer-to-pointer and consider what would make an easy to use data type instead. You have buses, seats and passengers, so it makes sense to come a couple of structs to handle the abstraction for that:
typedef struct
{
char* forename;
char* lastname;
} passenger_t;
typedef struct
{
int total_seats;
passenger_t seat[];
} bus_t;
The last struct uses a trick called flexible array member to allow a variable-sized array at the end of a struct. You'd allocate memory for it as:
int seats = 100;
bus_t* my_bus = calloc( seats, sizeof(*my_bus) + sizeof(passenger_t[seats]) );
if(my_bus == NULL) { /* handle errors */ }
my_bus->total_seats = seats;
Now every seat is represented by an allocated passenger_t item in the seat array, each item with two pointers set to null by calloc (malloc wouldn't set them to null). You can use this to keep track of if a seat is free or taken.
Add a passenger:
if(n >= my_bus->total_seats) // check that input is valid
{
/* error handling */
}
if(my_bus->seat[n].forename != NULL)
{
my_bus->seat[n].forename = ... /* allocate room for forename then strcpy to it*/
my_bus->seat[n].lastname = ... /* allocate room for lastname then strcpy to it*/
}
Note how we only have one array here and one level of indirection, so this code turns out much easier to read and maintain.
How can i create N elements of this struct after the input N?
typedef struct cat{
int code;
int age;
float weight;
enum {kibbles,canned_food,tuna_fish}food;
} cats;
int n,i;
printf("Insert a number: ");
scanf("%d",&n);
for(i=0;i<n;i++){
....
}
I want to create N cats (named cat1,cat2 etc..)
Make a function to input a single struct cat
struct cat inputsinglecat(void);
After you know how many cats you need, get the amount of memory required
struct cat *memcat;
memcat = malloc(n * sizeof *memcat);
if (memcat == NULL) exit(EXIT_FAILURE);
Then, to enter cats, use a loop and the function defined above
for (int k = 0; k < n; k++) {
memcat[k] = inputsinglecat();
}
Don't forget to release the memory when you don't need it anymore
free(memcat);
You can either do it statically, by creating an array of cats:
cats myCatsArray[10];
or dynamically, using malloc or calloc (the latter defined as void *calloc(size_t nitems, size_t size)):
cats *myCatsArray = calloc( 10, sizeof (cats)):
Just avoid the static definition as a local variable of a function, in order to avoid to occupy to much memory in the stack. In case of dynamic allocation you have to remember to free() the structs as soon as you don't need them anymore.
After allocating all the N cats you need, you are able to populate their fields according to your requirements.
In both cases you can access an element (lets say the sixth one) in this way
int myCode = myCatsArray[5].code;
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 writing a program where I need to add students to a array of structs.
Well, at the beginning I have the following struct array:
struct student *students[4];
After the declaration I add students like this to the array (just to have examples ...):
students[0] = malloc(sizeof(struct student));
students[0]->firstname = "Max";
students[0]->secondname = "Taler";
students[0]->number = 123456l;
...
Now I have to write a additional function, where I can pass the array as a parameter and add a new student.
So my function looks like this:
void add_student(struct student *students[],char *fristname, char *secondname, long number)
{
int i=0;
int new_position=0;
int return_value = 0;
// Search for the first available position for a new student
for(i=0; i<sizeof(students); i++)
{
if(students[i]==NULL)
{
new_position=i;
return_value = 1;
break;
}
}
struct student *new_student = malloc(sizeof(struct student));
students[new_position] = new_student;
students[new_position]->fristname = fristname;
students[new_position]->secondname = secondname;
students[new_position]->number = number;
return return_value;
}
I call the function with this code:
add_student(students, "Anni", "Karls", 123232);
But now my issue: In my "add_student" function I get an array that has a strange structur: it includes itself as the first element and every other element is shifted by 1 element.
Cant figure out what the problem is ... Can somebody please help?
EDIT: Somebody asked me, how this can be. How the array can include "itself" as the first element.
Well, here are screenshots of the debugger (xCode):
Before entering "add_student":
After entering "add_student" -> IN the function "add_student":
As you see, "students" has now 5 elements ...
Pass the length of the array to the function and change this
for(i=0; i<sizeof(students); i++)
to this
for(i=0; i<arrayLen; i++)
It seems you are inserting an element to some position in an array which has been allocated before hand; inserting is maybe more proper term than adding, but that is opinion.
You were applying sizeof operator on array type inside function - this will likely give you size of pointer in bytes because array name decays to pointer.
It's a little strange to statically allocate the array but then dynamically allocate the individual structures. You might consider dynamically allocating them all:
int num_students = 4;
struct student **students = malloc(sizeof(struct student) * num_students);
And then change your add_student function to look like this:
void add_student(struct student *students[], int arr_size, char *firstname, char *secondname, long number)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define RECORDS 10
The function below is what I am asking for help with.
static char searchforRecordbystate(char input[3])
{
for / while /if loop
search struct array members
if a match is found
return (print) the entire struct where a match was found
return 0;
}
Main function - first time ever using pointers, (xcode is not complaining with it set to be as strict as possible) but all of you are welcome to complain, especially if I am making a huge oversight.
int main() {
typedef struct {
char *firstName[RECORDS];
char *lastName[RECORDS];
char *street[RECORDS];
char *city[RECORDS];
char *state[RECORDS];
int *zip[RECORDS];
char *phone[RECORDS];
int *accountId[RECORDS];
} Customer ;
typedef int records;
records i = 0;
array loop for data entry
Customer custArray[RECORDS];
printf("================================================================\n");
for(i = 0; i < RECORDS; ++i)
{
printf("Enter data for customer %d\n", i + 1);
printf("Enter firstname, last name, phone\n");
scanf("%s %s %s", *custArray[i].firstName, *custArray[i].lastName, *custArray[i].phone);
printf("Enter Address (Street City State ZIP)");
scanf("%s %s %s*c %d", *custArray[i].street, *custArray[i].city, *custArray[i].state, *custArray[i].zip);
break;
}
char input[3];
printf("Enter in state to search for customer a customer record:\n");
scanf("%s", input);
searchforRecordbystate(input);
}
No error checking necessary, just trying to crawl into learning c at the moment. And there will not be duplicate data in the state member. Hope that makes this easier.
how would I write a search function to look for a match in a struct
array and return (printf) the entire struct it matched?
Declare the struct datatype outside of the function so it's "visible" to the whole module.
Create a function that is able to pretty-print a struct:
void CustomerPrint(const Customer *toPrint) {
...
}
Create a search function that iterates through the array comparing given arguments:
Customer *CustomerFind(const char *name) {
...
}
Connect the two function blocks by calling CustomerFind and in case the result is not NULL call the CustomerPrint function.
Of course the interfaces are only proposal and are subject to be changed. If you've got any questions regarding the details of the proposal leave a comment, I'll explain it in great detail if you like.
Additional thoughts
While rereading my post I realized that some of my decisions I've made in above proposal need an explaination anyway:
In CustomerPrint the pointer taken is `const? because this function is not going to modify any field of the struct. Therefore we tell the compiler that we are not going to change anything.
CustomerFind is expected to have arguments for all searchable fields. (So you are encouraged to extend the signature) I'd propose to take all the "compare" values by pointer and let the caller those pointers be NULL which are not relevant for the search. (e.g. if you have name and city you can leave city NULL in order to only search for the first occurence of name.
The function itself runs through the array of records and compares the fields that are not NULL. In case it finds one, it returns the pointer to that element (return &(myRecords[n]);). If the function comes to the end of the array, it will return NULL to indicate no record matched.
There is also a concept you can introduce if you want to have "search - search next" capabilities. Let me know if you are intrested in a concept for that too.
typedef struct {
char firstName[NAMEMAX];
char lastName[NAMXMAX];
char street[STREETMAX];
char city[CITYMAX];
char state[STATEMAX];
int zip;
char phone[PHONEMAX];
int accountId;
} Customer ;
Customer Customers[RECORDS];
static int searchforRecordbystate(char input[]) {
for (int i = 0; i < RECORDS; i++) {
if (strcmp(input, Customers[i].state) == 0) {
printCustomer(Customers[i]);
return i;
}
}
return -1; // Not found
}
Writing printCustomer() is an exercise for the reader.