Dynamically allocating array of structs C - c

I'm trying to make an array of structs in c, but I can't make it work. When I try to run it, the program crashes.
typedef struct{
char name[20];
char manufacturer[20];
unsigned int price;
} product;
unsigned int stringToNr(char *numbers){
unsigned int nr = 0;
unsigned int i;
for (i = 0; i < strlen(numbers); i ++)
{
nr *= 10; nr += numbers[i] - '0';
}
return nr;
}
I have a function that would print the list to a file, sometimes it reaches this function, sometimes it crashes before.
void printList(product *products, unsigned int nr){
unsigned int i;
FILE *f;
f = fopen("output.txt", "w");
for (i = 0; i < nr; i ++){
fprintf(f, "%s ", products[i].name);
fprintf(f, "%s ", products[i].manufacturer);
fprintf(f, "%d\n", products[i].price);
}
fclose(f);
}
I have to use a separate function to read the list from file.
void readList(product **products, unsigned int *nr){
FILE *f;
f = fopen("input.txt", "r");
char *row;
row = malloc(sizeof(char) * 45);
unsigned int rowLength;
fgets(row, 45, f);
rowLength = strlen(row);
if (row[rowLength - 1] == '\n'){
rowLength--;
row[rowLength ] = '\0';
}
*nr = stringToNr(row);
products = malloc((*nr) * sizeof(product*));
unsigned int i;
char *rowElement;
for (i = 0; i < *nr; i ++){
fgets(row, 45, f);
rowElement = strtok(row, " ");
strcpy((*products)[i].name, rowElement);
rowElement = strtok(NULL, " ");
strcpy((*products)[i].manufacturer, rowElement);
rowElement = strtok(NULL, " ");
rowLength = strlen(row);
if (row[rowLength- 1] == '\n'){
rowLength--;
row[rowLength] = '\0';
}
(*products)[i].price = stringToNr(rowElement);
}
free(row);
fclose(f);
}
Obviously the program has more features, but those work fine.
int main(){
product *products;
unsigned int nr;
readList(&products, &nr);
printList(products, nr);
free(products);
return 0;
}
My input file looks like this:
3
AAA FactoryA 300
BBB FactoryC 550
ZZZ Factory5 100

Code ignores value of products.
What ever readList() receives in products is overwritten with the malloc() call.
void readList(product **products, unsigned int *nr){
...
// bad
products = malloc((*nr) * sizeof(product*));
Instead, use *products. Also allocate by the size of the referenced variable, not by the size of the type. Easier to code, review and maintain.
*products = malloc(sizeof *(*products) * (*nr));
if (*products == NULL) Handle_OOM();
Minor: After fgets(row, ..., ...); , following is not safe from a hacker exploit of reading an initial null character.
rowLength = strlen(row);
// What happens when rowLength == 0
if (row[rowLength- 1] == '\n'){
...
Instead code could use below to rid the optional trailing '\n'.
row[strcspn(row, "\n")] = '\0';

Related

Alphabetical order error in C code

I have created a program that reads a series of strings from a .txt file and after compiling a new .txt file is created where the strings should be in alphabetical order.The problem is that I can't write more than 10 words, the compiler just stops/crashes, WHY? Does it depend by the type of compiler? I am currently using Code-Bloks.How can I optimize the code to run more smoothly?
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void arrange(int n, char *x[])
{
char *temp;
int i,str;
for(str = 0; str < n-1; ++str)
{
for(i = str+1; i < n; ++i)
{
if(strcmp(x[str],x[i]) > 0)
{
temp = x[str];
x[str] = x[i];
x[i] = temp;
}
}
}
return;
}
int number_of_lines = 0;
void countOfLinesFromFile(char *filename){
FILE* myfile = fopen(filename, "r");
int ch;
do
{
ch = fgetc(myfile);
if(ch == '\n')
number_of_lines++;
}
while (ch != EOF);
if(ch != '\n' && number_of_lines != 0)
number_of_lines++;
fclose(myfile);
return number_of_lines;
}
int main()
{
int i , ts=0;
char *x[10];
char *fileName = "WORDS.txt";
countOfLinesFromFile(fileName);
printf("%d",number_of_lines);
FILE * fp;
fp = fopen ("WORDS.txt", "r");
for(i = 0; i < number_of_lines; i++)
{
x[i] = (char*) malloc (1200*sizeof(char));
fscanf(fp, "%s", x[i]);
}
FILE *fPointer;
fPointer=fopen("Alphabetical.txt","w+");
arrange(i,x);
for(i = 0; i < number_of_lines; i++)
{
fprintf(fPointer,"%s\n",x[i]);
}
fclose(fPointer);
fclose(fp);
return 0;
}
char *x[10];
The buffer size is too small
These two lines define how much information you can store
char *x[10]; // 10 strings
x[i] = (char*) malloc (1200*sizeof(char)); // 1200 characters each
As it is written now, you can only hold a maximum of 10 strings with each string being no longer than 1200 characters.
The crash is caused when number_of_lines >= 11 in the following for loop:
for(i = 0; i < number_of_lines; i++)
{
x[i] = (char*) malloc (1200*sizeof(char));
fscanf(fp, "%s", x[i]);
}
When i is 11 you write to x[11] which is past the end of x.

Read a text file into a struct array with functions

so I have this file:
Jane
18
5.3
John
23
5.8
and I need to create a program to store this two persons details on a array of structs.
I have done this:
#include <stdio.h>
typedef struct
{
char name[100];
int age;
float height;
} PERSON;
main()
{
PERSON *X = NULL;
FILE *f;
char ch;
int lines = 1;
f = fopen("filename.txt", "r");
while ((ch = fgetc(f)) !=EOF)
{
if (ch == '\n')
lines++;
}
rewind(f);
X = (PERSON*) malloc ((lines/3) * sizeof(PERSON));
StoreInArray(X, f, lines);
}
StoreInArray(PERSON *X, FILE *f, int lines)
{
int i = 0;
for (i=0; i < lines/3; i++)
{
fscanf(f, "%s%d%f", (*(X+i)).name[100], &(*(X+i)).age, &(*(X+i)).height);
}
//for testing//
for (i=0; i < lines/3; i++)
printf("%s\n%d \n%f\n",X[i].name[100], X[i].age, X[i].height);
}
But all it prints is:
(null)
0
0.000000
If you could help me figure out what is wrong I'd be very appreciated!
Thank you in advance.
I'd compile with -Wall -O2 to catch warnings, which would have pointed out an error.
In your fscanf and printf, you don't want name[100] but name. You want to point to the name array, but name[100] is a single char [fetching past the end of the array (i.e.) undefined behavior], and would be flagged by the compiler.
Here's a version of your code with the bugs annotated/corrected:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char name[100];
int age;
float height;
} PERSON;
void StoreInArray(PERSON * X, FILE * f, int lines);
int
main()
{
PERSON *X = NULL;
FILE *f;
char ch;
int lines = 1;
f = fopen("filename.txt", "r");
while ((ch = fgetc(f)) != EOF) {
if (ch == '\n')
lines++;
}
rewind(f);
X = (PERSON *) malloc((lines / 3) * sizeof(PERSON));
StoreInArray(X, f, lines);
return 0;
}
void
StoreInArray(PERSON * X, FILE * f, int lines)
{
int i = 0;
for (i = 0; i < lines / 3; i++) {
#if 0
fscanf(f, "%s%d%f", (*(X + i)).name[100], &(*(X + i)).age, &(*(X + i)).height);
#else
fscanf(f, "%s%d%f", (*(X + i)).name, &(*(X + i)).age, &(*(X + i)).height);
#endif
}
//for testing//
for (i = 0; i < lines / 3; i++) {
#if 0
printf("%s\n%d \n%f\n", X[i].name[100], X[i].age, X[i].height);
#else
printf("%s\n%d \n%f\n", X[i].name, X[i].age, X[i].height);
#endif
}
}
Note that using *(X + i)).name is cumbersome, so here's a simplified and more readable version that uses an extra pointer variable:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char name[100];
int age;
float height;
} PERSON;
void StoreInArray(PERSON * X, FILE * f, int lines);
int
main()
{
PERSON *X = NULL;
FILE *f;
char ch;
int lines = 1;
f = fopen("filename.txt", "r");
while ((ch = fgetc(f)) != EOF) {
if (ch == '\n')
lines++;
}
rewind(f);
X = (PERSON *) malloc((lines / 3) * sizeof(PERSON));
StoreInArray(X, f, lines);
return 0;
}
void
StoreInArray(PERSON * X, FILE * f, int lines)
{
int i = 0;
PERSON *p;
p = X;
for (i = 0; i < lines / 3; i++, p++)
fscanf(f, "%s%d%f", p->name, &p->age, &p->height);
//for testing//
p = X;
for (i = 0; i < lines / 3; i++, p++)
printf("%s\n%d \n%f\n", p->name, p->age, p->height);
}
It isn't really necessary to preread the file to get a line count. It is possible to grow your array dynamically, as lines are read in, using realloc:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char name[100];
int age;
float height;
} PERSON;
int
main()
{
PERSON *X = NULL;
FILE *f;
int i;
PERSON *p;
int count = 0;
f = fopen("filename.txt", "r");
while (1) {
X = realloc(X,sizeof(PERSON) * (count + 1));
p = &X[count];
if (fscanf(f, "%s%d%f", p->name, &p->age, &p->height) == EOF)
break;
++count;
}
fclose(f);
// trim the array
X = realloc(X,sizeof(PERSON) * count);
p = X;
for (i = 0; i < count; i++, p++)
printf("%s\n%d \n%f\n", p->name, p->age, p->height);
return 0;
}
Here's a further refinement that reduces the number of realloc calls needed:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char name[100];
int age;
float height;
} PERSON;
int
main()
{
PERSON *X = NULL;
FILE *f;
int i;
PERSON *p;
size_t count = 0;
size_t alloc = 0;
f = fopen("filename.txt", "r");
while (1) {
if (count >= alloc) {
alloc += 100;
X = realloc(X,sizeof(PERSON) * alloc);
}
p = &X[count];
if (fscanf(f, "%s%d%f", p->name, &p->age, &p->height) == EOF)
break;
++count;
}
fclose(f);
// trim the array to what was actually used
X = realloc(X,sizeof(PERSON) * count);
p = X;
for (i = 0; i < count; i++, p++)
printf("%s\n%d \n%f\n", p->name, p->age, p->height);
return 0;
}

I have a csv file and I want to put data into a struct, but I don't know why it retain only the last line

Why is the struct retain only the last line of the file?
typedef struct Student
{
char* nume;
char* prenume;
int cod;
float nota;
}Student;
int fileNrLines(int lines)
{
FILE *fp = fopen("students.csv", "r");
char ch;
lines = 0;
lines++;
while ((ch = fgetc(fp)) != EOF)
{
if (ch == '\n')
lines++;
}
fclose(fp);
return lines;
}
void printStudents(Student *myStudents, int size)
{
for (int i = 0; i < size; ++i)
printf("| %20s | %20s | %d | %.2f |\n",
myStudents[i].nume, myStudents[i].prenume, myStudents[i].cod, myStudents[i].nota);
}
void main()
{
int lines=0;
Student *myStudents;
myStudents = (Student*)malloc(fileNrLines(lines) * sizeof(Student));
lines = fileNrLines(lines);
FILE *fp = fopen("students.csv", "r");
char myLine[50];
char* myWord;
int index = 0;
for (int i = 0; i < lines; i++)
{
fgets(myLine, 50, fp);
int contor = 1;
myWord = strtok(myLine, ",");
while (myWord!=NULL)
{
myStudents[index].nume = myWord;
//puts(myStudents[i].nume);
myWord=strtok(NULL, ",");
contor++;
myStudents[index].prenume = myWord;
//puts(myStudents[i].prenume);
myWord=strtok(NULL, ",");
contor++;
int x = atoi(myWord);
myStudents[index].cod = x;
//printf(" %d \n",myStudents[i].cod);
myWord=strtok(NULL, ",");
contor++;
float y = atoll(myWord);
myStudents[index].nota = y;
//printf(" %.2f \n", myStudents[i].nota);
myWord=strtok(NULL, ",");
contor++;
}
index++;
}
fclose(fp);
printStudents(myStudents, &lines);
}
if I print into the while loop is ok but after that not
The problem is that (for example this line)
myStudents[index].nume = myWord;
doesn't populate nume with a new and unique string. Instead, it's simply assigning the memory location that myWord is currently pointing to. Since this is the first token on each line, it'll always likely be pointing to the start of myLine so each nume will be identical. For other columns, the position of myWord will be different so you'll get rather more unexpected results.
Instead you want to create a new string each time, like this.
myStudents[index].nume = strdup(myWord);
Or
myStudents[index].nume = malloc(strlen(myWord)+1);
strcpy(myStudents[index].nume,myWord);
You will need to remember to free() this memory that has been allocated.

fscanf() in C - need help reading a file using pointer notation

Spent an hour or two trying to debug this, but cannot figure out why it won't read my file correctly.
FILE *input;
int numAccounts = 7;
char **accountNames = malloc(sizeof(char)*numAccounts*10);
int *accountNumbers = malloc(sizeof(int)*numAccounts);
float *accountValues = malloc(sizeof(float)*numAccounts);
char *fileName = malloc(sizeof(char)*20);
input=fopen("input.txt","r");
int i;
for(i=0;i<numAccounts;i++) {
fscanf(input,"%s%d%f",*(accountNames+i),accountNumbers+i,accountValues+i);
printf("\n%s %d %f", *(accountNames+i), *(accountNumbers+i), *(accountValues+i));
}
fclose(input);
return 0;
And here is input.txt
Brown 1435 234.55
Dunn 2091 2011.75
Smith 8766 945.05
Stone 4530 0.0
Becker 9073 6235.75
Rich 1003 -42.00
Doe 6739 3655.80
Thank you!
You should start with non-dynamic allocation,
int main()
{
FILE *input = NULL;
int numAccounts = 7;
char accountNames[20] = "";
int accountNumbers = 0;
float accountValues = 0;
input=fopen("input.txt","r");
int i;
for(i=0;i<numAccounts;i++)
{
fscanf(input,"%s %d %f",accountNames,&accountNumbers,&accountValues);
printf("\n%s %d %f\n", accountNames, accountNumbers, accountValues);
}
fclose(input);
return 0;
}
If you must do dynamic allocation, then this pointer-to-pointer to char allocation is incorrect:
char **accountNames = malloc(sizeof(char)*numAccounts*10);
You'd need to allocate numAccounts of char * first, then for each char *, allocate 10 char. That can be done as follows:
char **accountNames = malloc(sizeof(char*)*numAccounts); // allocate numAccounts of char *
int i;
for( i=0; i < numAccounts; i++ )
{
*(accountNames + i) = malloc(sizeof(char)*10); // allocate 10 of char
}
So this should work:
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *input = NULL;
int numAccounts = 7;
char **accountNames = malloc(sizeof(char*)*numAccounts); // allocate numAccounts of char *
int i;
for( i=0; i < numAccounts; i++ )
{
*(accountNames + i) = malloc(sizeof(char)*10); // allocate 10 of char
}
int *accountNumbers = malloc(sizeof(int)*numAccounts);
float *accountValues = malloc(sizeof(float)*numAccounts);
input=fopen("input.txt","r");
for(i=0; i<numAccounts; i++)
{
fscanf(input,"%s%d%f",*(accountNames+i), accountNumbers+i, accountValues+i);
printf("\n%s %d %f", *(accountNames+i), *(accountNumbers+i), *(accountValues+i));
}
fclose(input);
return 0;
}
If the account values are currency. You may want to save them as ints and divide by the lowest unity of that currency.
So instead of saving float accountValue = 23.25 as 23.25, save it as 2335 (23.25 * 100).
Then when you read it divide by 100 to get that value back.
float accountValueDollars;
int accountValueInCents;
fscanf(..., &accountValueInCents);
accountValueDollars = accountValueInCents / 100;
Also, you may want to consider saving in binary format, this way you don't have to worry about parsing the text.
typedef struct _account_t{
char name[50];
int account_number;
double amount;
} account_t;
int how_many = 0;
account_t * accounts = NULL;
// error checking skipped
fread(&how_many, 1, sizeof(int), input);
accounts = (account_t*)malloc(sizeof(account_t) * how_many);
for(int i = 0; i < how_many; i++){
fread(&accounts[i], 1, sizeof(account_t), input);
// print here or whatever
}
fclose(input);
NOTE: Untested code.

How can i use input data from a file in C programming?

I have a program that uses word search. I have a data file which contains the puzzle and the words. What can i implement into my program so that it reads the file and stores the letters present in it as an array?
Example of the data file (it is called testdata):
h e l l o a c d
f g b w o r l d
h a c c v b n a
e q b x n t q q
y e h n c a q r
hello
world
hey
I want to store all the letters in a 2-d array.
Also, I need to store all the words in a 1-dimensional array.
The maximum number of rows of columns or rows that AxA square of letters that is possible in a data file is 25. So, I believe that I should declare an array of that size for the letter and then write them into that array.
I just can't figure out how to read them into that array. There is a space after each letter in the array and no spaces in the words so I think that might be helpful when putting the letters in one array and words in another.
Given your question, and your input, there are a few questions, but in the interest of time, for now, I have made some assumptions about the dimensions of the array, i.e. that it is not necessarily square (as implied by columns or rows that AxA square). The actual data sample disagrees, so I wrote a routine that counts everything as it goes. The letter array is simply an array of arrays, but since it is stored in sequential memory, it just looks like one long array. The strings are each in there own location as well. In any case, this code should illustrate enough to get you on the right track...
#include <ansi_c.h>
#include <stdio.h>
void GetFileContents(char *file, int *nWords, int *lw, int *r, int *c);
void allocMemoryStr(int numStrings, int max);
void allocMemoryLtr(int numStrings, int max);
void freeMemoryStr(int numStrings);
void freeMemoryLtr(int numletters);
#define FILENAME "c:\\dev\\play\\_puzzle.txt"
char **letters;
char **strings;
int main()
{
int longest, cnt, wCount, rows, cols, i;
char line[260];
FILE *fp;
char *buf=0;
GetFileContents(FILENAME, &wCount, &longest, &rows, &cols);
allocMemoryStr(wCount, longest); //for strings
allocMemoryLtr(rows*cols, 1); //for strings
//read file into string arrays
fp = fopen(FILENAME, "r");
cnt=0;
for(i=0;i<rows;i++)
{
fgets(line, 260, fp);
buf = strtok(line, " \n");
while(buf)
{
strcpy(letters[cnt], buf);
buf = strtok(NULL, " \n");
cnt++; //use as accurate count of words.
}
}
cnt=0;
while(fgets(line, 260, fp)) //get remainder of lines into strings
{
//[EDIT]removed fgets()
buf = strtok(line, " \n");
while(buf)
{
strcpy(strings[cnt], buf);
buf = strtok(NULL, " \n");
cnt++; //use as accurate count of words.
}
}
fclose(fp);
freeMemoryStr(wCount);
freeMemoryLtr(rows*cols);
return 0;
}
void GetFileContents(char *file, int *nWords, int *lw, int *r, int *c)
{
char line[260];
FILE *fp;
char *buf=0;
char temp[80];
int wc=0, rc=0, cc=0, ck=0;
fp = fopen(FILENAME, "r");
while(fgets(line, 260, fp))
{
rc++;
buf = strtok(line, " \n");
while(buf)
{
strcpy(temp, buf); // word handler
if(strlen(temp) > 1)
{
wc++;
rc--; //
}
else if(strlen(temp) == 1) //leter handler
{
cc++;
(cc>ck)?(ck=cc):(cc=cc);
}
buf = strtok(NULL, " \n");
}
cc = 0;
}
fclose(fp);
*nWords = wc;
*r = rc;
*c = ck;
}
void allocMemoryStr(int numStrings, int max)
{
int i;
strings = calloc(sizeof(char*)*(numStrings+1), sizeof(char*));
for(i=0;i<numStrings; i++)
{
strings[i] = calloc(sizeof(char)*max + 1, sizeof(char));
}
}
void allocMemoryLtr(int numletters, int max)
{
int i;
letters = calloc(sizeof(char*)*(numletters+1), sizeof(char*));
for(i=0;i<numletters; i++)
{
letters[i] = calloc(sizeof(char)*max + 1, sizeof(char));
}
}
void freeMemoryStr(int numStrings)
{
int i;
for(i=0;i<numStrings; i++)
if(strings[i]) free(strings[i]);
free(strings);
}
void freeMemoryLtr(int numletters)
{
int i;
for(i=0;i<numletters; i++)
if(letters[i]) free(letters[i]);
free(letters);
}
I would parse the file line by line and char by char looking for what i need. In the example (which is untested), i hold three counters to help filling the arrays correctly.
char letters[25][25];
char words[10][25]
int letters_x_pos = 0; // Row counter
int letters_y_pos = 0; // Column counter
int words_pos = 0;
for (int i = 0; i < 25; i++) {
for (int j = 0; j < 25; j++) {
letters[i][j] = '\0';
}
}
const char *line;
while (line = some_read_function()) {
if (!(strlen(line) > 1)) {
continue;
}
if (line[1] == ' ') {
// Line contains letters
const char *letter = line;
while (*letter != '\0') {
if (*letter == ' ' || *letter == '\n' || *letter == '\r') {
continue;
}
else {
letters[letters_x_pos][letters_y_pos++] = *letter;
}
if (letters_y_pos == 25) {
// Maximum reached
break;
}
letter++;
}
// Increment row counter and reset column counter
letters_x_pos++;
letters_y_pos = 0;
if (letters_x_pos == 25) {
// Maximum reached
break;
}
}
else {
// Line contains word
strncpy(words[words_pos++], line, 25);
if (words_pos == 25) {
// Maximum reached
break;
}
}
}

Resources