How do i create multiple distinguishable members in a struct - arrays

I need to store multiple distinguishable members in a struct for a personal diary to show different records for each day. I want multiple members like e1 without writing it 365 times. I want a solution that works e1 on a loop or on an array.
struct add
{
char day[365];
char date[365];
char time[365];
char place[365];
char data[365];
};
int main()
{
int i=1;
struct add e1;
while(i!=0)
{
printf("Enter Day:");
gets(e1.day);
printf("Enter Date:");
gets(e1.date);
printf("Enter Time:");
gets(e1.time);
printf("Enter Place:");
gets(e1.place);
printf("Tell me about your day:");
gets(e1.data);
printf("\n\n\n");
printf("Day: %s\n",e1.day);
printf("Day: %s\n",e1.date);
printf("Day: %s\n",e1.time);
printf("Day: %s\n",e1.place);
printf("Day: %s\n\n\n",e1.data);
}
return 0;
}

I don't think its possible to create different variable with each loop, you can refer to this question for info. Instead of making different instances, make your struct in a way so that it stores data for all (365) days. Something like array of strings so that it stores for all days, currently even if you write something like char day[365] it creates separate day for each instance. make something like:
struct add
{
char day[NUMBER_OF_STRING][MAX_STRING_SIZE]; // syntax
// MAX_STRING_SIZE does not need to be 365, i just used it because you used that
// otherwise it does not make sense, date , time etc won't take more than 10 char
char date[365][365];
char time[365][365];
char place[365][365];
char data[365][365];
};
Now keep on adding to a single instance of struct add using for loop, this should be easy.
Also Better approach can be to create a array of struct add itself with your original struct code.

This example does what you want, and you can create as many instance of add as you want.
I limit your loop to run once and have tested that the code works with visual studio 2019:
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#define DAY_SIZ 20
#define DATE_SIZ 40
#define TIME_SIZ 20
#define PLACE_SIZ 120
#define DATA_SIZ (BUFSIZ - 1)
struct add
{
char day[DAY_SIZ];
char date[DATE_SIZ];
char time[TIME_SIZ];
char place[PLACE_SIZ];
char data[DATA_SIZ];
};
struct add* create_add_struct() {
struct add* rec;
rec = (struct add*)malloc(sizeof(struct add));
return rec;
}
void destroy_add_struct(struct add* rec) {
free(rec);
}
void clear_add_struct(struct add* rec) {
memset(rec->day, 0, sizeof(rec->day));
memset(rec->date, 0, sizeof(rec->date));
memset(rec->time, 0, sizeof(rec->time));
memset(rec->place, 0, sizeof(rec->place));
memset(rec->data, 0, sizeof(rec->data));
}
void get_text_from_user(const char prompt[], char *txt, int siz) {
char buf[BUFSIZ];
printf("%s", prompt);
fgets(buf, BUFSIZ, stdin);
memcpy(txt, buf, siz);
}
void fill_add_struct(struct add* rec) {
printf("Do not enter any lines longer than %d\n\n", BUFSIZ);
get_text_from_user("Enter Day:", rec->day, sizeof(rec->day) - 1);
get_text_from_user("Enter Date:", rec->date, sizeof(rec->date) - 1);
get_text_from_user("Enter Time:", rec->time, sizeof(rec->time) - 1);
get_text_from_user("Enter Place:", rec->place, sizeof(rec->place) - 1);
get_text_from_user("Tell me about your day:", rec->data, sizeof(rec->data) - 1);
}
void print_add_struct(struct add* rec) {
printf("\n\n\n");
printf("Day : %s\n", rec->day);
printf("Date : %s\n", rec->date);
printf("Time : %s\n", rec->time);
printf("Place: %s\n", rec->place);
printf("Data : %s\n\n\n", rec->data);
}
int main()
{
// The number of times we want the loop to run
int i = 1;
// Collect data from the user and print it
while (i-- != 0)
{
// Allocate memory and initialize it
struct add* rec = create_add_struct();
clear_add_struct(rec);
// Get data from the user and print it
fill_add_struct(rec);
print_add_struct(rec);
// Release memory
destroy_add_struct(rec);
}
// Return success
return 0;
}
The reason I allocate your structure, is to make sure a stack-overflow does not occur, no pun intended.
If you want to create 365 add structs, you can create them like so:
struct add **records;
records = (struct add **) malloc(sizeof(struct add *) * 365);
for(int idx = 0; idx < 365; ++idx) {
records[idx] = create_add_struct();
clear_add_struct(records[idx]);
/* ... insert code ... */
}
/* ... insert code ... */
// Free memory
for(int idx = 0; idx < 365; ++idx) {
destroy_add_struct(records[idx]);
}
free(records);
I have not tested if the last piece of code compiles and works. Again I use malloc() and free() to avoid a stack-overflow.

typedef struct
{
time_t tm;
char *description;
}entry;
typedef struct day
{
time_t date;
size_t nentries;
struct day *next;
entry entries[];
}day;
and create linked list od days. Every day has array of diary entries.

Related

Sorting structures from files

I recently got an assignment to sort members in a struct by last name and if they are the same to sort by first name. What i have so far only reads their name and age from the file but I am not properly grapsing how I would be able to sort it. So far I gathered the data from the file but im at a loss from there. I followed a code I saw but i didnt get a proper grasping of the process so i reverted back to step one.
struct Members{
int id;
char fname[50];
char lname[50];
int age;
}bio;
int main(){
int i=0;
FILE *fptr;
file = fopen("Members Bio.txt", "r");
while ( fscanf(file, "%d%s%s%d", &bio[i].id,bio[i].fname,bio[i].lname,&bio[i].age) != EOF)
{
printf("%d %s %s %d %d\n", bio[i].id,bio[i].fname, bio[i].lname, bio[i].age);
i++;
}
fclose(fptr);
}
Can anyone help me out on this one?
Code goes something like this for your case.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Members{
int id;
char fname[50];
char lname[50];
int age;
};
typedef int (*compare_func)(void*, void*);
int struct_cmp(void* s1, void* s2)
{
int l_result = strcmp(((struct Members*) s1)->lname, \
((struct Members*) s2)->lname);
if (l_result < 0)
return 1;
else if (l_result > 0)
return 0;
else
return (strcmp(((struct Members*) s1)->fname, \
((struct Members*) s2)->fname) < 0 ? 1 : 0);
}
void sort(void* arr,long ele_size,long start,long end,compare_func compare)
{
// Generic Recursive Quick Sort Algorithm
if (start < end)
{
/* Partitioning index */
void* x = arr+end*ele_size;
long i = (start - 1);
void* tmp=malloc(ele_size);
for (long j = start; j <= end - 1; j++)
{
if ((*compare)(arr+j*ele_size,x))
{
i++;
// Swap is done by copying memory areas
memcpy(tmp,arr+i*ele_size,ele_size);
memcpy(arr+i*ele_size,arr+j*ele_size,ele_size);
memcpy(arr+j*ele_size,tmp,ele_size);
}
}
memcpy(tmp,arr+(i+1)*ele_size,ele_size);
memcpy(arr+(i+1)*ele_size,arr+end*ele_size,ele_size);
memcpy(arr+end*ele_size,tmp,ele_size);
i= (i + 1);
sort(arr,ele_size,start, i - 1,compare);
sort(arr,ele_size,i + 1, end,compare);
}
}
int main()
{
FILE* fp;
int bio_max = 3;
struct Members bio[bio_max]; // Define bio to be large enough.
/* Open FILE and setup bio matrix */
/* For testing */
bio[0].id = 0;
strcpy(bio[0].fname, "");
strcpy(bio[0].lname, "Apple");
bio[0].age = 0;
bio[1].id = 1;
strcpy(bio[1].fname, "");
strcpy(bio[1].lname, "Cat");
bio[1].age = 1;
bio[2].id = 2;
strcpy(bio[2].fname, "");
strcpy(bio[2].lname, "Bat");
bio[2].age = 2;
/* Sort the structure */
sort(bio, sizeof(struct Members), 0, bio_max - 1, struct_cmp);
/* Print the sorted structure */
for (int i = 0; i < bio_max; i++) {
printf("%d %s %s %d\n", bio[i].id, bio[i].fname, \
bio[i].lname, bio[i].age);
}
}
Output
0 Apple 0
2 Bat 2
1 Cat 1
If the strings are not sorting in the way you want, you can redefine the struct_cmp function. Code is self explanatory, the base logic in the code is pass an array and swap elements using memcpy functions. You cant use simple assignment operator if you want to be generic, so that is why the element size is explicitly passed.
Edit
The code was not handling the condition, if lname are same. I missed it thanks for #4386427 for pointing this out.
I think you should define bio to be an array. And google sort algorithms please. Also recommend you google how to use libc function qsort.

Can you put a dynamic pointer to a regular array in C

I am trying to code a scheduler in C for practice. Currently my issue is that I am trying to put the "student" object that i created into an array of students called "employees". So i create the student, get its schedule and then dereference the *student and put it in employees[i] array. But when I print the names in the employees array it prints the last entered name repeatedly. I also tried making employee array a pointer (Student *employee) but got the same problem. Any help is appreciated. Thank You
______________________________________________________________________________
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define NUM_SLOTS 24
#define NUM_EMPLOYEES 5
#define BUFF 25
#pragma warning(disable : 4996)
typedef struct Student Student;
typedef struct StudentDay StudentDay;
typedef struct Slot Slot;
//array of strings of all the timeslots
char *timeSlots[NUM_SLOTS] = { "08:00-08:30", "08:30-09:00", "09:00-09:30" , "09:30-10:00" , "10:00-10:30" , "10:30-11:00" , "11:00-11:30" , "11:30-12:00",
"12:00-12:30", "12:30-13:00", "13:00-13:30", "13:30-14:00", "14:00-14:30", "14:30-15:00", "15:00-15:30", "15:30-16:00", "16:00-16:30",
"16:30-17:00", "17:00-17:30", "17:30-18:00", "18:00-18:30", "18:30-19:00", "19:00-19:30", "19:30-20:00"
};
char *daysOfWeek = "MTWRFS";
//data structure represents a day for a student
typedef struct StudentDay {
char name;
//binary slots representing 1 for available -1 for unavailable
int slots[NUM_SLOTS];
};
//data structure represents a student
typedef struct Student {
char *name;
//array of 24 half hour times starting 8am - 8pm
StudentDay week[6];
};
//data structure represents a time slot in a day
typedef struct Slot{
char *time;
char *personAssigned;
int done; // 1 if done assinging
};
//data structure represents a day for coach will
typedef struct WorkDay {
char *name;
Slot slots[NUM_SLOTS];
};
int main() {
printf("Collin College Fitness Center Scheduling System\n");
int numEmployees;
printf("Please enter number of employees\n");
scanf("%d", &numEmployees);
//create an array of student employees
Student employees[BUFF];
//enter which day are you scheduling for
printf("What day are you wanting to schedule, Enter M-Mon, T-Tue, W-Wed, R-Thu, F-Fri, S-Sat?\n");
char day = getchar();
//will possibly need another loop for all siz days right now just one day ex. monday M
for (int i = 0; i < numEmployees; i++) {
Student *student = malloc(sizeof(Student));
//setting name for student
char name[BUFF];
printf("Please enter name for emplyee number %d\n", i + 1);
fgets(name, sizeof(name), stdin);
student->name = name;
printf("Enter the student's class times in military format Example 13:00-14:30 and press enter, input Done when done\n");
//time stores the input class time by coach
char time[BUFF] = "";
while (strcmp(time, "done") != 1 || strcmp(time, "Done") != 1) {
fgets(time, sizeof(time), stdin);
char *temp = time;
//get the index of the class' time slot
int indexOfSlot = getIndexOfString(timeSlots, temp);
//get index of the day we are scheduling for
int dayIndex = getIndexOfChar(daysOfWeek, day);
//for this students particular days' this time slot is made unavailable
student->week[dayIndex].slots[indexOfSlot] = -1;
}
//put this students info in the employees array
employees[i] = *student;
}
for (int k = 0; k < 2; k++) {
printf("employee stored is %s", employees[k].name);
}
system("pause");
return 0;
}
//helper method to get the index of a string in an array
int getIndexOfString(char*names[NUM_SLOTS], char *name) {
for (int i = 0; i < NUM_SLOTS; i++) {
if (strcmp(names[i], name) == 0) {
return i;
}
}
return 0;
}
//helper method to get te index of char in a string
int getIndexOfChar(char *name, char c) {
char *temp = name;
int i = 0;
while (*temp != '\0') {
if (*temp == c) {
return i;
}
temp++;
i++;
}
return 0;
}
Array name is created locally and overwritten on every iteration. You need to allocate a separate string in the heap for every student.
Replace this:
char name[BUFF];
...
fgets(name, sizeof(name), stdin);
With this:
char* name = malloc(sizeof(char)*BUFF);
...
fgets(name, BUFF, stdin);

Working With Stacks in C

After many hours of working on trying to debug this code myself I'm giving up an seeking help. I created a program that is designed to hold a "record", a struct, of my choice and show I know how to utilize/manipulate them in memory. In my code I made a car inventory that allows you to add, delete, and print out structs. My struct looks like the following:
struct car{
char *make;
char *model;
int year;
int mpg;
int mileage;
int cost;
};
I use a menu system of number that allows the user to choose what to do with the struct. Anyways here's where I'm having the issue:
I can add two struct and print them all out just fine. However if I add more than two structs than I run into a seg fault. I've already tried to run GDB on it and I get the following error:
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7a60a03 in _IO_vfprintf_internal (s=<optimized out>, format=<optimized out>,
ap=<optimized out>) at vfprintf.c:1661
1661 vfprintf.c: No such file or directory.
As for how I am taking in this data I have 6 scanf() functions that take in the 6 characteristics of the struct, store them to a temporary variable, malloc for each string, malloc a new car struct called newCar, and set each variable inside struct to its temporary variable. If it's the first car entry then carInventory is equal to newCar. When adding another car the same process occurs except a while loop moves along a new struct called newCarInventory and stores data from carInventory using memcpy(). Below is the actual code I'm using:
void addRecord(){
newCar = (struct car *) malloc(sizeof(struct car)); //Allocates memory for a car entry
int i;
int inventoryAmountTemp = inventoryAmount; //Grabs the global value and stores it to a local variable
//Local variables used to hold onto inputted values
char make[25];
char model[25];
int year;
int mpg;
int mileage;
int cost;
printf("\nMake: ");
scanf("%s", &make);
printf("\nModel: ");
scanf("%s", &model);
printf("\nYear: ");
scanf("%i", &year);
printf("\nMiles Per Gallon: ");
scanf("%i", &mpg);
printf("\nMileage: ");
scanf("%i", &mileage);
printf("\nCost: ");
scanf("%i", &cost);
newCar->make = (char *)malloc(strlen(make)+2); //Allocates memory for string
newCar->model = (char *)malloc(strlen(model)+2);
strcpy(newCar->make, make); //Coppies string into newCar
strcpy(newCar->model, model);
newCar->year = year;
newCar->mpg = mpg;
newCar->mileage = mileage;
newCar->cost = cost;
if(inventoryAmountTemp == 0){
carInventory = newCar;
}
else{
newCarInventory = (struct car *) malloc(inventoryAmountTemp * sizeof(struct car)); //Memory made for a new Car Inventory.
headPtr = carInventory;
headPtr2 = newCarInventory;
tailPtr = newCar;
i = 0;
while(i <= inventoryAmountTemp){
memcpy(&(newCarInventory->make), &(headPtr->make), sizeof(headPtr->make));
memcpy(&(newCarInventory->model), &(headPtr->model), sizeof(headPtr->model));
memcpy(&(newCarInventory->year), &(headPtr->year), sizeof(headPtr->year));
memcpy(&(newCarInventory->mpg), &(headPtr->mpg), sizeof(headPtr->mpg));
memcpy(&(newCarInventory->mileage), &(headPtr->mileage), sizeof(headPtr->mileage));
memcpy(&(newCarInventory->cost), &(headPtr->cost), sizeof(headPtr->cost));
i++;
if(i < inventoryAmountTemp && i != inventoryAmountTemp){
headPtr++;
newCarInventory++;
}
else if(i == inventoryAmountTemp){
headPtr = tailPtr;
newCarInventory++;
}
}
newCarInventory = headPtr2;
carInventory = newCarInventory;
}
inventoryAmountTemp += 1;
accessTemp += 1;
access = accessTemp;
inventoryAmount = inventoryAmountTemp;
}
I know the issue resides from this function because I had fprintf() statements (that I took out in this code) that would test each step. The ones that would fail were the ones inside the while loop the as the loop went around the second time, of the third car entry.
Any guidance would be really appreciated!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
struct car{
char *make;
char *model;
int year;
int mpg;
int mileage;
int cost;
};
int inventoryAmount=0;
struct car* CarList=0;
char *copyString(char *str){
char *buff=0;
if(str){
int l=strlen(str);
buff=malloc(l+1);
if(buff){
for(int i=0;i<=l;i++)
buff[i]=str[i];
}
}
return buff;
}
/*_______________________________________________________
*/
void propmt(const char *msg,const char*fmt,void *output){
printf("\n%s: ",msg);
scanf(fmt,output);
return;
}
/*_______________________________________________________
*/
void addRecord(void){
struct car *newCar ;
//Local variables used to hold onto inputted values
char make[25];
char model[25];
int year;
int mpg;
int mileage;
int cost;
if(CarList){
newCar= realloc(CarList,sizeof(struct car)*(inventoryAmount+1));
}else{
newCar= malloc(sizeof(struct car));
}
if(!newCar){
printf("Failed to allocate new car\n");
exit(1);
}
CarList=newCar;
newCar=&newCar[inventoryAmount];
inventoryAmount++;
propmt("Make","%s", &make);
propmt("Model","%s", &model);
propmt("Year","%i", &year);
propmt("Miles Per Gallon","%i", &mpg);
propmt("Mileage","%i", &mileage);
propmt("Cost","%i", &cost);
newCar->make=copyString( make); //Coppies string into newCar
newCar->model=copyString( model);
newCar->year = year;
newCar->mpg = mpg;
newCar->mileage = mileage;
newCar->cost = cost;
return;
}
/*_______________________________________________________
*/
void listCars(void){
int i;
if(!inventoryAmount){
printf("\nNo cars in inventory\n");
return;
}
for(i=0;i<inventoryAmount;i++){
printf("Make: %s\n" ,CarList[i].make);
printf("Model:%s\n" ,CarList[i].model);
printf("Year:%i\n" ,CarList[i].year);
printf("Miles Per Gallon:%i\n" ,CarList[i].mpg);
printf("Mileage:%i\n" ,CarList[i].mileage);
printf("Cost:%i\n" ,CarList[i].cost);
printf("======================================\n");
}
return;
}
void pause(void){
printf("Press a key to continue");
_getch();
printf("\n");
}
int main(void){
int q;
for(;;){
_clrscr();
printf(
"1 - List cars\n"
"2 - Add new car\n"
"0 - Exit program\n");
scanf("%i",&q);
switch(q){
case 1:
listCars();
pause();
break;
case 2:
addRecord();
pause();
break;
case 0:
return 0;
break;
}
}
return 0;
}
a good sample of using malloc and realloc
Try using backtrace in gdb to see where the segmentation fault originates

Overlapping and too long integer values in dynamic c structs

I have the following problem.
I need to create a list of savestates with dynamical length. That's why I decided to create a list by defining some structs and connecting dynamically created structs together to build a list of structs which can dynamically be extended and so on.
However, some things seem to not work at all. Here's the relevant code first:
saves.h:
#ifndef SAVES_H
#include<time.h>
#define SAVES_H
#define SVS_STRLEN 500
#define SVS_FILE "savefile.dat"
#define True 1
#define False 0
typedef struct SVS_STATE SVS_STATE;
typedef struct SVS_STATES SVS_STATES;
struct SVS_STATE {
int i_playfield[6][7];
int i_turn;
time_t i_time;
void *next;
};
struct SVS_STATES {
SVS_STATE *states;
int count;
int loaded;
};
void SVS_Add_State(int i_playfield[][7], int i_turn, time_t i_time);
void SVS_Debug_State(SVS_STATE *state);
void SVS_Format_State(SVS_STATE *state, char text[]);
SVS_STATE *SVS_Get_State(int number);
#endif
saves.c:
#include "saves.h"
#include<string.h>
#include<time.h>
SVS_STATE *SVS_Get_State(int number)
{
int i = 1;
SVS_STATE *state;
if (svs_current_state.loaded == False) return NULL;
if (number > svs_current_state.count) return NULL;
state = svs_current_state.states;
printf("printing state 1:");
SVS_Debug_State(state);
while( i < number)
{
i++;
state = (SVS_STATE*)(state->next);
printf("printing state %i:", i);
SVS_Debug_State(state);
}
return state;
}
void SVS_Format_State(SVS_STATE *state, char text[])
{
int i, j;
if (svs_current_state.loaded == False) return;
text[0] = '\0';
strcat(text, "{\0");
for (i = 0; i < X_SIZE; i++)
{
strcat(text, "{\0");
for(j = 0; j < Y_SIZE; j++)
{
strcat(text, "%i,\0");
sprintf(text, text, state->i_playfield[i][j]);
}
strcat(text, "}\0");
}
strcat(text, "};%i;%i\n\0");
sprintf(text, text, state->i_turn, state->i_time);
printf("\nFormatted state:%s\n", text);
}
void SVS_Debug_State(SVS_STATE *state)
{
char text[SVS_STRLEN];
SVS_Format_State(state, text);
printf("%s\n", text);
}
void SVS_Add_State(int i_playfield[][7], int i_turn, time_t i_time)
{
int i, j;
SVS_STATE *laststate, *newstate;
newstate = (SVS_STATE*)malloc(sizeof(SVS_STATE));
printf("adding state with time:%i\n", i_time);
if (svs_current_state.loaded == False) return;
for (i = 0; i < 6; i++)
for (j = 0; j < 7; j++)
newstate->i_playfield[i][j] = i_playfield[i][j];
newstate->i_turn = i_turn;
newstate->i_time = i_time;
newstate->next = NULL;
printf("initialized state:");
SVS_Debug_State(newstate);
if (svs_current_state.coun > 0)
{
laststate = SVS_Get_State(svs_current_state.count);
laststate->next = (void*)newstate;
} else
svs_current_state.states=newstate;
svs_current_state.count++;
}
int main()
{
int i_playfield[6][7] = {0};
// mark saves library as loaded here, but removed function, since it
// just sets svs_current_state.loaded (which is the global struct of
// type SVS_STATES) to 1
SVS_Add_State(i_playfield, 1, time(NULL));
i_playfield[0][0] = 2;
SVS_Add_State(i_playfield, 2, time(NULL));
return 0;
}
The actual problems I encountered while using the printf's and Debug_State calls in these functions:
- the i_time I give is printed out once in Add_State(), correctly. Means it is a legal time and stuff, but when printed out after creating the full state by using Format_State() the string is 50 percent to long and the last part is displayed twice, for example:
if the time is 12345678, it is displayed correctly while debugging in Add_State, but Format_State() displays 123456785678.
- second problem: the first state added works, more or less, fine. But after adding a second one, printing the first state (retrieved by using Get_State and formatted with Format_State) prints a mixture of two states, for example something like this:
state 1: {{0,0,0,0,0,0,0}{0,0,0,0,0,0,0}{0,0,0,0,0,0,0}...
{0,0,0,0,0,0}};1;123456785678
state 2: {{0,0,0,0,0,0}{0,0,0,0,0,0}...
{0,0,0,0,0,0}};2;1234567856785678,0}{0,0,0,0,0,0}...
Thanks for reading.
These calls
sprintf(text, text, ...
invoke undefined behaviour, as the target buffer and one of the other arguments overlap.
From the POSIX specs to sprintf():
If copying takes place between objects that overlap as a result of a call to sprintf() [...], the results are undefined.

structs and qsort not working when sorting time

I dont know where I 'm going wrong. Code below is expected to accept user input of olympic swimmers' fname, lname country and finishing time and qsort the result on the fastest time as below;
**Sample Input**
ADLINGTON Rebecca GBR 4:03.01
MUFFAT Camille FRA 4:01.45
FRIIS Lotte DNK 4:03.98
SCHMITT Allison USA 4:01.77
**Sample Output**
MUFFAT Camille FRA 4:01.45
SCHMITT Allison USA 4:01.77
ADLINGTON Rebecca GBR 4:03.01
FRIIS Lotte DNK 4:03.98
struct mtime_t {
int mins;
int secs;
int fsecs;
};
typedef struct mtime_t mtime;
struct olympians {
char fname[15];
char lname[15];
char country[3];
int time;
int mins, secs, fsecs;
mtime otime;
};
/* qsort struct comparision function (time float field) */
int struct_cmp_by_time(const void *a, const void *b)
{
struct olympians *ia = (struct olympians *)a;
struct olympians *ib = (struct olympians *)b;
return (int)(60*ia->time - 60*ib->time);
}
/* struct array printing function */
void print_struct_array(struct olympians *array, size_t len)
{
size_t i;
for(i=0; i<len; i++)
printf("%s %s %s \t %d:%d,%d\n", array[i].lname, array[i].fname,
array[i].country, &array[i].otime.mins,
&array[i].otime.secs, &array[i].otime.fsecs);
puts("\n");
}
/* sorting structs using qsort() */
void sort_structs_time()
{
int i, ath_num;
struct olympians *ath_recs;
scanf("%d", &ath_num);
ath_recs = (struct olympians *) malloc(ath_num*sizeof(struct olympians));
for(i = 0; i < ath_num; i++) {
scanf("%s %s %s %d:%d,%d\n", ath_recs[i].fname, ath_recs[i].lname,
ath_recs[i].country, &ath_recs[i].mins, &ath_recs[i].secs, &ath_recs[i].fsecs);
}
puts("\n");
/* print original struct array */
print_struct_array(ath_recs, ath_num);
/* sort array using qsort function */
qsort(ath_recs, (size_t) ath_num, sizeof(struct olympians), struct_cmp_by_time);
/* print sorted struct array */
print_struct_array(ath_recs, ath_num);
}
/* call to the function) */
int main()
{
/* run the function */
sort_structs_time();
return 0;
}
One problem is that:
char country[3];
Is not large enough to hold GBR or any 3 character string as an additional character is required for the null terminator, which will be appended by scanf(). This will result in memory being written to that is not supposed to, causing undefined behaviour. Change to:
char country[4];
Recommend restricting the number of characters read by scanf() to prevent buffer overruns and checking the return value of scanf() to ensure all expected assignments were made:
if (6 == scanf("%14s %14s %3s %d:%d,%d\n", ...
Note that the format of the time in the input is 4:03.01 but in the scanf() format specifier , is used to separate the last two ints. Change to:
if (6 == scanf("%14s %14s %3s %d:%d.%d\n", ...
Another problem is that your input and output formats use , as the decimal point; your sample data uses . as the decimal point.
And another problem is that you don't set the time field to any value during input. I'd probably go with:
for (i = 0; i < ath_num; i++)
{
if (scanf("%s %s %s %d:%2d,%2d\n", ath_recs[i].fname, ath_recs[i].lname,
ath_recs[i].country, &ath_recs[i].mins, &ath_recs[i].secs,
&ath_recs[i].fsecs) != 6)
break;
ath_recs[i].time = (ath_recs[i].mins * 60 + ath_recs[i].secs) * 100 +
ath_recs[i].fsecs;
}
If I was being paranoid, I'd ensure that the minutes, seconds and fractional seconds were all zero or positive (non-negative). And I'd probably write the code to read a whole line into a buffer with fgets() and then parse with sscanf(); it makes error detection easier.
char buffer[4096];
int i = 0;
while (fgets(buffer, sizeof(buffer), stdin))
{
if (i >= ath_num)
...too much data...
if (sscanf(buffer, "%s %s %s %d:%2d,%2d\n", ath_recs[i].fname, ath_recs[i].lname,
ath_recs[i].country, &ath_recs[i].mins, &ath_recs[i].secs,
&ath_recs[i].fsecs) != 6)
{
...report problems...but I've got the whole line to identify which record
...is giving the problem — which helps you and the user...
break;
}
...other checking...
ath_recs[i].time = (ath_recs[i].mins * 60 + ath_recs[i].secs) * 100 +
ath_recs[i].fsecs;
i++;
}
...actual number of athletes is in i...
It would be better not to make the user do the counting of the data; computers are good at counting. You just have to allocate the array as you go, which requires a modicum of care to avoid quadratic behaviour.
Then, in the comparison code, there's no need to multiply by 60:
/* qsort struct comparision function (time float field) */
int struct_cmp_by_time(const void *a, const void *b)
{
const struct olympians *ia = (struct olympians *)a;
const struct olympians *ib = (struct olympians *)b;
if (ia->time > ib->time)
return +1;
else if (ia->time < ib->time)
return -1;
...else if ... tie-breaker decisions - name? ...
return 0;
}
I use the structure shown for comparator functions because it is extensible, and because it avoids problems with arithmetic overflow. In this application, it is very unlikely that the difference between two times will ever cause problems, but avoiding trouble is still a good idea, and subtracting two integers could lead to overflow (wraparound) in general.
In your code:
printf("%s %s %s \t %d:%d,%d\n", array[i].lname, array[i].fname,
array[i].country, &array[i].otime.mins,
&array[i].otime.secs, &array[i].otime.fsecs);
My compiler complains about the & in front of mins, secs and fsecs. If your compiler isn't doing that, turn up the warning levels until it does, or get a better compiler.
Your athlete printing code uses the otime sub-structure of struct olympians, but your code doesn't set it (and it duplicates the values in the separate members mins, secs, fsecs). That threw me off track for a bit while debugging your code. This code works. It includes a separate little function print_athlete() (which should probably be print_olympian()) to print a single athlete, and the print_struct_array() code uses it — but I was also able to use it while working out why the input data wasn't being printed in the output (that call is still in the code). Echoing input after reading it is a basic debugging technique. The code also checks that malloc() succeeded. (I usually have a function such as void dump_olympian(FILE *fp, const char *tag, const struct olympian *athlete); to print a complex structure to a nominated file. The tag is printed too, which allows me to annotate each call to to the dump function. The dump function should normally dump every element of the structure.)
In production code, I have a set of functions such as extern void err_error(const char *fmt, ...); which report an error with an interface like the printf() functions; err_error() exits too. This reduces the 4 lines of error reporting to just 1, which means I'm more likely to do it.
For the input data (note switch of . and ,):
4
ADLINGTON Rebecca GBR 4:03,01
MUFFAT Camille FRA 4:01,45
FRIIS Lotte DNK 4:03,98
SCHMITT Allison USA 4:01,77
The output is:
Processing 4 athletes
Rebecca ADLINGTON GBR 4:3,1
Camille MUFFAT FRA 4:1,45
Lotte FRIIS DNK 4:3,98
Allison SCHMITT USA 4:1,77
Rebecca ADLINGTON GBR 4:3,1
Camille MUFFAT FRA 4:1,45
Lotte FRIIS DNK 4:3,98
Allison SCHMITT USA 4:1,77
Camille MUFFAT FRA 4:1,45
Allison SCHMITT USA 4:1,77
Rebecca ADLINGTON GBR 4:3,1
Lotte FRIIS DNK 4:3,98
The first block is from the debug printing in the input loop; the other two are the before and after images, of course, from your original code.
Unfixed issue
There's one problem you'll need to resolve (in two parts). The easy bit is that the output:
Rebecca ADLINGTON GBR 4:3,1
should be
Rebecca ADLINGTON GBR 4:3,01
or even:
Rebecca ADLINGTON GBR 4:03,01
This is fixed by using %.2d instead of %d in the print formats.
The hard part is that if the input time string is:
4:03,1
it needs to be treated as 4:03,10 and not as 4:03,01. Worse, 4:3,9 should be 4:03,90, not 4:03,09; that gives the athlete a 0.81 second advantage in the sorting, simply because the trailing zero was omitted (so it really does matter). This will require different input code; I might even go so far as to read the fraction part into a string of length 3 with %2[0-9], and then post-process that into a number. That way you can tell whether one or two digits were entered.
Incidentally, you could avoid the conversion to time altogether by sorting directly on the component parts, and then the systematic structure of the comparator becomes beneficial:
int struct_cmp_by_time(const void *a, const void *b)
{
const struct olympians *ia = (struct olympians *)a;
const struct olympians *ib = (struct olympians *)b;
int rc;
if (ia->mins > ib->mins)
return +1;
else if (ia->mins < ib->mins)
return -1;
else if (ia->secs > ib->secs)
return +1;
else if (ia->secs < ib->secs)
return -1;
else if (ia->fsecs > ib->fsecs)
return +1;
else if (ia->fsecs < ib->fsecs)
return -1;
else if ((rc = strcmp(ia->lname, ib->lname)) < 0)
return -1;
else if (rc > 0)
return +1;
else if ((rc = strcmp(ia->fname, ib->fname)) < 0)
return -1;
else if (rc > 0)
return +1;
return 0;
}
(On the whole, you were pretty close to getting your code right — well done.)
Mildly hacked but working code
Not all the recommended changes are in this code, but it produces reasonable output, more or less.
#include <stdio.h>
#include <stdlib.h>
struct mtime_t {
int mins;
int secs;
int fsecs;
};
typedef struct mtime_t mtime;
struct olympians {
char fname[15];
char lname[15];
char country[4];
int time;
int mins, secs, fsecs;
mtime otime;
};
/* qsort struct comparison function (time float field) */
int struct_cmp_by_time(const void *a, const void *b)
{
struct olympians *ia = (struct olympians *)a;
struct olympians *ib = (struct olympians *)b;
return (int)(60*ia->time - 60*ib->time);
}
static void print_athlete(const struct olympians *ath)
{
printf("%s %s %s \t %d:%d,%d\n", ath->lname, ath->fname, ath->country,
ath->mins, ath->secs, ath->fsecs);
}
/* struct array printing function */
void print_struct_array(struct olympians *array, size_t len)
{
size_t i;
for(i=0; i<len; i++)
print_athlete(&array[i]);
puts("\n");
}
/* sorting structs using qsort() */
void sort_structs_time(void)
{
int i, ath_num;
struct olympians *ath_recs;
scanf("%d", &ath_num);
printf("Processing %d athletes\n", ath_num);
ath_recs = (struct olympians *) malloc(ath_num*sizeof(struct olympians));
if (ath_recs == 0)
{
fprintf(stderr, "Out of memory\n");
exit(1);
}
for(i = 0; i < ath_num; i++) {
if (scanf("%14s %14s %3s %d:%2d,%2d\n", ath_recs[i].fname, ath_recs[i].lname,
ath_recs[i].country, &ath_recs[i].mins, &ath_recs[i].secs, &ath_recs[i].fsecs) != 6)
{
fprintf(stderr, "Ooops\n");
exit(1);
}
ath_recs[i].time = (ath_recs[i].mins * 60 + ath_recs[i].secs) * 100 +
ath_recs[i].fsecs;
print_athlete(&ath_recs[i]);
}
puts("\n");
/* print original struct array */
print_struct_array(ath_recs, ath_num);
/* sort array using qsort function */
qsort(ath_recs, (size_t) ath_num, sizeof(struct olympians), struct_cmp_by_time);
/* print sorted struct array */
print_struct_array(ath_recs, ath_num);
}
/* call to the function) */
int main(void)
{
/* run the function */
sort_structs_time();
return 0;
}

Resources