creating a variable name during runtime in C - c

I wonder if anyone could kindly help me out? I have the following basic struct that holds subject scores for students:
typedef struct student{
char name[25];
int maths,
science,
english,
age;
} student;
I've created an array of this type and populated with data. I wrote 3 functions that work out the highest and lowest scores per subject. These functions are almost identical, but the only difference is the subjects they're working on, for example to work out min/max scores for maths
void highestAndLowestMaths(student s1[]){
int highest = s1[0].maths, highestPos = 0,
lowest = s1[0].maths, lowestPos = 0;
for( int x = 1; x < MAX_RECS; x++ ){
if( s1[x].maths > highest ){
highest = s1[x].maths;
highestPos = x;
}
if( s1[x].maths < lowest ){
lowest = s1[x].maths;
lowestPos = x;
}
}
// then display details of highest/lowest in maths etc..
The other 2 functions for science and english are identical and the only part of the code that needs to change is s1[x].maths to s1[x].science and s1[x].english respectively.
So rather than write 3 separate and almost identical functions, could this not be achieved by altering the s1[x].maths/science/english section of code, that would update depending on another parameter passed?
The nearest I got to solving this involved sending a char array:
void allHighestLowest(student s1[], char subject[]){
int highest = -1, highestPos = 0,
lowest = 101, lowestPos = 0,
option;
if(strcmp(subject, "MATHS") == 0){
option = 0;
} else if(strcmp(subject, "SCIENCE") == 0){
option = 1;
} else if( strcmp(subject, "ENGLISH" ) == 0){
option = 2;
} else {
printf("Invalid subject:\t[%s]\nExiting...\n", subject);
exit(1);
}
int member[3];
for(int x = 0; x < MAX_RECS; x++){
member[0] = s1[x].maths;
member[1] = s1[x].science;
member[2] = s1[x].english;
if(member[option] > highest){
highest = member[option];
highestPos = x;
}
if(member[option] < lowest){
lowest = member[option];
lowestPos = x;
}
}
// then display details of highest/lowest in chosen subject etc..
Although it works, I'm sure there has to be a better way to dynamically create the line of code to s1[x].maths/science/english during runtime, rather than using a temp array to grab the values as shown above? The struct itself could change and have many more members added, so I'm looking for the best solution, rather than duplicating functions and code.
Can anyone point me in the right directions? Thanks in advance!

One way of doing this is to provide an accessor function as a parameter to allHighestLowest:
typedef int (*fn_getter)(student *s);
int get_maths(student *s) { return s->maths; }
int get_science(student *s) { return s->science; }
int get_english(student *s) { return s->english; }
void highestAndLowest(student s1[], fn_getter getter)
{
// code here
}
Now, instead of accessing maths, science, and english directly you do:
if (getter(&s1[x]) > highest) {
highest = getter(&s1[x]);
highestPos = x;
}
if (getter(&s1[x]) < lowest) {
lowest = getter(&s1[x]);
lowestPos = x;
}
So instead of calling highestAndLowestMaths(students_list) you now do highestAndLowest(students_list, get_maths).
Note: I wrote the example code without having access to a compiler so it might contain some small errors or typos.
For details about function pointers see this question.

This is the kind of thing that simply doesn't work well in C. The C language is intended for low level programming, and not high level data abstractions.
You could do something like this:
enum subject { MATH, SCIENCE, ENGLISH };
typedef struct student{
char name[25];
int subject[3];
int age;
} student;
and then something like this:
void highestAndLowest(student s1[], enum subject s){
int highest = s1[0].subject[s],
lowest = highest, // Removed code duplication
highestPos = 0,
lowestPos = 0;
for( int x = 1; x < MAX_RECS; x++ ){
if( s1[x].subject[s] > highest ){
highest = s1[x].subject[s];
highestPos = x;
}
if( s1[x].subject[s] < lowest ){
lowest = s1[x].subject[s];
lowestPos = x;
}
}
...
If you want to do this as neatly as in Python, then code Python.

Related

How to initialize a field in a struct from another struct? C

So im new to C programming and my assignment is to write a function(Max_way) that prints the driver who had the total of longest trips.
im using these 2 structs:
#define LEN 8
typedef struct
{
unsigned ID;
char name[LEN];
}Driver;
typedef struct
{
unsigned T_id;
char T_origin[LEN];
char T_dest[LEN];
unsigned T_way;
}Trip;
and a function to determine the total trips of a certain driver:
int Driver_way(Trip trips[], int size, unsigned id)
{
int km=0;
for (int i = 0; i < size; i++)
{
if (id == trips[i].T_id)
{
km = km + trips[i].T_way;
}
}
return km;
}
but when im trying to print the details of a specific driver from an array of drivers, i receive the correct ID, the correct distance of km, but the driver's name is not copied properly and i get garbage string containing 1 character instead of 8.
i've also tried strcpy(max_driver.name,driver[i].name) with same result.
void Max_way(Trip trips[], int size_of_trips, Driver drivers[], int size_of_drivers)
{
int *km;
int max = 0;
Driver max_driver;
km = (int*)malloc(sizeof(int) * (sizeof(drivers) / sizeof(Driver)));
for (int i = 0; i < size_of_drivers; i++)
{
km[i] = Driver_way(trips, sizeof(trips), drivers[i].ID);
for (int j = 1; j < size_of_drivers; j++)
{
if (km[j] > km[j - 1])
{
max = km[j];
max_driver.ID = drivers[i].ID;
max_driver.name = drivers[i].name;
}
}
}
printf("The driver who drove the most is:\n%d\n%s\n%d km\n", max_driver.ID, max_driver.name, max);
}
any idea why this is happening?
Note that one cannot copy a string using a simple assignment operator; you must use strcpy (or similar) as follows:
if (km[j] > km[j - 1]) {
max = km[j];
max_driver.ID = drivers[i].ID;
strcpy(max_driver.name,drivers[i].name);
}
Also note that since you were using ==, this was not even a simple assignment, put a comparison. Changing to == likely fixed a compile-time error, but it did NOT give you what you want.

Segmentation Fault when returning integer

I recently joined Stackoverflow community because I had to ask this question. I've been searching for possible explanations and solutions on the website but so far nothing enlightened me as I wanted. My error is probably caused by a very specific line of code. I'm trying to create a function that reads an array of struct votes, (struct contains integer member number, char *category, char *nominee) and copies all the votes that contain the same number and category to another array of struct. Basically to show all the repeated votes.
typedef struct
{
int member;
char *categ;
char *nom;
}Vote
Vote vote(int member, char *categ, char *nom)
{
Vote result;
result.member = member;
result.categ = categ;
result.nom = nom;
return result;
}
int votes_count(Vote *v, int n, Vote *v1)
{
int result = 0;
int *index = malloc(sizeof(int) * 1000);
int a = 0;
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < n; ++j)
{
if (a == 0 && v[i].member == v[j].member && strcmp(v[i].categ, v[j].categ) == 0)
{
v1[result++] = vote(v[j].member, str_dup(v[j].categ), str_dup(v[j].nom));
index[a++] = j;
}
for (int b = 0; b < a; ++b)
{
if( a > 0 && v[i].member == v[j].member && strcmp(v[i].categ, v[j].categ) == 0 && j != index[b])
{
v1[result++] = voto(v[j].member, str_dup(v[j].categ), str_dup(v[j].nom));
index[a++] = j;
}
}
}
}
return result;
}
Afterwads, it returns the number of elements of new array that contains all repetitions. I want to use an array of ints to save all line indexes so that the function doesn't read and copy the lines it already accounted.
Sorry if the code is hard to understand, if needed I can edit to be more understandable. Thanks for any answears.
P.S: I'm portuguese, sorry in advance for grammar mistakes
if your only intention is to harvest the duplicates, you only need to compare to the elements that came before an element
you don't need the index[] array
For simplicity, I used two integer arrays, you should change them to your struct arrays, also change the compare function.
unsigned fetchdups(int orig[], int dups[], unsigned count)
{
unsigned this, that, ndup=0;
for (this=1; this<count; this++){
for (that=0; that<this; that++){
/* change this to your compare() */
if(orig[that] == orig[this]) break;
}
if (this == that) continue; /* no duplicate */
dups[ndup++] = this;
}
return ndup;
}

Problems with passing arrays as parameters

I am a novice programmer in C and am running into an issue that is almost painfully simple. I am writing a basic program that creates two arrays, one of student names and one of student ID numbers, then sorts them and prints them in various ways, and finally allows the user to search the arrays by ID number. Here is the code:
#include <stdio.h>
#include <string.h>
#define ARRAY_SIZE 3
#define MAX_NAME_LENGTH 32
int main()
{
// Student info arrays
char NAME[ARRAY_SIZE][MAX_NAME_LENGTH];
int ID[ARRAY_SIZE];
// Array for student IDs, shifted twice to the right
int shiftedID[ARRAY_SIZE];
// Boolean value to keep while loop running and
// the ID search prompt repeating
int loop = 1;
// Counter variable for the for loop
int counter;
// Gets input values for the student info arrays
for (counter = 0; counter < ARRAY_SIZE; counter++)
{
printf("Input student name: ");
scanf("%s", NAME[counter]);
printf("Input student ID: ");
scanf("%d", &ID[counter]);
}
// Sorts the arrays
sort(NAME, ID);
// Prints the arrays
print_array(&NAME, ID);
// Shifts the ID value two bits to the right
shiftright(ID, shiftedID);
print_array(NAME, shiftedID);
// Repeatedely prompts the user for an ID to
// search for
while(loop == 1)
{
search_id(NAME, ID);
}
}
And here are the function definitions:
#define ARRAY_SIZE 3
#define MAX_NAME_LENGTH 32
// Sorts the two arrays by student ID. (Bubble sort)
void sort(char **nameArray, int idArray[])
{
// Counter variables for the for loop
int firstCounter = 0;
int secondCounter = 0;
for(firstCounter = 0; firstCounter < ARRAY_SIZE; firstCounter++)
{
for(secondCounter = 0; secondCounter < ARRAY_SIZE - 1;
secondCounter++)
{
if(idArray[secondCounter] > idArray[secondCounter + 1])
{
// Temporary variables for the sort algorithm
int tempInt = 0;
char tempName[32];
tempInt = idArray[secondCounter + 1];
idArray[secondCounter + 1] = idArray[secondCounter];
idArray[secondCounter] = tempInt;
strcpy(tempName, nameArray[secondCounter + 1]);
strcpy(nameArray[secondCounter + 1],
nameArray[secondCounter]);
strcpy(nameArray[secondCounter], tempName);
}
}
}
}
// Searches the ID array for a user input student
// ID and prints the corresponding student's info.
void search_id(char **nameArray, int idArray[])
{
// A boolean value representing whether or not
// the input ID value was found
int isFound = 0;
// The input ID the user is searching for
int searchID = 0;
printf("Input student ID to search for: ");
scanf("%d", &searchID);
// Counter variable for the for loop
int counter = 0;
while (counter < ARRAY_SIZE && isFound == 0)
{
counter++;
if (idArray[counter] == searchID)
{
// Prints the name associated with the input ID
isFound = 1;
printf("%s", nameArray[counter]);
}
}
// If the input ID is not found, prints a failure message.
if (isFound == 0)
{
printf("ID not found.\n");
}
}
// Prints the name and ID of each student.
void print_array(char **nameArray, int idArray[])
{
// Counter variable for the for loop
int counter = 0;
printf("Student Name & Student ID: \n");
for (counter = 0; counter < ARRAY_SIZE; counter++)
{
printf("%s --- %d\n", nameArray[counter], idArray[counter]);
}
}
// Shifts the ID value to the right by two bits
void shiftright(int idArray[], int shiftedID[])
{
// Counter variable for the for loop
int counter = 0;
for (counter = 0; counter < ARRAY_SIZE; counter++)
{
shiftedID[counter] = idArray[counter] >> 2;
}
}
I am aware that this program is fairly basic in nature, and more than anything it is an exercise to get me more well versed in a language such as C. I've been working on it for some time, and have worked through several problems, but seem to be stuck on three issues:
If the input ID numbers are not input already in order, a segmentation fault results. If the ID numbers are input already in order, the sort function never passes through the if statement, and no problems arise.
When passing the arrays of names/IDs to the print_array function, the IDs are printed just fine, but the names will be printed either entirely blank or as a series of strange characters.
When searching by ID at the end of the program, the ID number that was entered first (so, the number in ID[0]) displays an ID not found message, where all numbers at index 1 or greater will work fine - aside from the corresponding names that should be printed being printed as blank, as mentioned in the second issue.
Any advice that I can get would be greatly appreciated! I find the power behind the fine details needed in C to be both really interesting but also very confusing, intimidatingly so, and that means any help I can get makes a big difference.
The problem is that you are assuming that char [ARRAY_SIZE][MAX_NAME_LENGTH] and char ** are interchangeable
void sort(char **nameArray, int idArray[])
should be
void sort(char nameArray[][MAX_NAME_LENGTH], int idArray[])
or
void sort(char (*nameArray)[MAX_NAME_LENGTH], int idArray[])
in order to use a pointer to an array of MAX_NAME_LENGTH chars, same for your search_id function.
Take a look to question 6.13 of C-FAQ
I would advise you to restructure your program. Rather than storing two independent arrays for names and IDs, you can store one array of structs which contain all the necessary data:
typedef struct student
{
int id;
char name[MAX_NAME_LENGTH];
} student_t;
student_t students[ARRAY_SIZE];
Now you have a single array which can never become "mismatched" by sorting the IDs without the names, etc.
You can sort an array in C using the standard library function qsort():
qsort(students, ARRAY_SIZE, sizeof(student_t), comparator);
This requires you define a comparator, which is fairly simple. One example would be:
int comparator(const void *lhs, const void *rhs)
{
const student_t *s1 = lhs, *s2 = rhs;
return s1->id - s2->id;
}
You can use the same comparator with another standard library function bsearch() to search the array of students after it is sorted:
student_t key = { 42 }; // name doesn't matter, search by ID
student_t* result = bsearch(&key, students, ARRAY_SIZE, sizeof(student_t), comparator);
These standard functions are more efficient than what you had, and require you to write much less code, with fewer chances for mistakes.

Trouble accessing value in array of structs

I am having trouble with this code. In particular I can't seem to figure out why it is that the voteFractions() function doesn't work for me. It gets called appropriately, and all the correct parameters seem to reach the function, but I cannot get anything from "candidates[i].votes_fraction = candidates[i].votes/total;". All I ever get for candidates[i].votes_fraction is 0.00.
I tried running the program with NUMBER_OF_CANDIDATES = 1, and everything runs OK when that is the case, so I feel like I may be doing something silly, but I just can't seem to see it...
#define NUMBER_OF_CANDIDATES 10
typedef struct candidate{
char name[20];
int votes;
float votes_fraction;
} candidate;
int totalVotes(candidate *candidates)
{
int total = 0;
for (int i = 0; i<NUMBER_OF_CANDIDATES; i++)
total += candidates[i].votes;
return total;
}
void voteFractions(candidate *candidates, int total, char *winner)
{
float most_votes = 0, test;
for (int i = 0; i<NUMBER_OF_CANDIDATES; i++)
{
candidates[i].votes_fraction = candidates[i].votes/total;
if (candidates[i].votes_fraction > most_votes)
{
most_votes = candidates[i].votes_fraction;
strcpy(winner, candidates[i].name);
}
}
}
int main()
{
candidate candidates[NUMBER_OF_CANDIDATES];
int total;
char winner[20];
for (int i = 0; i<NUMBER_OF_CANDIDATES; i++)
{
printf("Enter candidate's name and the number of votes received: ");
scanf("%s %d", candidates[i].name, &candidates[i].votes);
}
total = totalVotes(candidates);
voteFractions(candidates, total, winner);
return 0;
}
The problem is that in this statement
candidates[i].votes_fraction = candidates[i].votes/total;
expression
candidates[i].votes/total
uses integer arithmetic because the both operands have type int. As total is always greater than or equal to candidates[i].votes then the result of the division is equal to 0. You have to write
( float )candidates[i].votes/total
Take into account that variable test is declared but not used in function voteFractions. You may remove it.
void voteFractions(candidate *candidates, int total, char *winner)
{
float most_votes = 0, test;
//...

How to sort a group of structures in C

I made a 4 variables structures and assigned values just to age.
struct database {
int id_number;
int age;
float salary;
};
main()
{
struct database employee[4];
struct database current; // Used later in my attempted sorting
employee[1].age = 12;
employee[2].age = 112;
employee[3].age = 2;
employee[4].age = 22;
I tried various ways to attempt to sort these but all failed,
for example:
for(i = 0; i < 4; i++)
{
current = employee[i];//"current" was previously assigned to the same type of struct
j = i;
while(employee[j-1].age > current.age)
{
employee[j] = employee[j-1];
j = j-1;
}
employee[j] = current;
}
Many errors, as pointed by others in comments.
(Out of bound array access, sorting algorithm is not correct, etc. )
For sorting array of struct, simplest way would be to use qsort
where you need to define a custom comparator, something like following :
typedef struct database db;
int sort_by_age(void *a, void *b)
{
db *_a = (db *)a;
db *_b = (db *)b;
if( _a->age > _b->age ) return -1;
if( _a->age == _b->age ) return 0;
return 1;
}
And then use it like,
int no_of_employee = 4;
qsort( employee, no_of_employee, sizeof(db), sort_by_age );

Resources