Scanning from a file into an array of Mallocated Structs? (c) - c

I'm having some trouble reading in data from a file into an array of memory allocated structs in c. My relevant code is as follows:
//Struct that holds the restaurant information
typedef struct
{
char *name;
char *food;
double *price;
int *rating;
}RESTAURANT;
//Function to get all the reviews from the file
void reviews()
{
int numReviews, i;
char tempBuffer[BUFFER_SIZE]; //hold the user input
RESTAURANT *reviews; //create an array of structs
FILE *inputFile; //file pointer
inputFile = fopen(INPUT_FILE_NAME, "r"); //open the input file
//Get the number of reviews from the top of the file
fgets(tempBuffer, BUFFER_SIZE, inputFile);
sscanf(tempBuffer, "%d", &numReviews);
//Allocate enough space in the struct array for the number of reviews
reviews = malloc(sizeof(*reviews)*numReviews);
//Loop to allocate memory for each field in each struct
for(i = 0; i < numReviews; i++)
{
reviews[i].name = malloc(sizeof(char *));
reviews[i].food = malloc(sizeof(char *));
reviews[i].price = malloc(sizeof(double *));
reviews[i].rating = malloc(sizeof(int *));
}
//Loop to get each field for each struct from the file
//And store it in the struct array at the correct struct
for(i = 0; i < numReviews; i++)
{
fscanf(inputFile, "%s", reviews[i].name);
fscanf(inputFile, "%s", reviews[i].food);
fscanf(inputFile, "%lf", reviews[i].price);
fscanf(inputFile, "%d", reviews[i].rating);
}
And the file at reviews.txt is:
4
Chili's
AmericanizedMexican
10.95
3
BurgerKing
American
4.50
2
IHOP
American
9.50
1
OliveGarden
AmericanizedItalian
11.00
4
Reading in Chili's and AmericanizedMexican works fine. But when I try to print the price or rating of Chili's the price always prints 0.0 and the rating is always some huge number over 1 million. What am I doing wrong here? I'm guessing it must be either something with allocating the memory or something with the way I'm meant to read it in.

I don't know, but storing scalar values like price and rating as allocated data via pointers seems strange. You can do that, but it adds a lot of allocation overhead. Remeber that you have to free everything that you have allocated.
Besides that, you got the allocation wrong:
reviews[i].name = malloc(sizeof(char *));
reviews[i].food = malloc(sizeof(char *));
reviews[i].price = malloc(sizeof(double *));
reviews[i].rating = malloc(sizeof(int *));
You allocate memory to hold something the size of a pointer. You must allocate memory that can hold the thing pointed to. A useful allocation pattern is:
x = malloc(sizeof(*x));
for single values and
x = malloc(count * sizeof(*x));
for arrays of length count. You do that already for reviews. Your strings, i.e. char arrays, should be such arrays. So you should allocate:
reviews[i].name = malloc(MAX_LEN * sizeof(char));
reviews[i].food = malloc(MAX_LEN * sizeof(char));
reviews[i].price = malloc(sizeof(double));
reviews[i].rating = malloc(sizeof(int));
where MAX_LEN is a more or less arbitrary limit that you must set and enforce. (For example, you should make sure that fscanf never writes more than ´MAX_LEN` characters to the buffer; that includes the trailing null character.)
Your treatment of the scalar values is awkward. I'd change the struct to
typedef struct {
char *name;
char *food;
double price; // Store values, not pointers
int rating; // Same here
} RESTAURANT;
Throw out the allocation and scan directly into these fields, using the address operator & to get a pointer:
fscanf(inputFile, "%lf", &reviews[i].price);
fscanf(inputFile, "%d", &reviews[i].rating);

your problem is
for(i = 0; i < numReviews; i++)
{
reviews[i].name = malloc(sizeof(char *));
reviews[i].food = malloc(sizeof(char *));
reviews[i].price = malloc(sizeof(double *));
reviews[i].rating = malloc(sizeof(int *));
}
Here, sufficient memory is not allocated.
do this
for(i = 0; i < numReviews; i++)
{
reviews[i].name = malloc(128 *sizeof(char));
reviews[i].food = malloc(128 *sizeof(char));
reviews[i].price = malloc(sizeof(double));
reviews[i].rating = malloc(sizeof(int));
}
EDIT:
The value 128 is used for demonstration purpose only, just to point out the erroneous part in OP's code.

Related

How to access a double pointer structure in another structure

I'm having trouble accessing my double pointer struct within my structure.
typedef struct monster
{
char *name;
char *element;
int population;
} monster;
typedef struct region
{
char *name;
int nmonsters;
int total_population;
monster **monsters;
} region;
region **
readRegion (FILE * infile, int *regionCount)
{
region **temp;
char garbage[50];
char garbage2[50];
char rName[50];
int monsterNum;
fscanf (infile, "%d %s", regionCount, garbage);
temp = malloc (*regionCount * sizeof (region *));
for (int i = 0; i < *regionCount; i++)
{
fscanf (infile, "%s%d%s", rName, &monsterNum, garbage2);
temp[i] = createRegion (inFile, rName, monsterNum);
}
return temp;
}
region *
createRegion (FILE * inFile, char *rName, int nMonsters)
{
region *r = malloc (sizeof (region));
char rMonster[50];
int rLength;
r->name = malloc ((strlen (rName) + 1) * sizeof (char));
strcpy (r->name, rName);
r->nmonsters = nMonsters;
for (int i = 0; i < nMonsters; i++)
{
r->monsters.name = (nMonsters * sizeof (r->monsters.name));
fscanf (in, "%s", rMonster);
r->monsters.name = malloc ((strlen (rMonster) + 1) * sizeof (char));
strcpy (r->monsters.name, rMonster);
}
return r;
}
Hopefully my code is readable where you can get the jist of what im trying to do with the monster** monsters pointer in my region struct. Any explnation on how to access and use a double struct pointer within a structure would help.
I've tried to clean up and re-interpret your createRegion to read a lot more like traditional C:
region* createRegion(FILE * inFile, char *rName, int nMonsters) {
region *r = malloc(sizeof(region));
char buffer[1024];
r->name = strdup(rName);
r->nmonsters = nMonsters;
r->monsters = calloc(nMonsters, sizeof(monster*));
for (int i=0; i < nMonsters; i++) {
// Allocate a monster
monster *m = malloc(sizeof(monster));
fscanf(in,"%s", buffer);
m->name = strdup(buffer);
m->element = NULL; // TBD?
m->population = 1; // TBD?
// Put this monster in the monsters pointer array
r->monsters[i] = m;
}
return r;
}
Where the key here is you must allocate the monsters. Here it's done individually, but you could also allocate as a slab:
region* createRegion(FILE * inFile, char *rName, int nMonsters) {
region *r = malloc(sizeof(region));
char buffer[1024];
r->name = strdup(rName);
r->nmonsters = nMonsters;
// Make a single allocation, which is usually what's returned from
// C functions that allocate N of something
monsters* m = calloc(nMonsters, sizeof(monster));
// Normally you'd see a definition like m in the region struct, but
// that's not the case here because reasons.
r->monsters = calloc(nMonsters, sizeof(monster*));
for (int i=0; i < nMonsters; i++) {
fscanf(in,"%s", buffer);
m[i].name = strdup(buffer);
m[i].element = NULL; // TBD?
m[i].population = 1; // TBD?
// Put this monster in the monsters pointer array
r->monsters[i] = &m[i];
}
return r;
}
Note I've switched out the highly quirky strlen-based code with a simple strdup call. It's also very odd to see sizeof(char) used since on any computer you're likely to interface with, be it an embedded microcontroller or a fancy mainframe, that will be 1.
Inasmuch as you are asking about accessing a double pointer inside a structure, I think your issue is mostly about this function:
region *
createRegion (FILE * inFile, char *rName, int nMonsters)
{
region *r = malloc (sizeof (region));
char rMonster[50];
int rLength;
r->name = malloc ((strlen (rName) + 1) * sizeof (char));
strcpy (r->name, rName);
r->nmonsters = nMonsters;
[Point A]
So far, so good, but here you start to run off the rails.
for (int i = 0; i < nMonsters; i++)
{
r->monsters.name = (nMonsters * sizeof (r->monsters.name));
Hold on. r->monsters has type monster **, but you are trying to access it as if it were a monster. Moreover, r->monsters has never had a value assigned to it, so there's very little indeed that you can safely do with it.
I think the idea must be that r->monsters is to be made to point to a dynamically-allocated array of monster *, and that the loop allocates and initializes the monsters, and writes pointers to them into the array.
You need to allocate space for the array, then, but you only need or want to allocate the array once. Do that before the loop, at Point A, above, something like this:
r->monsters = malloc(nMonsters * sizeof(*r->monsters)); // a monster **
Then, inside the loop, you need to allocate space for one monster, and assign a pointer to that to your array:*
r->monsters[i] = malloc(sizeof(*r->monsters[i])); // a monster *
Then, to access the actual monster objects, you need to either dererference and use the direct member selection operator (.) ...
(*r->monsters[i]).name = /* ... */;
... or use the indirect member selection operator (->) ...
r->monsters[i]->name = /* ... */;
. The two are equivalent, but most C programmers seem to prefer the latter style.
At this point, however, I note that in the body of the loop, you seem to be trying to make two separate assignments to the monster's name member. That doesn't make sense, and the first attempt definitely doesn't make sense, because you seem to be trying to assign a number to a pointer.
fscanf (in, "%s", rMonster);
r->monsters.name = malloc ((strlen (rMonster) + 1) * sizeof (char));
strcpy (r->monsters.name, rMonster);
Using the above, then, and taking advantage of the fact that sizeof(char) is 1 by definition, it appears that what you want is
// ...
r->monsters[i]->name = malloc(strlen(rMonster) + 1);
strcpy (r->monsters[i]->name, rMonster);
And finally,
}
return r;
}
Note well that corresponding to the two levels of indirection in type monster **, each access to an individual monster property via r->members requires two levels of derferencing. In the expressions above, one is provided by the indexing operator, [], and the other is provided by the indirect member access operator, ->.
* Or you could allocate space for all of the monsters in one go, before the loop, and inside the loop just initialize them and the array of pointers to them. The use of a monster ** suggests the individual allocation approach, but which to choose depends somewhat on how these will be used. The two options are substantially interchangeable, but not wholly equivalent.

Characters matrix written in a binary file

I have a text file from which I read words. After that, I have to write in a binary file each word and near it the row and column where it appears. At _strdup(p) my programm crashes. Does anyone know why? I would appreciate your help. Here is the code:
void create(const char *filename, const char ****matrix) {
FILE *u, *f;
u = fopen(filename, "wb");
assert(u != NULL);
f = fopen("in.txt", "r");
assert(f != NULL);
(*matrix) = (char ***)malloc(sizeof(char **) * 1);
int i = 0;
int j=0; char buff[1024];
while (fgets(buff, 1024, f)!=NULL) {
(*matrix) = realloc((*matrix), (i + 1) * sizeof(char **));
char *p = strtok(buff, " ().,");
(*matrix)[i] = (char **)malloc(sizeof(char *));
while (p) {
(*matrix)[i] = (char **)realloc((*matrix)[i], sizeof(char *)*(j + 1));
strcpy((*matrix)[i], buff);
(*matrix)[i][j] = _strdup(p);
fwrite((*matrix)[i][j], sizeof(char *), 1, u);
fwrite(&i, sizeof(int), 1, u);
fwrite(&i, sizeof(int), 1, u);
j++;
(*matrix)[i][j] = NULL;
p = strtok(NULL, " ().,");
}
(*matrix)[i] = NULL;
i++;
printf("\n");
}
fclose(u);
fclose(f);
}
The call to _strdup is likely failing because memory for (*matrix)[i][j] has not been allocated properly..
Using as many pointers as you are to read words from a file is not necessary, but given that you are using them, memory needs to be created for each, in the right order.
That is
matrix = malloc(sizeof(char *));
is the first of locations required before creating any others
For example, the method to create a properly allocated collection of pointers, for a 4 dimension array, might look like this:
char **** Create4D(int hR, int p, int c, int r)
{
char ****arr;
int w,x,y;
arr = calloc(hR, sizeof(char *));
for(w=0;w<hR;w++)
{
arr[w] = calloc(p, sizeof(char *));
for(x=0;x<p;x++)
{
arr[w][x] = calloc(c, sizeof(char *));
for(y=0;y<c;y++)
{
arr[w][x][y] = calloc(r, 1);
// ^ sizeof(char) == 1
}
}
}
return arr;
}
Note the italicized is to remind you that this is not actually creating a matrix, just a collection of memory addresses, some with space for pointers, others with space for strings of char.
To call this function:
char ****matrix = Create4D(2, 4, 6, 8);
if(matrix)//always test for success. If failed, matrix is set to NULL.
{
//use matrix
//With these arguments, 48 (2*4*6) pointers are created,
//each with space allocated to contain strings of 8 char each
....
Note also that in all of these calls, casting the return of [m][c]alloc is not used here, because it is not recommended in C. Also, if you plan create all of this memory, corresponding calls to free() must be made for each call to calloc().
Memory from calling the method above on my system is illustrated here:

Circular Char Array Buffer - c

I am using a char * array to make a buffer to move information from multiple mapping threads to a reducing thread. I need to make the array circular, however, i keep getting segmentation faults when the array runs out of room. How do I make the array circular? I currently have
for(j = 0; j < i; j++){
int next = mr->nextIndex + j;
if(next > 1023){
next = 0;
}
mr->buffer[next] = temp[j];
}
The array is set up as,
new_mr->buffer = malloc(sizeof(char *) * MR_BUFFER_SIZE);
with the macro being 1024. Any help is appreciated.
temp is
char *temp = malloc(sizeof(char *));
and it gets its value from
memcpy(temp, kv, i);
and kv is passed into the function from the main.
This is wrong:
char *temp = malloc(sizeof(char *));
You're storing some data into there using memcpy() but the storage space is only sizeof(char*) which is 4 or 8 bytes. You probably meant to use some other size there, e.g. the value of i you pass to memcpy().
char *temp = malloc(sizeof (char *));
should be
char *temp = malloc(sizeof (char) * i); // sizeof (char) can be omitted
because temp is expected to point to an array of char.
mr->buffer[next] = temp[j];
should be
mr->buffer[next] = &temp[j];
because mr->buffer[next] is of type char *.

pointer of a matrix in C

tPeca* criarPecas(FILE *pFile, int tam){
int i = 0,linhaV,colunaV,j = 0;
char ***elemento = (char***)malloc(tam*sizeof(char**));;
tPeca *pecaJogo = (tPeca*)malloc(tam*sizeof(tPeca));
if(pecaJogo==NULL)
return NULL;
for(i=0;i<tam;i++){
j=0;
fscanf (pFile, "%[^;]", pecaJogo[i].nome);
fscanf (pFile, ";%d", &pecaJogo[i].qtd);
fscanf (pFile, ";%d", &linhaV);
pecaJogo[i].linha = linhaV;
fscanf (pFile, ";%d", &colunaV);
pecaJogo[i].coluna = colunaV;
**elemento[i] = (char**)malloc(linhaV * sizeof(char*));
*elemento[i][j] = (char*)malloc(colunaV * sizeof(char));
j++;
}
return pecaJogo;
}
*** elemento is a pointer of a matriz, i think that i have problem with malloc... I received Segmentation Fault
These two statements are where I guess you ran into your problem:
**elemento[i] = (char**)malloc(linhaV * sizeof(char*));
*elemento[i][j] = (char*)malloc(colunaV * sizeof(char));
You created char *** above, and attempted to create an array of pointers:
char ***elemento = (char***)malloc(tam*sizeof(char**));;
Should be:
//this step creates an array of pointers
char ***elemento = malloc(tam*sizeof(char*));
//Note: there is no need to cast the return of [m][c][re]alloc in C
// The rules are different in C++ however.
Now you can put elemento in a loop to allocate pointer space for each of the pointers you created:
//this step creates an array pointers for each element of the array created above:
for(i=0;i<tam;i++) //assuming size is also tam (you specified nothing else)
{
elemento[i] = malloc(tam*sizeof(char *));//each ith element will
//now have tam elements of its own.
}
Next, you allocate memory at each location:
for(i=0;i<tam;i++)
{
for(j=0;j<tam;j++)
{
elemento[i][j] = malloc(someValue*sizeof(char));
//Note: sizeof(char) == 1, so could be:
//elemento[i][j] = malloc(someValue);
}
}
Now you have a fully allocated 3D array.
Putting it all together, (A simple 2D example)
When you create memory for a multi-dimensional array, you must create a combination of array of pointers, and memory for each. For 2D example, (used for an array of strings perhaps) you could do this:
char ** allocMemory(char ** a, int numStrings, int maxStrLen)
{
int i;
a = calloc(sizeof(char*)*(numStrings), sizeof(char*));//create array of pointers
for(i=0;i<numStrings; i++)
{
a[i] = calloc(sizeof(char)*maxStrLen + 1, sizeof(char));//create memory at each location
}
return a;
}
You must also create method to free memory:
void freeMemory(char ** a, int numStrings)
{
int i;
for(i=0;i<numStrings; i++)
if(a[i]) free(a[i]);
free(a);
}
Usage:
char **array = {0};
...
array = allocMemory(array, 10, 80);
...
freeMemory(array, 10);
Will create memory, and addresses sufficient to contain 10 arrays of 80 character strings (arrays of char), then free it.
This could be expanded to 3D by adding another layer (for loop) of pointer creation, as shown at top of post). In this implementation, the inner most loop always creates the actual memory for each of the address locations you create.

Structure with another structure into it

How can I correctly access the price member from the category structure?
This is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SMAX 128
typedef struct {
int price;
char favCharacter[SMAX], edition[SMAX];
} category;
typedef struct {
char title[SMAX], fname[SMAX], lname[SMAX];
category *cat;
} book;
int main(int argc, char **argv) {
book *data = malloc(100 * sizeof(book));
(data->cat)->price = 10; //1
printf("\n\n(*(data->cat)).price is: %d%s", (data->cat)->price, "\n\n");
return 0;
}
My first attempt failed (//1)
What is the right way to solve this? (I mean to have a structure with another structure).
You are allocating memory for book, but not for the cats inside the books. Here's an example:
/* allocate a hundred books */
book *data = malloc(100 * sizeof *data);
/* allocate one cat per book */
for (i = 0; i < 100; ++i)
data[i].cat = malloc(sizeof *data[i].cat);
/* now you can use it */
for (i = 0; i < 100; ++i)
data[i].cat->price = 50;
Note: you need to add checks to make sure malloc doesn't fail before continuing using the returned memory. Also, it's best to avoid magic numbers such as 100 above. Furthermore, don't forget to free the memory later.
You are allocating an array of books in your line:
book *data = malloc(100 * sizeof(book));
So you should access an array element before accessing its contents:
data[0].cat->price = 10; // lets access element 0 for example.
But, first, you must also alloc data for the cat variable inside each array element:
for ( int i = 0; i < 100; i++ ) {
data[i].cat = malloc(sizeof(category));
}
Now you can access your elements correctly.
Don't forget to free the allocated memory.
Change
book *data = malloc(100 * sizeof(book));
(data->cat)->price = 10; //1
printf("\n\n(*(data->cat)).price is: %d%s", (data->cat)->price, "\n\n");
to
// 1. need explicit conversion
book *data = (book *) malloc(100 * sizeof(book));
// 2. allocate category for book *data
for (int i=0; i<100; i++)
data[i].cat = (category *) malloc(sizeof(category));
// 3. setting
(data[0].cat)->price = 10;
// 4. read
printf("\n\n(*(data->cat)).price is: %d%s", (data[0].cat)->price, "\n\n");

Resources