Trouble storing a struct in an array - c

I want to create a database sort of, that stores a list of 5000 names and 5000 coresponding salaries into an array, I simply can't find the problem since the console either crashes or the compiler gives me the following error: "cannot convert 'char* ()[30]' to 'char' for argument '1' to 'char*' fgets(char*,int,*FILE)".
EDIT: I changed whatever I could figure out in the code, and I seem to have an issue with
this line in particular:
person* TAB = calloc(N, sizeof(struct));
I can't spot other errors(lack of experience), and I don't know exactly what to use instead of fgets to put in a line.
#include <stdio.h>
#include <stdlib.h>
#define N 5000
typedef struct {
char name[30]
int salary;
} person;
int main()
{
person* TAB = calloc(N, sizeof(struct));
FILE * input;
input = fopen("in.txt","r+");
int nr=0;
int r;
while(nr<5000)
{
fscanf(input,"%s",TAB[nr].name);
fscanf(input,"%d",TAB[nr].salary);
nr++;
}
printf("%s %d",TAB[1].name,TAB[1].salary);
fclose(input);
return 0;
}

You really should avoid putting that much data on the stack, which is what a normal variable like that will generally do. It will occupy around 5000 * (30 + 4 * 5000) = 95 MB of stack space, which might be more than your operating system feels is reasonable.
Anyway, the fix is not to allocate this on the heap; the fix is to change the declaration. I believe there's a logic error, since you allocate space for 5000 salaries per person, which is probably not what you meant.
Also, the name field should be an array of characters, but you've declared it as an array of character pointers, which is what the warnings are all about.
I believe you should have:
struct person
{
char name[30];
int salary;
};
This will drop the memory usage for struct person TAB[N]; down to around 5000 * (30 + 4) or around 166 KB which is way more reasonable. This assumes a 4-byte int which is a pretty common situation.
Finally, your file reading code is not very nicely designed, it will probably not work.
Look into using fgets() to read lines, stopping when it fails (i.e. never calling feof()), and then parsing/tokenizing each line as read. Remember that names can contain whitespace, which will make %s in sscanf() stop.

The name struct member should not be declared as 30 char pointers and the salary is one per name so it should look something like this
typedef struct {
char name[30]
int salary;
} person;
Now in order to have it as an array you are best off allocating on the heap
person* persons = calloc(N, sizeof(struct));
Now you can access the name and salary of one person
persons[3].name
persons[3].salary
...
fgets(persons[nr].name,30,input); // although you may want to remove \n

struct person
{
char* name[30];
I think you mean char name[30]. That is if you want one name of at most 29 bytes.
Later on you do:
fgets(&TAB[nr].name,30,input);
Why are you taking the address? If you make the change above, the TAB[nr].name should be of type char[30], which should degenerate into a char* as desired.

Related

printing struct member using user input

So I've been looking at structures, functions and pointers for days now. I just cant wrap my head around structures good enough to do what I want...
I was trying to write a function, which was originally going to receive user input (taken with fgets) as an argument. I have put that aside now, and just decided to give the function a single argument. That argument will be the name of a struct, and I'll use that name to access it's variables and print them the way I want.
typedef struct
{
int hp;
char *name;
} bare;
bare example;
void print_info(char *name);
int main()
{
example.hp = 5;
strcpy(example.name,"John");
print_info("example");
}
void print_info(char *name)
{
printf("The hp of %s is %d", (*name), (*name)->hp);
}
Whatever bloody thing I put there instead of char *name, it always ended up giving me the error "error: struct or union expected"! I tried struct bare **name and (*name)->hp/(*name).hp, char *name/**name and *&name.hp, *&name->hp, every possible solution I could think of..! i think they all turned out to be nonsense... I just cant wrap my head around pointers and structs enough to do this! A little help please? I searched high and low on function arguments, pointers and structs, yet couldn't find a solution/question like mine..
First, it's better to declare your struct this way:
typedef struct bare {
int hp;
char *name;
} bare;
Second, avoid global variable as much as you can. I don't see the point of declaring example in the global namespace since you are using it only inside main().
Third, this line has a problem:
strcpy(example.name, "John");
You are attempting to copy "John" to an uninitialized pointer (example.name) that points to some random memory address. You have to either allocate enough space using malloc() (and free it when you're done with it), or use a fixed-length array. Moreover, it's better to use strncpy() because it allows to specify the maximum number of characters to copy. This way you avoid the risk of buffer overflow.
Fourth, to avoid copying your entire struct to print_info() (in fact, any other struct to any other function), you should pass its address.
With all that said, here is how your code should be written:
#include <stdio.h>
#include <string.h>
typedef struct bare {
int hp;
char name[100]; // Make sure it has enough space, or use malloc() if you don't know how much it will hold initially
} bare;
void print_info(bare *name);
int main(void)
{
bare example; // Declare it inside main()
example.hp = 5;
strncpy(example.name, "John", sizeof example.name); // This works and is safe
print_info(&example);
}
void print_info(bare *name)
{
printf("The hp of %s is %d", name->name, name->hp);
}
Output:
The hp of John is 5
I think what you wish to do is this:
#include <stdio.h>
#include <string.h>
typedef struct {
int hp;
char *name;
} bare;
bare example;
void print_info(bare *name);
int main() {
example.hp = 5;
strcpy(example.name, "John");
print_info(&example);
}
void print_info(bare *name) {
printf("The hp of %s is %d", name->name, name->hp);
}
Or if you want to pass example by value:
#include <stdio.h>
#include <string.h>
typedef struct {
int hp;
char *name;
} bare;
bare example;
void print_info(bare name);
int main() {
example.hp = 5;
strcpy(example.name, "John");
print_info(example);
}
void print_info(bare name) {
printf("The hp of %s is %d", name.name, name.hp);
}
Why did your code not work?
print_info had an incorrect argument data type. What you wanted was to pass an object of bare or perhaps a pointer to an object of bare, but you were instead passing a variable of type char *.
The arrow operator is used on pointers. Maybe take a look at Arrow operator (->) usage in C.
You wanted to pass in a string typed in by the user.
I was trying to write a function, which was originally going to receive user input (taken with fgets) as an argument. I have put that aside now, and just decided to give the function a single argument.
This explains why you pass in a char * to your function. The input value was originally going to be read from fgets. In your program, you passed in the name of your variable.
bare example;
/* ... */
print_info("example");
To do a dynamic lookup on a symbol name, use dlsym.
As I suggested in comments, if you want to be able to look up the name of a variable to find the associated object, you can use dlsym so long as you are on a POSIX system (like Linux). For example:
// Need to inlcude <dlfcn.h> and link with -ldl
// Make local variables findable with -rdynamic
void print_info(char *name)
{
bare *p = dlsym(0, name);
if (p != NULL)
printf("The hp of %s is %d", p->name, p->hp);
else
printf("%s not found!\n", name);
}
So long as you include <dlfcn.h> and use -ldl when linking the program, and you make your symbol table visible (with -rdynamic on GCC), the program will find the pointer to your example variable. (Try it online!)
But you probably meant to do a lookup by name.
However, you seemed to have mixed some things up. Usually, the user will not care what names you have used for the variables in your program. You would never expect fgets to give you "example" because that is not what the user would type in.
You probably meant to search for the bare record that matches the name parameter of bare. In your case, "John".
print_info("John");
Normally, you would have a table of bares that you would look over and check for a match. However, in your simplified example, there is only one to check.
bare * find_bare(char *name)
{
if (strcmp(name, example.name) == 0) return &example;
return NULL;
}
void print_info(char *name)
{
bare *p = find_bare(name);
if (p != NULL)
printf("The hp of %s is %d", p->name, p->hp);
else
printf("%s not found!\n", name);
}
It isn't hard to create and search a table of bare.
In this case, you could probably simple create an array of bare to represent your collection that you would search over.
#define BARE_TABLE_SIZE 50
bare table_example[BARE_TABLE_SIZE];
Assuming you add the code to populate your table, you could use a simple loop to search for a matching name.
bare * find_bare(char *name)
{
for (int i = 0; i < BARE_TABLE_SIZE; ++i)
{
if (strcmp(name, table_example[i].name) == 0)
return &table_example[i];
}
return NULL;
}
Your example.name was an uninitialized pointer.
Finally, the most egregious error in your program is the attempt to call strcpy on an uninitialized pointer. One solution is to allocate new memory to hold the new name and assign the location of the new name to the pointer. POSIX systems (like Linux) supply a function called strdup that creates a copy of the input for you, in newly allocated memory.
example.name = strdup("John");
Since the memory is allocated by malloc, you would need to call free on the pointer if example is ever recycled for a new name.

2D dynamic array in c - accessing allocated variable

I'm struggling (again..) with a project in C coding.
Please help me to understand how to access a variable that was dynamically allocated, through 2D array that was also dynamically allocated.
Every attempt ends up with failure, and I can't find the right syntax...
The attempt : putting a string in the family_name pointer in the new Family allocated into **list.
scanf(" %s",&lod->list[0]->family_name);
The program:
#define _CRT_SECURE_NO_WARNINGS
//Libraries
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//Consts, globals, structs
#define MAX 10000
typedef struct person
{
char *name;
int gender; //1-female, 0-male
} Person;
typedef struct family
{
char *family_name;
Person *mother, *father; // parents' names
Person **children_list; // list of the children in this family
unsigned child_list_size; // size of child_list array
unsigned current_listChild_size;
} Family;
typedef struct listFamilys
{
Family **list;
unsigned current_listFamily_size; // current size of family
}
ListFamilys;
//main
void main()
{
ListFamilys *lod = (ListFamilys*) malloc(sizeof(ListFamilys));
if(! lod) exit(0);
lod->current_listFamily_size =0;
lod->list = (Family**)malloc(sizeof(Family*));
if (!lod->list) exit(0);
printf("Enter family name: ");
**scanf(" %s",&lod->list[0]->family_name);**
system("pause");
}
The issue here is simply a type error combined with insufficient allocation. In order for this to work you need to allocate space for some Family pointers (which you are doing, though only 1), and then an actual Family element (which you are not doing) and then allocate space for the name (which you are not doing). Basically, the code should be
lod->list = (Family**) malloc(sizeof(Family*));
lod->list[0] = (Family*) malloc(sizeof(Family));
lod->list[0]->family_name = (char*) calloc(11, 1);
where you are only doing the first line of that. My usage of calloc here is somewhat arbitrary, as is the 10 char limit. You can modify as is appropriate.
However, while this is the simplest way to get a single element up and running, this approach collapses at the large scale because of how many allocations you have to do, and deallocating memory becomes a nightmare due to how many separate allocations are made using this approach. Therefore, it is better to allocate say 4kb of space for names to a separate pointer, and then point the name field of persons and families to the relevant name rather than a separate allocation for each of their names. So on with the rest of it. Essentially, try to extract allocations rather than tunnel down with them field after field like the code above does.

Structures used to store postal codes

It asks me to create a hash function that turns the postal code into an integer by summing its characters. This is my code. I haven't got to the hash function yet.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct PostalCode
{
int size;
char *array[6];
} PostalCode;
int main()
{
int size = 6, i;
PostalCode *h = malloc(sizeof(PostalCode));
h->size = size;
h->array = malloc(sizeof(char)*size);
printf("Please enter your postal code: \n");
for (i=0; i<6; i++)
scanf(" %c", &(h->array[i]));
}
And the errors I get now are: postalcode.c:9:11: error: assignment to expression with array type
h->array = malloc(sizeof(char)*size);
The line
char *array[6]
declares an array of 6 pointers to char, and that's not what you want here. You can just make it
char * array
and resolve the error that way, or follow chux's advice, i.e. have
char array[6]
in which case you don't need the line that's causing the error, just remove it. No need to dynamically allocate the array.
A few other suggestions:
If your postal code is always 6 characters, then you don't really
need the PostalCode structure, unless you plan on keeping some other
information about the postal code there. You could just do
typedef char PostalCode[6];
Of course, if you expect postal codes to have
variable length, then size might help.
Regardless of whether you want a struct or typedef a char array, hard-coding the value 6 is a bad idea. If it's always 6 characters, then you can #define it; or if you keep the size in the structure, then make sure you initialize it and use the struct member instead of the literal number 6.
There is no need to dynamically allocate memory here, but if you do make sure you free it.
Good luck!

C structures, memory alloc and structures

So, the task is to read file and push data to structure. the data file is:
babe 12 red 12
deas 12 blue 12
dsa 12 red 512
bxvx 15 blue 52
reed 18 black 15
while code is something like that
struct shoes {
char name[8];
int size;
char color[8];
int price;
};
//open file
shoes *item=(struct shoes*)malloc(sizeof(struct shoes));
for (i=0; !feof(file); i++) {
item=(struct shoes*)realloc(item,sizeof(struct shoes)+i*sizeof(struct shoes));
fscanf(file,"%s %i %s %i\n",(item+i)->name,(item+i)->size,(item+i)->color,(item+i)->price);
}
but the program crashes every time.
dbg says: No symbol "item" in current context.
where is error?
You code has some bugs:
1) You need a typedef for shoes unless you're compiling with a C++ compiler ... but then you should have tagged this C++.
2) feof doesn't return false until an attempt has actually been made to read beyond the end of file, so your code makes the array of shoes 1 too large.
3) You're passing ints to fscanf instead their addresses.
If compiling as C code, not C++ code, the casts on malloc and realloc aren't necessary and are advised against. And there are other stylistic issues that make you code harder to comprehend than it could be. Try this:
typedef struct {
char name[8];
int size;
char color[8];
int price;
} Shoe;
// [open file]
Shoe* shoes = NULL; // list of Shoes
Shoe shoe; // temp for reading
for (i = 0; fscanf(file,"%s %i %s %i\n", shoe.name, &shoe.size, shoe.color, &shoe.price) == 4; i++)
{
shoes = realloc(shoes, (i+1) * sizeof *shoes);
if (!shoes) ReportOutOfMemoryAndDie();
shoes[i] = shoe;
}
The problem is that you are not passing pointers to the integers you want to read using fscanf, you are passing the integers themselves.
fscanf treats them as pointers. And where do they point? Who knows - the integers were uninitialized, so they could point ANYWHERE. Therefore, CRASH.
Fix thusly:
fscanf(file,"%s %i %s %i\n",
(item+i)->name,
&((item+i)->size),
(item+i)->color,
&((item+i)->price));
Note that you don't need the same for name and color because arrays degenerate to pointers, so you're passing the right thing already.
And please, consider ditching the item+i notation; item[i] is so much clearer and easier to understand when casually reading the code:
fscanf("%s %i %s %i\n",
item[i].name,
&item[i].size,
item[i].color,
&item[i].price);
Are you sure the debugger says that? I'm surprised it even compiled...
You wanted:
struct shoes *item
If you don't proved a typedef of your structure, you have to explicitly say "struct" every time you reference it.
Second note:
item=(struct shoes*)realloc(item...
Don't assign the same pointer from realloc() that you pass into it. If the reallocation fails it will return NULL and that could be killing you. You should make sure you're checking the results of both the initial malloc() and the realloc()
Third point:
You need to be passing the address of the int's to the fscanf().
fscanf(file,"%s %i %s %i\n",(item+i)->name,&((item+i)->size),(item+i)->color,&((item+i)->price));
There are two issues with your code:
1st. The struct:
you could define your struct in two ways,
the way you did:
struct shoe{
char name[8];
int size;
char color[8];
int price;
};
and in this case you should refer to a pointer to it as:
struct shoe *item;
the other (perhaps more convenient?) way using a typedef along with the defenition:
typedef struct {
char name[8];
int size;
char color[8];
int price;
} shoe;
and in this case you should refer to a pointer to it as:
shoe *item;
Therefore the code you posted isn't supposed to compile.
2nd one:
fscanf should be given pointers to integers/chars in the case you showed.
You've correctly passed the char pointer(since you passed the name of a char array which is actually a char pointer to the first element), but you have passed some integers and fscanf needs pointer to integers it is supposed to fill, so your code should be:
fscanf(file,"%s %i %s %i\n",(item+i)->name,&((item+i)->size),(item+i)->color,&((item+i)->price));

C - Build dynamically allocated array of pointers to structures filled with input from file

I need to build an array of pointers to dynamically allocated structures (DBrecord) and fill that array with input from another file. Not sure how to approach this.
The data file will have the number of entries first, followed by entries in a specific order.
numOfEntries
lastName firstName studentID year gpa expGradYear
example:
1
Doe John 12345678 senior 3.14159 2015
Here's the code I have so far:
class.h
typedef enum {firstYear, sophomore, junior, senior, grad} class;
main.c
#include <stdio.h>
#include <stdlib.h>
#include "class.h"
int main(){
//DBrecord is name for structure
struct DBrecord{
int DBrecordID; //ID for each entry, range 0-319
char *last; //student last name
char *first; //student first name
char studentID[8]; //student ID
int age; //student age
class year; //year in school
float gpa; //GPA
int expGradYear; //expected graduation year
};
int numEntries; //total number of entries, first num in data file
struct DBrecord **ptrToDB;
//scan first int in data file and assign to numEntries
scanf("%d", &numEntries);
//allocate memory for structures, each is 36 bytes
*ptrToDB = malloc (sizeof(struct DBrecord) * numEntries);
//free allocated memory
free(ptrToDB);
//build an array of pointers to dynamically allocated structures
//fill that array with input from data file
//build 7 arrays of pointers to DBrecords, one for each field except DB ID
//sort each a different way
//note the 7 arrays are pointers, no copying
//print each of the 7 sorted arrays
return 0;
}
I can give you some snippets on how to look at this problem.
First - I would avoid using class name for any variable, because in many object-oriented programming languages (including C++) it is a keyword and can't be a name of variable.
Structure DBrecord
It might be a good idea to use typedef. Then you could declare a struct variable without using "struct DBrecord", just "DBrecord". But that's optional. This is how it would look:
typedef struct {
int DBrecordID; // ID for each entry
char *lastName;
char *firstName;
char studentID[8];
...
} DBrecord;
Loading from file
In this homework you have the number of records at the beginning of the file, so you don't need to take "extra" care about it. Just load it.
Let's assume the file is like this:
2
Doe John 12345678 senior 3.14159 2015
Carl Boss 32315484 junior 2.71 2013
Therefore the first thing you do with your file is to open it.
Portable way of working with files is by using FILE pointer. Let me show it (stdio.h must be included):
FILE *filePtr; // Define pointer to file
if((filePtr = fopen("records.txt", "r")) == NULL) // If couldn't open the file
{
printf("Error: Couldn't open records.txt file.\n"); // Printf error message
exit(1); // Exit the program
}
Then you can read from your file by line using fgets() to read by lines or fgetc() to read by characters. This is how you can read number of records (remember that it's on the first line and we've just opened the file - we are at the beginning of the file):
char buffer[100]; // Define the buffer
fgets(buffer, 100 /* size of buffer */, filePtr);
Now buffer contains the first line (without \n character) - number of records. Continue with converting the num's characters into integer (here stdlib.h also has to be included):
int numOfRecords = atoi(buffer);
Allocating enough DBrecords
Now you know the number of records, you can allocate enough space for them. We will use array of pointers.
DBrecord **recs;
recs = (DBrecord **) malloc(sizeof(DBrecord *) * numOfRecords);
Now we have created array of pointers, so now we need to allocate every individual pointer as a DBrecord. Using cycle:
int i;
for(i = 0; i < numOfRecords; i++)
{
recs[i] = (DBRecord *) malloc(sizeof(DBrecord));
}
Now you can acces array elements (= individual records) like this:
recs[0]->lastname /* two possibilities */
*(recs[0]).lastname
an so on.
Filling array with values from file
Now you know everything to get the homework done. This way you fill the array:
int i;
for(i = 0; i < numOfRecords; i++)
{
// Content of cycle reads one line of a file and parses the values into recs[i]->type...
/* I give you small advice - you can use fgetc(filePtr); to obtain character by character from the file. As a 'deliminer' character you use space, when you hit newline, then the for cycle continues.
You know the order of stored values in the file, so it shouldn't be hard for you.
If you don't get it, let me now in comments */
}
Is it somehow clearer now?
EDIT: File name as main's argument
There are usually two ways of 'passing' arguments (values) to a program. They are:
./program < records.txt // Here the file's name is passed to program on stdin
./program records.txt // Here the file's name is passed as value in `argv`.
If you can choose, I strongly recommend you the second one. Therefore you need to have main defined as this:
int main(int argc, char *argv[]) // this is important!
{
// code
return 0;
}
argc is integer which says, how much arguments were passed to the program. argv is array storing them. Remember, that the first argument is name of the program. Therefore if you need to check for it, do it:
if(argc != 2)
{
printf("Number of arguments is invalid\n");
exit(1); // exit program
}
Then you only put argv[1] into fopen function, instead of the string "records.txt".
EDIT 2: Reading file's name from stdin
Another approach must be done, if the name of the records file is passed to the program via ./program < records.txt, which means that "records.txt" (without quotes) will be passed (redirected) to program's standard input.
Therefore to handle that, you can do this:
char filename[50]; // buffer for file's name
scanf("%s", &filename); // reads standard input into 'filename' string until white character appears (new line, blank, tabulator).
Then you have your desired file's name in filename string.
Where to start, where to start.....
//allocate memory for structures, each is 36 bytes
mem = (double *)malloc(36*numEntries);
malloc should be malloc (sizeof (struct DBRecord) * numEntries);
don't cast the result of malloc
2a. you forgot stdlib.h
Why include class.h?
your array of pointers are not double, they are instead
struct DBRecord **ptrToDB;
*ptrToDB = malloc (sizeof (struct DBRecord) * numEntries);
This should get you started.
Next, free() should be the last thing you do before leaving your function (and yes, main is a function)
You'll have to insert some code for the next part, I can't do the homework for you.

Resources