I am trying to write text to a file using loops, but when I make it like (ogrenci+i) using i and not like (ogrenci+0) I'm getting some weird numbers and text in txt file.
When writing like this (ogrenci+0) it works correctly. What am I doing wrong?
I attent pointer to struct in a different function.
this is the question
QUESTIONS
Assume that you are given the structure below
typedef struct StudentMark {
char name[20];
char surname[20];
int midterm;
int final;
}STUDENT_MARK;
1-) Write down a program which contains
a-) A function to get the user entered name, surname, and exam marks into
dynamically
allocated STUDENT_MARK structure (your function MUST check input validity
i.e
entered marks must between [0..100]).
b-) A function to write down the entered VALID structures into a file
named as
marks_YOUR_STUDENT_ID.txt.
2-) Write a program which contains
a-) A function to read a file named as marks_YOUR_STUDENT_ID.txt which contains
STUDENT_MARK structures’ data.
b-) A function to calculate the average of each student’s exam marks and writes the result
onto screen as
“The student NAME SURNAME’s midterm mark is MIDTERM, final mark is
FINAL and his/her average is AVERAGE”
void girme (int studentnum){
int i;
studentnum = 2;
STUDENT_MARK *ogrenci;
ogrenci = (STUDENT_MARK*) malloc(studentnum * sizeof(STUDENT_MARK));
if(ogrenci == NULL) { exit(1); }
for(i=0;i<studentnum;i++)
{
printf("Enter the student's name, surname, midterm and final respectively: \n");
scanf("%s %s %d %d",(ogrenci+i)->name, (ogrenci+i)->surname, &(ogrenci+i)->midterm, &(ogrenci+i)->final);
if((ogrenci+i)->midterm > 100 || (ogrenci+i)->midterm < 0 || (ogrenci+i)->final > 100 || (ogrenci+i)->final < 0)
{
printf("midterm or final can not be higher than 100 or lower than 0 \n");
exit(1);
}
}
}
void yazma (int studentnum){
int i;
STUDENT_MARK *ogrenci;
FILE *dosya;
dosya = fopen("marks_190704033.txt","w");
if{ (dosya == NULL)
{
printf("Could not open file");
exit(1);
}
else
{
for(i=0;i<studentnum;i++)
{
fprintf(dosya, "%s %s %d %d", (ogrenci+0)->name, (ogrenci+0)-
>surname, (ogrenci+0)->midterm, (ogrenci+0)->final);
}
}
fclose(dosya);
}
int main()
{
int n =2;
girme(n);
yazma(n);
return 0;
}
STUDENT_MARK *ogrenci;
ogrenci = (STUDENT_MARK*) malloc(studentnum * sizeof(STUDENT_MARK));
ogrenci is a single pointer to the structure STUDENT_MARK to space allocated for several objects of struct STUDENT_MARK.
When using, f.e.:
(ogrenci+i)->name
in the for loop, you attempt to access not existing struct pointers to not existing structure objects.
Note: The compiler do not associate the allocated space with several pointers!
If you want to use pointer arithmetics like (ogrenci + i) you need to either define ogrenci as an array of pointers to STUDENT_MARK:
int studentnum = 5;
STUDENT_MARK *ogrenci[studentnum];
and initialize each pointer by the address of an existing structure object for which were each allocated space individually, f.e. like:
int studentnum = 5;
STUDENT_MARK *ogrenci[studentnum];
for(int i = 0; i < studentnum; i++)
{
ogrenci[i] = malloc(sizeof(*ogrenci));
}
or you define ogrenci as a pointer to pointer to STUDENT_MARK:
int studentnum = 5;
STUDENT_MARK **ogrenci;
ogrenci = malloc(sizeof(*ogrenci) * studentnum);
*ogrenci = malloc(sizeof(**ogrenci) * studentnum);
"When writing like this (ogrenci+0) it works correctly."
However, It "works" with 0 because ogrenci + 0 = ogrenci. There is no difference to ogrenci.
Side note: As you mabe have already seen, I omitted the cast of the returned pointer from malloc. This is because it is unnecessary and it might "add clutter" to your code: Do I cast the result of malloc
Related
so I'm having a little problem with my struct array not doing what its supposed to. I get no compiler warnings or errors when building the program.
int Array_Size=0;;
int Array_Index=0;
FILE *Writer;
struct WordElement
{
int Count;
char Word[50];
};
struct WordElement *StructPointer; //just a pointer to a structure
int Create_Array(int Size){
StructPointer = (struct WordElement *) malloc(Size * sizeof(StructPointer));
Array_Size = Size;
return 0;
}
int Add_To_Array(char Word[50]){
int Word_Found=0;
for(int i=0; i <= Array_Size && Word_Found!=1; i++)
{
if(strcmp(StructPointer[i].Word, Word)) // This should only run if the word exists in struct array
{
StructPointer[i].Count++;
Word_Found=1;
}
}
if(Word_Found==0) // if the above if statement doesnt evualate, this should run
{
strcpy(StructPointer[Array_Index].Word, Word); //copying the word passed by the main function to the struct array at a specific index
printf("WORD: %s\n", StructPointer[Array_Index].Word); // printing it just to make sure it got copied correctly
Array_Index++;
}
return 0;
}
int Print_All(char File_Name[50])
{
Writer = fopen(File_Name, "w");
printf("Printing starts now: \n");
for(int i=0; i < Array_Size; i++)
{
fprintf(Writer, "%s\t\t%d\n",StructPointer[i].Word, StructPointer[i].Count);
}
free(StructPointer);
return 0;
}
These functions get called from a different file, The Add_To_Array is called when the program reads a new word form the text file. That function is supposed to check if the word already exists in the struct array and if it does, it should just increment the counter. If it doesn't, then it adds it.
The Print_All function is called after all the words have been stored in the struct array. Its supposed to loop through them and print each word and their occurrence. In the text file, there are 2 of every words but my program outputs:
this 13762753
document -1772785369
contains 1129268256
two 6619253
of 5701679
every 5570645
word 3342389
doccontains 5374021
I don't know what to make of this as im really new to C programming... It's probably worth mentioning the if(Word_Foun==0) doesn't execute
StructPointer = malloc(Size * sizeof(*StructPointer));
This will be the correct allocation. Otherwise you will have erroneous behavior in your code. Also check the return value of malloc.
StructPointer = malloc(Size * sizeof(*StructPointer));
if(NULL == StructPointer){
perror("malloc failure");
exit(EXIT_FAILURE);
}
You are allocating for struct WordElement not a for a pointer to it. You already have a pointer to struct WordElement all that you needed was memory for a struct WordElement.
Also in the loop you are accessing array index out of bound
for(int i=0; i <= Array_Size && Word_Found!=1; i++)
^^^
It will be i < Array_Size.
In case match occurs you want to set the variable Word_found to 1.
if(strcmp(StructPointer[i].Word, Word) == 0){
/* it macthed */
}
Also Writer = fopen(File_Name, "w"); you should check the return value of fopen.
if(Writer == NULL){
fprintf(stderr,"Error in file opening");
exit(EXIT_FAILURE);
}
Also when you are increasing the Array_index place a check whether it might access the array index out of bound.
The more global variable you use for achieving a small task would make it more difficult to track down a bug. It is always problematic because the places from which data might change is scattered - making it difficult to manage.
I am trying to take number of inputs from a user and take the inputs then and store them under dynamically created variable names. can anyone help?
I want to take the number of array user want to input then create the exact number of variables which maintains a common pattern so I can know which array is under which variable and I can call them for further processing.
My current code is as follows
int input, eqn, m, i,n,x;
char inputarr[100], eqnarr[100];
printf("Enter number of variables: ");
scanf("%d",&n);
m=n;
printf("Enter your variables: \n");
while(n!=-1){
gets(inputarr[n]);
n--;
}
while(m!=0){
puts(inputarr[m]);
printf("\n");
m--;
}
my inputs are like
2 (here 2 is number of inputs user intended to give)
a = 3
b = 4
I need to save them in 2 variables say var1 and var2 as I need to work with them later.
C does not support dynamically created variables. You can instantiate dynamic objects through calls to malloc(), but these will not be named. In C, names are just labels used to associate names to memory locations at compile time, and resolved at link time. It is way too late at run time.
You can create a mapping from names to int values, but you cannot create new variables. A Mapping will work for you. You need to create a method to add a named value to your mapping, a method to retrieve the value, a method to update its value, and for completeness, you need a fourth method to delete an element when you no longer need it.
Here is a simple example of mapping variable names to int values using a dynamic lookup table. To be complete, you would need to add methods for updating values, and deleting them, etc.
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#define MAX_VARIABLES 100
typedef struct Lookup_Entry_Struct {
char* Name;
int Value;
} LookUp_Entry;
typedef struct Mapping_Struct {
int MaxEntries;
int NumEntries;
LookUp_Entry* mapping;
} Mapping;
void initMapping(Mapping* map, int MaxEntries)
{
map->NumEntries = 0;
map->MaxEntries = MaxEntries;
map->mapping = calloc(sizeof(LookUp_Entry), MaxEntries);
if (map->mapping == NULL) {
// Failed to allocate the Mapping table
fprintf(stderr, "Failed to alloc Mapping table of %d entries\n", MaxEntries);
map->MaxEntries = 0;
}
}
bool addMap(Mapping* map, char* Name, int Val)
{
bool Added = false;
if (map->NumEntries < map->MaxEntries) {
// There is still room in the table, add this new variable
LookUp_Entry* newEntry = &(map->mapping[map->NumEntries]);
newEntry->Value = Val;
newEntry->Name = malloc(strlen(Name)+1);
strcpy(newEntry->Name, Name);
map->NumEntries++;
Added = true;
}
return Added;
}
int lookup(Mapping* map, char* Name)
{
int val = -1;
int i = 0;
bool Found = false;
// Search the map to see if we can find Name
for(i=0; i < map->NumEntries && !Found; i++)
{
LookUp_Entry* entry = &(map->mapping[i]);
if (strcmp(entry->Name, Name) == 0) {
// Found a match, return the value in *Val
val = entry->Value;
Found = true;
}
}
if (!Found)
fprintf(stderr, "lookup of \"%s\" not found in map\n", Name);
// Found value, or -1 if not found
return val;
}
void getVariablesFromUser(Mapping* map)
{
#define MAXNAMELEN 100
// Code modified from Buno's sample
int NumVariables = 0;
int i;
char inputName[100];
int inputVal;
while ((NumVariables<1) || (NumVariables > MAX_VARIABLES)) {
printf("Enter number of variables: ");
scanf("%d", &NumVariables);
if (NumVariables<0 || NumVariables>MAX_VARIABLES)
fprintf(stderr, "Please enter no more than %d variables!\n", MAX_VARIABLES);
}
printf("Init mapping for %d variables\n", NumVariables);
initMapping(map, NumVariables);
for(i=0; i<NumVariables; i++) {
printf("Enter variable #%d name and initial value: ", i+1);
scanf("%s %d", &(inputName[0]), &inputVal);
printf("Adding variable %s with initial value %d\n", inputName, inputVal);
addMap(map, inputName, inputVal);
}
}
int main(int argc, char** argv)
{
Mapping myVarMap;
char* varName;
int i;
getVariablesFromUser(&myVarMap);
// Display all the variables to show how to retrieve values
printf("%d variables added by user\n", myVarMap.NumEntries);
for(i=0; i<myVarMap.NumEntries; i++) {
LookUp_Entry *entry = &(myVarMap.mapping[i]);
char* name = entry->Name;
printf("Entry #%d: %s = %d\n", i+1, name, lookup(&myVarMap,name));
}
}
Save this in file lookup.c, then to compile it:
gcc lookup.c -o lookup
Here is sample run:
scott> lookup
Enter number of variables: 3
Init mapping for 3 variables
Enter variable #1 name and initial value: Bob 123
Adding variable Bob with initial value 123
Enter variable #2 name and initial value: Ted 999
Adding variable Ted with initial value 999
Enter variable #3 name and initial value: Sally 0
Adding variable Sally with initial value 0
3 variables added by user
Entry #1: Bob = 123
Entry #2: Ted = 999
Entry #3: Sally = 0
scott>
You cannot create variable names dynamically, however you can dynamically allocate memory using a function called 'malloc()'. You need to first understand pointers and then learn how to create and access memory during run-time.
As mentioned in the comments and answers of others, you cannot dynamically create variables in C.
However, you can follow this approach:
Dynamically read the characters for each line of n expressions (When the
length of input string is not fixed) For ex, Consider n =1 and the
input line is "a=10" Read each character 'a', '=' , '1' , '0'
Parse the string "a=10" to obtain the value 10. Dynamically allocate
memory to store this value 10.
Do this for all n input lines.
Here is the code to achieve this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Dynamically read the input string character by character: Taken from a SO answer
char *inputString(FILE* fp, size_t size){
//The size is extended by the input with the value of the provisional
char *str;
int ch;
size_t len = 0;
str = realloc(NULL, sizeof(char)*size);//size is start size
if(!str)return str;
while(EOF!=(ch=fgetc(fp)) && ch != '\n'){
str[len++]=ch;
if(len==size){
str = realloc(str, sizeof(char)*(size+=16));
if(!str)return str;
}
}
str[len++]='\0';
return realloc(str, sizeof(char)*len);
}
int main(void) {
int n,i,numindex;
char *variableline,*vartemp;
char keys[] = "1234567890";
//Read in the number of variables
printf("Enter n\n");
scanf("%d\n",&n);
printf("Enter %d lines\n",n);
//Allocate memory for these variables
int *variable_values = malloc(n*sizeof(int));
for(i=0;i<n;i++){
// Read the characters dynamically from the n input lines
variableline = inputString(stdin, 10);
//numindex - index where the number starts. For ex, in the string "a=12", numindex is the position of '1'
numindex = strcspn (variableline,keys);
//Read the number into vartemp
vartemp = malloc(strlen(variableline));
strncpy(vartemp, variableline+numindex, strlen(variableline) - numindex);
//Convert the string to number
*(variable_values+i) = atoi(vartemp);
}
printf("The variable values are:\n");
// All the variable values are stored in the dynamically created memory variable_values
for(i=0;i<n;i++)
printf("%d\n",variable_values[i]);
return 0;
}
I want to store each line of the file in an 2D array, and anthor array pointing each line (so I can identify each line), I need to pass this pointers array to a function so I can manipulate my lines, I d'ont know how to do that
I have this code to read and store in the arrays
char ligne[MAX];
//open end test the file
FILE* fichier = fopen("csp.txt","r");
if(fichier == NULL){
printf("can't open the file \n");
return EXIT_FAILURE;
}
//the first line in the file contain number of other lines
fgets(ligne, sizeof(ligne), fichier);
int nbrTaille = strtol(ligne, NULL, 10);
//array of pointers
char (*tab)[nbrTaille] = malloc(nbrTaille * sizeof(ligne));
int i = 0;
//tab array point each line
while(fgets(ligne, sizeof(ligne), fichier)){
if(ligne == NULL) EXIT_FAILURE;
strncpy(tab[i], ligne, strlen(ligne));
printf("line%d : %s\n", i, tab[i]);
i++;
}
//call the funnction by passing array of pointers and the number of lines
allDiff(tab, nbrTaille);
the file I'm reading is
2
1 2
2 3
to receive the array by the function I tried this but it doesn't work
void allDiff(char** T, int taille)
I am always confused with the parenthesies and asterisks. The easier way is to declare a double pointer:
char ** tab;
//you need to take care of errors. I am not doing it for simplicity
//tab = {pointer1, pointer2, pointer3, ...., pointerN} total memory needed N * sizeof(pointer)
tab = malloc(lines* sizeof(tab)); //how many pointers you want
for(i = 0; i < lines; i++){
tab[i] = malloc(MAX); //each string
}
In the end free the memory:
for(i = 0; i < lines; i++){
free(tab[i]);
}
free(tab);
EDIT complete code
char ligne[MAX];
//open end test the file
FILE* fichier = fopen("csp.txt","r");
if(fichier == NULL){
printf("can't open the file \n");
return EXIT_FAILURE;
}
//the first line in the file contain number of other lines
fgets(ligne, sizeof(ligne), fichier);
int nbrTaille = strtol(ligne, NULL, 10);
//array of pointers
//char (*tab)[nbrTaille] = malloc(nbrTaille * sizeof(ligne));
int i = 0;
char **tab;
tab = malloc(nbrTaille * sizeof(tab)); //how many pointers you want
for(i = 0; i < nbrTaille; i++){
//sizeof(ligne) is equal to MAX
tab[i] = malloc(MAX); //each string
}
i = 0;
//tab array point each line
while(fgets(ligne, sizeof(ligne), fichier)){
if(ligne == NULL) EXIT_FAILURE;
strncpy(tab[i], ligne, strlen(ligne));
printf("line%d : %s\n", i, tab[i]);
i++;
}
//call the funnction by passing array of pointers and the number of lines
allDiff(tab, nbrTaille);
void function(char tab[][MAXLEN], ...);
This will do.
For the sake of readbility and sanity, typedef C function pointers before creating arrays or double pointers to them.
/* typedef the function pointer. An ftabptr points to a void function that
takes a char * */
typedef void (*ftabptr)(char *p);
/* create an uninitalised list of twenty of them */
int N = 20;
ftabptr *pointerlist = malloc(N * sizeof(ftabptr));
However I don't think you really want to do this. You can write a
functioning program that does weird things with tables of function
pointers, but normally you use a language other than C if you
want to play that game (Lisp-like languages, etc). The high level
language then often emits C as an intermediate step.
I am fairly new to the C language and allocating memory/using pointers in general. Anyway, I was experimenting with reading a file, putting those values in a struct, etc. I know exactly what I want to do and of course the program runs but the output is incorrect and some kind of jumbled numbers and letters.
There is a text file with new information for each line. Each line represents one object.
This is how a line in the file might look:
meat sirloin 6.55 8 8.50 4
Overall I want to be able to store all of my PRODUCT objects in an array (so I have an array of structs). So I attempted to allocate memory with a pointer, use a line count, and then send the pointer to a function called read. In read I add each struct to the array via a pointer. The program doesn't crash, the output is just not correct and I have no clue why not. It's prob something with pointers. If anyone could help me I would really appreciate it. Any help at all would be great.
//prototype
void read(pointerToArr);
typedef struct
{
char supType[15];
char prodName[15];
double wholePrice;
int quantWhole;
double retPrice;
int retProdQuantity;
}PRODUCT;
FILE *fr;
int lineCount = 0;
int main()
{
PRODUCT *ptr;
int i;
char check[50];
fr = fopen("ttt.txt", "r");
while(fgets(check, sizeof(check), fr)!= NULL)
{
if(check[0] != '\n')
{
lineCount++;
}
}
// allocate memory for array based on line count.
PRODUCT prodContainter[lineCount];
ptr = (PRODUCT*)malloc(sizeof(PRODUCT)*lineCount);
ptr = prodContainter;
read(ptr);
//print after adding to array via pointer from other
//function. this was a test.
for(i = 0; i < lineCount; i++)
{
printf("%s ", prodContainter[i].supType);
printf("%s ", prodContainter[i].prodName);
printf("%f ", prodContainter[i].wholePrice);
printf("%d ", prodContainter[i].quantWhole);
printf("%f ", prodContainter[i].retPrice);
printf("%d\n\n", prodContainter[i].retProdQuantity);
}
return 0;
}
void read(PRODUCT *pointerToArr)
{
// objective in this method is to read in data from the file, create an object for every line and
// then use the pointer array to add those objects to prodConstainer up above.
char supplyName[15];
char productName[15];
double wholeP = 0;
int quantityWhole = 0;
double retailPrice = 0;
int retailProductQuant = 0;
while(fscanf(fr, "%s %s %lf %d %lf %d", supplyName, productName, &wholeP, &quantityWhole, &retailPrice, &retailProductQuant) == 6)
{
PRODUCT record;
int i;
strcpy(record.supType, supplyName);
strcpy(record.prodName, productName);
record.wholePrice = wholeP;
record.quantWhole = quantityWhole;
record.retPrice = retailPrice;
record.retProdQuantity = retailProductQuant;
for(i = 0; i < lineCount; i++)
{
pointerToArr[i] = record;
}
}
fclose(fr);
}
You never rewind the file, so all the reading after you count the number of lines fails.
What you're printing is just what happens to be in memory.
There are many ways to fix this, of course.
Rewind the file, using rewind()
Close the file and let your read() function (whose name collides with a POSIX standard function, btw) re-open the file. This would also involve removing the scary global variable fr.
Re-structure so you never count the number of lines, by just reading and letting the ptr array grow as necessary (see realloc()).
Also, you should really avoid casting the return value of malloc() in C. This:
ptr = (PRODUCT*)malloc(sizeof(PRODUCT)*lineCount);
is better written as:
ptr = malloc(lineCount * sizeof *ptr);
This does away with the cast, and also uses sizeof on a value of the type pointed at to automatically compute the proper number of bytes to allocate.
Hmm not sure if the title of this makes any sense.
I'm creating a program that reads information from the file marks.txt (This is homework by the way so please bear with me I'm still wandering in the dark a bit :) )
marks.txt:
U08006 3 30 40 30
12000001 55 42 60
12000002 37 45 40
12000003 58 0 24
12000004 74 67 80
12000005 61 50 38
12000006 70 45 58
99999999
the first line is a module code followed by number of assignments and weighting of each assignment. The other lines are student numbers followed by marks for each assignment. And finally the end of the list is indicated by the student number 99999999.
EDIT: the program should eventually output the number of students and the average and standard deviation, for each assignment and overall module mark. Also I've been instructed to use an array of structures.
I have stored the module information in a struct (module), but when it comes to storing the student information I am really lost. I need an array of structs for the students but the problem is that I don't know how many students are in the file (well obviously I do right now but if I want my program to be able to read the file even though the number of students changes having a fixed size array isn't helpful).
I was thinking maybe I could count the number of lines in the file between the first and the last. and then use malloc to allocate memory for the array? am I on the right track here. so something like student = malloc(sizeof(student???) * number of lines)
this is really confusing me! dealing with pointers arrays structs and files all at once is really getting me all muddled up and this is due during my next class so can't ask my tutor for help. So any tips would be greatly appreciated.
my code so far in all it's "work in progress"ness:
EDIT: ok so I've pretty much finished the program i.e. it does what it is suppose to but I'm still struggling with the whole malloc thing. for my student struct I want an array of structs and I want to allocate the memory after I determine the number of students but I'm not sure how I'm suppose to do this?! this whole pointer to array of structs I'm not quite getting it so the following code doesn't work. (if I have an array of struct student of predetermined size everything is peachy)
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int studentCount();
void getModuleDetails(FILE *fileptr);
void getStudentDetails(FILE *fileptr, int classSize);
void overallMarks(int classSize);
void cleanUp(FILE *fileptr, int classSize);
void printModuleStats(int classSize);
struct ModuleSpec {
char moduleCode[7];
int numOfAssign; /*number of assignments*/
int *assignWeighting; /*assignment weighting*/
} module; /*declare struct with variable name module*/
struct StudentMarks {
int studentNumber;
int *assignMark; //assignment mark
int moduleMark; //overall mark for module
}*student;
/*******************************************************************************************/
int main() {
int i; /*index*/
FILE *pFile;
int studentCounter = studentCount(); //count number of students in class
student = malloc(sizeof(student)*studentCounter); /*allocate memory for array of student structs*/
pFile = fopen("marks.txt", "r"); /*open file marks.txt for reading*/
if(pFile==NULL){ /*check if file was opened successfully*/
printf("File could not be opened!");
exit(EXIT_FAILURE);
}
getModuleDetails(pFile); //get module details from pFile and store in struct module
getStudentDetails(pFile, studentCounter); //get student details from pFile and store in student array fo structs
overallMarks(studentCounter); //calculate and print overall marks of students
printModuleStats(studentCounter);
cleanUp(pFile, studentCounter);
return 0;
}
/*****************************************************************************************/
int studentCount(){
FILE *pFile;
int temp;
int studentCount = 0;
pFile = fopen("marks.txt", "r"); /*open file marks.txt for reading*/
if(pFile==NULL){ /*if file can't be opened*/
printf("File could not be opened!");
exit(EXIT_FAILURE);
}
do{
temp = fgetc(pFile);
if(temp == '\n'){
studentCount++;
}
}while(temp != EOF);
studentCount -=2; /*subtract first and last lines to count only students*/
//printf("The number of students on this module is: %d\n", studentCount);
fclose(pFile);
return studentCount;
}
/*******************************************************************************************/
void getModuleDetails(FILE *fileptr){
int i;
int sumWeighting = 0;
fscanf(fileptr, "%s %d", module.moduleCode, &module.numOfAssign);
printf("Module code: %s\n", module.moduleCode);
printf("Number of assignments: %d\n", module.numOfAssign);
module.assignWeighting = malloc(module.numOfAssign * sizeof(int)); /*Allocate memory to hold assignment weightings*/
if (module.assignWeighting == NULL) { /*check if memory allocation was successful*/
printf("Memory allocation failed.");
exit(EXIT_FAILURE);
}
/*get weighting for each assignment and store in module.assignWeighting*/
for(i=0;i<module.numOfAssign;i++){
fscanf(fileptr, "%d", &module.assignWeighting[i]);
//printf("module %d %d\n", i+1, module.assignWeighting[i]);
sumWeighting += module.assignWeighting[i];
}
/*check if sum of weighting equals 100*/
if(sumWeighting != 100){
printf("Error: Sum of weighting = %d\n Expected sum: 100\n", sumWeighting);
}
}
/*********************************************************************************************/
void getStudentDetails(FILE *fileptr, int classSize){
int i;
int j;
for(i=0;i<classSize;i++){
student[i].assignMark = (int *)malloc(sizeof(int) * module.numOfAssign);
if(student[i].assignMark == NULL) { //check if memory allocation was successful
printf("Memory allocation failed.");
exit(EXIT_FAILURE);
}
fscanf(fileptr, "%d", &student[i].studentNumber);
/*get student assignment marks*/
for(j=0;j<module.numOfAssign;j++){
fscanf(fileptr, "%d", &student[i].assignMark[j]);
//printf("mark for assignment %d: %d\n", j+1, student[i].assignMark[j] );
/*check if mark is within range 0 to 100*/
if(student[i].assignMark[j]<0 || student[i].assignMark[j]>100){
printf("Error: Assignment mark is not within the range 0 to 100");
}
}
}
}
/************************************************************************************************/
void overallMarks(int classSize){
int i;
int j;
float temp;
for(i=0;i<classSize;i++){
printf("Overall mark for student %d: \n", student[i].studentNumber);
for(j=0;j<module.numOfAssign;j++){
temp += (float)(module.assignWeighting[j] * student[i].assignMark[j]) / 100;
}
student[i].moduleMark = temp + 0.5; /*add 0.5 for rounding as converting float to int rounds down*/
printf("%d%%", student[i].moduleMark);
if(student[i].moduleMark<25){
printf(" is a FAIL\n");
}
else if(student[i].moduleMark<40){
printf(" is a RESIT\n");
}
else if(student[i].moduleMark<55){
printf(" is a PASS\n");
}
else if(student[i].moduleMark<70){
printf(" is a MERIT\n");
}
else if(student[i].moduleMark<100){
printf(" is a DISTINCTION\n");
}
temp = 0;
}
}
/***********************************************************************************************/
void printModuleStats(int classSize){
int i;
int j;
float averageDevMarks;
float stdDevMarks;
float averageDevModule;
float stdDevModule;
printf("\nModule Statistics for %s\n", module.moduleCode);
printf("\nNumber of students: %d\n", classSize);
/*assignments*/
for(i=0;i<module.numOfAssign;i++){
printf("\nAssignment %d:\n", i+1);
for(j=0;j<classSize;j++){
averageDevMarks += student[j].assignMark[i];
}
averageDevMarks /= classSize;
printf("Average deviation: %f\n", averageDevMarks);
for(j=0;j<classSize;j++){
stdDevMarks += (student[j].assignMark[i] - averageDevMarks)*(student[j].assignMark[i] - averageDevMarks);
}
stdDevMarks = sqrt(stdDevMarks/classSize);
printf("Standard deviation: %f\n", stdDevMarks);
stdDevMarks = 0;
averageDevMarks = 0;
}
/*modules*/
for(i=0;i<classSize;i++){
averageDevModule += student[i].moduleMark;
}
averageDevModule /= classSize;
printf("\nAverage deviation for module mark: %f\n", averageDevModule);
for(i=0;i<classSize;i++){
stdDevModule += (student[i].moduleMark - averageDevModule)*(student[i].moduleMark - averageDevModule);
}
stdDevModule = sqrt(stdDevModule/classSize);
printf("Standard deviation for module mark: %f\n", stdDevModule);
}
/************************************************************************************************/
void cleanUp(FILE *fileptr, int classSize){
int i;
fclose(fileptr); /*close file*/
/*free previously allocated memory*/
free(student);
free(module.assignWeighting);
for(i=0;i<classSize;i++){
free(student[i].assignMark);
}
}
You can either use a linked list instead of the array as Luchian Grigore suggests, or you can use dynamically allocated array of structures, or you can use a dynamically allocated array of pointers to dynamically allocated structures. The advantage of the latter is that there is less copying to do when the array is allocated; the disadvantage is that there are more memory allocations to release.
How does this translate into code? This is an outline of the 'dynamically allocated array of pointers to dynamically allocated structures'. I hypothesize a routine to read a single student's information; it will allocate a structure with the right number of marks too. Basically, it reads a line from the file (spotting EOF and returning a null pointer when the sentinel value is read). For a student, it allocates a student mark structure, and an array of integers of the right size. It parses the line for the student number and each grade. It can report an error if there are marks missing. It returns the pointer to the allocated student. I assume there are two separate allocations — one for the student marks structure and a separate one for the array of marks.
typedef struct StudentMarks Marks;
static Marks *read_student_marks(FILE *fp, int num_grades);
Marks **marks = 0; /* Array of pointers to student marks */
size_t num_marks = 0; /* Number of marks in use */
size_t max_marks = 0; /* Number of marks allocated */
Marks *student;
while ((student = read_student_marks(fp, num_grades)) != 0)
{
assert(num_marks <= max_marks);
if (num_marks == max_marks)
{
/* Not enough space left - allocate more */
size_t new_size = max_marks * 2 + 2;
Marks **new_marks = realloc(marks, new_size * sizeof(*marks));
if (new_marks == 0)
...handle out of memory error...
...NB: you still have the original array to work with...
marks = new_marks;
max_marks = new_size;
}
marks[num_marks++] = student;
}
This starts with a small allocation (2 entries), so that the reallocation code is exercised. I've also exploited the fact that if you pass a NULL pointer to realloc(), it will allocate new memory. An alternative version would use malloc() for the initial allocation and realloc() thereafter:
size_t num_marks = 0;
size_t max_marks = 2;
Marks **marks = malloc(max_marks * sizeof(*marks));
if (marks == 0)
...handle out of memory condition...
while ((students = ...
If you're worried about having allocated too much space at the end of the loop, you can release the surplus with:
marks = realloc(marks, num_marks * sizeof(*marks));
max_marks = num_marks;
The release code is:
for (i = 0; i < num_marks; i++)
{
free(marks[i]->assignMark);
free(marks[i]);
}
free(marks);
marks = 0;
num_marks = 0;
max_marks = 0;
If you want to allocate the array of student marks, you need to decide how the student reader function is going to work. You can use the design outlined above; you'd copy the returned structure into the allocated array. You then release just the marks structure (but not the array of marks; that is still in use). You can grow the array in very much the same way as I did above. The release code is different (simpler), though:
for (i = 0; i < num_marks; i++)
free(marks[i]->assignMarks);
free(marks);
marks = 0;
num_marks = 0;
max_marks = 0;
ASCII art to the rescue — maybe...
The first code assumes you get a pointer to a set of marks for a student from the read_student_marks() function. It keeps an array of pointers to those entries:
+----------+ +--------------------+
| marks[0] |-------->| marks for 12000001 |
+----------+ +--------------------+ +--------------------+
| marks[1] |------------------------------------>| marks for 12000002 |
+----------+ +--------------------+ +--------------------+
| marks[2] |-------->| marks for 12000003 |
+----------+ +--------------------+ +--------------------+
| marks[3] |------------------------------------>| marks for 12000004 |
+----------+ +--------------------+
Notice how the marks array is contiguous, but the marks for each student are separately allocated. The code periodically reallocates the marks array when it needs to grow, but does not move the individual marks for each student.
The alternative scheme outlined is like this:
+--------------------+
| marks for 12000001 |
+--------------------+
| marks for 12000002 |
+--------------------+
| marks for 12000003 |
+--------------------+
| marks for 12000004 |
+--------------------+
Here you would be reallocating all the memory, copying the complete marks structures around. At some levels, this is simpler than the scheme I outlined first.
Complicating this discussion is the fact that the 'marks for' structures are not as simple as I showed:
+--------------------------+
| studentNumber: 12000001 | +------+------+------+
| assignMark: *--------=----------->| 52 | 45 | 60 |
| moduleMark: 92 | +------+------+------+
+--------------------------+
If you have a C99 compiler, you could use a stucture with a 'flexible array member' for the last element of the structure; this would hold the marks:
struct StudentMarks
{
int studentNumber;
int moduleMark;
int assignMark[]; // Flexible array member
}
You would allocate this using a notation such as:
struct StudentMarks *marks = malloc(sizeof(*marks) +
num_assignments * sizeof(marks->assignMark[0]));
This allocates the student mark structure and the array in a single memory allocation. However, you cannot have an array of structures with a flexible array member; you can only use an array of pointers to a structure with a flexible array member (getting you back to the first code I showed).
In case you haven't already gathered, there is more than one way to do it — which is the motto of Perl (aka TMTOWTDI), but applies well here.
As some advice, I recommend drawing diagrams of pointers similar to those I made to help you understand what you are doing. After a while, they'll cease to be necessary, but diagrams can be a great help while you need them.
Pre-reading the number of students would require two passes through the file.
A cleaner solution would be to implement a linked list, and add to it each time you read a new student.
Given some of the constraints on how you can write the program, here is an outline of one way to do it:
Read the module information, and put values into a module_struct which has the module_name and number_of_assignments (this does not need to be malloc'd)
malloc enough space for an array to hold each assignment_weighting, then put the weightings into that.
Scan the file, counting the number_of_students.
malloc enough space for an array of student_structs, number_of_students long
for each entry in the array of student_structs, malloc enough space to hold the assignment_marks, and point a field in the student_struct entry at that assignment_marks array
There should now be enough space to hold all of the values in the file
rewind the file to the start, either using fseek, or fclose and fopen it again.
scan past the module header
read each student row, assigning values into an entry of student_structs, and its assignment_marks
Do all of the calculations on the array of student_structs, and print the answers.
Is that enough information to get you moving?