How to edit structure values after passing them c - c

Hello guys i just want to read the students from a file and then ask the user to add a new student but first I have to add it to my structur and I don't really kmow how to edite or add students to my allocated structure. Please learn me how to send the values of my struct to any other function and edite them..
struct student {
char personnummer[11];
char förnamn[11];
char efternamn[10];
char gender[7];
char programmdife[20];
char age[4];
char email[30];
//struct likaprogramms *likaprogramm;
} *personer;
void f(struct student **x)
{
char str[200];
int j=0;
struct student *personer = (struct student*)malloc(j * sizeof(struct student));
FILE* f;
f = fopen("elever.txt", "r");
while (fgets(str,sizeof str, f)!=NULL )
{
sscanf(str,"%10s %9s %9s %6s %19s %3s %29s", personer[j].personnummer,personer[j].förnamn, personer[j].efternamn, personer[j].gender, personer[j].programmdife, personer[j].age, personer[j].email);
printf("%10s %9s %9s %6s %19s %3s %29s\n", personer[j].personnummer,personer[j].förnamn, personer[j].efternamn, personer[j].gender, personer[j].programmdife, personer[j].age, personer[j].email);
j++;
*x=personer;
}
fclose(f);
//free(personer);
}
int i;
int main()
{
getchar();
printf("skriv ny personnummret\n");
getchar();
fgets(personer->personnummer,11,stdin);
printf("skriv förnamn\n");
getchar();
fgets(personer->förnamn,50,stdin);
printf("skriv gender\n");
fgets(personer->gender,20,stdin);
printf("skriv programm\n");
fgets(personer->programmdife,50,stdin);
printf("skriv ålder\n");
getchar();
fgets(personer->age,4,stdin);
printf("skriv email\n");
getchar();
fgets(personer->email,40,stdin);
// printf("%s %s %s %s %s %s %s", personnummer,förnamn,efternamn,gender,programm,age,email);
return 0;
}

As already stated in the comments your malloc will return either NULL or a valid pointer to a buffer of 0 bytes, because j is 0 when malloc is called.
No matter which happens, your code will write to invalid memory.
Then i would strongly recommend to enable warnings (gcc/clang: -W -Wall -Wextra).
One possible solution would be to set *x = NULL at the start of your function and use realloc to increase the buffer blockwise in the while loop.
In this case the caller has to make sure to use free on the memory if it is not used anymore.
Something like this:
const int block_size = 16;
int num_allocated = 0;
*x = NULL;
while (your_condition)
{
if (num_allocated <= j)
{
struct student *new_ptr = realloc(*x, (sizeof(struct student) * num_allocated) + (sizeof(struct student) * block_size));
if (new_ptr == NULL)
{
// realloc failed, but ptr is still valid (or NULL if the first call to realloc failed)
return num_allocated;
}
*x = new_ptr;
num_allocated += block_size;
}
// use *x as array of struct student here and fill it
// for every filled element j has to be increased
}
return number of (usable) elements
You can adjust the blocksize for your needs.
If you do not want to waste memory you can also shrink the buffer at the end to the really needed size:
struct student *new_ptr = realloc(*x, sizeof(struct student) * num_allocated);
if (new_ptr == NULL)
return j;
*x = new_ptr;
return j;

Related

Cannot use malloc with struct data type?

This is just part of a bigger code, but it's full of errors so I try to fix them one by one. When I try to use malloc on my pointer vector the line returns this error
main.c|14|error: expected '{' before '*' token
Any resolutions?
struct students {
int group;
char name[20];
int grade;
};
int main()
{
struct students *ptr[100];
int num, i, max=0;
scanf("%d", &num);
ptr = (struct*) malloc(num * sizeof(struct));
if(ptr == NULL)
{
printf("error");
exit(0);
}
}
Struct is reserved keyword for declaring/defining structures in C, it isn't variable, nor something you cant get size of it. You have declared struct students (according to your code, i think it should be student instead of students), now you have to define a variable and allocate space for 100 structs via a double pointer, the code should be something like this
struct student {
int group;
char name[20];
int grade;
};
int main()
{
struct student ** ptr;
int num, i, max=0;
scanf("%d", &num);
ptr = malloc(num * sizeof(struct student));
if(ptr == NULL)
{
printf("error");
exit(0);
}
}
Now you can access individual students with array subscript
ptr[0]->grade = 20; // putting 20 in grade of first student
Also, there is no need for casting malloc result in C
While using malloc for a 1D array, you should allocate a pointer, not an array of pointers as you have done.
While allocating you are using sizeof(struct). The type here is struct students and you need sizeof(struct students)
Do not cast the result of malloc. See Do I cast the result of malloc?
The final code is
struct students *ptr;
ptr = malloc (num * sizeof(struct students));
You have an array of pointers to structure. You should allocate memory for them separately.
for(int i=0; i<100 && i<num; i++)
{
ptr[i] = malloc(sizeof(struct students));
if(0 == ptr[i])
{
/* Handle this case. */
}
}
/* Your code. */
/* At the end free the memory. */
for(int i=0; i<100; i++)
{
if(0 != ptr[i])
{
free(ptr[i]);
ptr[i] = 0;
}
}
But I think you just wanted to allocate an array of struct students. In that case you just need one pointer.
struct students *ptr = 0;
/* You allocate memory and store it in that pointer. */
ptr = malloc(num * sizeof(struct students));
if(0 == ptr)
{
/* Handle this case. */
}
You can access ith element of the array like ptr[i]. But add necessary checks and make sure i < num.
You need to free the allocated memory whenever you are done using the array.
if(0 != ptr)
{
free(ptr);
ptr = 0;
}

Compiler complain about ''*** glibc detected *** ./myprog: corrupted double-linked list: 0x0000000001cd6240 *** ''

I have this function :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct adress Adress;
struct adress {char *num; char *street; char *namestreet; char *city; char *postal;};
int main(void) {
int n = 0;
int i;
struct adress *Adress;
FILE *f = fopen("/home/file.txt","r");
if (!f) {
printf("Impossible to open file\n");
return 1;
}
char line[256];
while (fgets(line,256,f)) {
if (n == 0) {
Adress = malloc(sizeof(struct adress)*(n+1));
}
Adress[n].num = malloc( 256*sizeof( char));
Adress[n].street = malloc( 256*sizeof( char));
Adress[n].namestreet = malloc( 256*sizeof( char));
Adress[n].city = malloc( 256*sizeof( char));
Adress[n].postal = malloc( 256*sizeof( char));
fscanf( f , "%s %s %s %s %s", Adress[n].num, Adress[n].street, Adress[n].namestreet, Adress[n].city ,Adress[n].postal);
n++;
}
for (i = 0; i < n; i++)
{
printf("\nNum : %s \n", Adress[i].num);
printf("\nStreet : %s \n", Adress[i].street);
printf("\nStreet Name :%s \n", Adress[i].namestreet);
printf("\nCity :%s \n", Adress[i].city);
printf("\nPostal Code : %s \n", Adress[i].postal);
}
fclose(f);
return 0;
}
When I try to compile my program I get the following errors:
* glibc detected * ./myprog: corrupted double-linked list: 0x0000000001cd6240
What is it that I am doing wrong?
Has anyone gotten this type of error before?
You only allocate enough space for one struct adress, if you read more than one line, you will write beyond the end of the array pointed to by Adress and corrupt the heap. A subsequent call to malloc detects this and aborts with the message: ./myprog: corrupted double-linked list: 0x0000000001cd6240.
Initiliaze Adress to NULL:
struct adress *Adress = NULL;
and change these 3 lines:
if (n == 0) {
Adress = malloc(sizeof(struct adress)*(n+1));
}
to
Adress = realloc(Adress, sizeof(struct adress)*(n+1));
if (!Adress) {
printf("out of memory\n");
exit(1);
}
your program is buggy and corrupted the libc linked list that handle your memory.
The lib c has 'private' data stored in the hunk of memory you allocated.
usually there is a header like:
struct hdr
{
size_t size; /* Exact size requested by user. */
unsigned long int magic; /* Magic number to check header integrity. */
struct hdr *prev;
struct hdr *next;
__ptr_t block; /* Real block allocated, for memalign. */
unsigned long int magic2; /* Extra, keeps us doubleword aligned. */
};
if one of the magic is corrupted by a buffer overflow in your code, you get such errors (as the libc can figure out that its structures were corrupted)

segmentation fault using malloc

I'm new to C, so this may be a silly question to ask:
What I want to do here is to input the data to the array of pointers to a structure and then print it out. But I get a segmentation fault when running into the insert function.
Below is my code
common.h
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct book * Book;
struct book{
int id;
char *name;
};
extern int b_insert(Book *b, int id, char *name);
extern int b_print(Book books[], int len);
insert.c
#include "common.h"
int b_insert(Book *b, int id, char *name){
Book p;
p = (Book)malloc(sizeof(struct book));
p->id = id;
strcpy(p->name, name);
*b = p;
printf("success insert book:\n");
printf("\tID: %d Name: %s\n", (*b)->id, (*b)->name);
return 0;
}
int b_print(Book books[], int len){
int i;
printf("Book List\n");
for(i=0; i<len; i++){
printf("books[%d] = ID: %d, Name: %s\n", i, books[i]->id, books[i]->name);
}
return 0;
}
main.c
#include "common.h"
#define MAX 2
int main(){
Book books[MAX];
Book *b=books;
int i;
int id;
char name[10];
for(i=0; i<MAX; i++){
printf("please input new books info\n");
printf("ID: ");
scanf("%d", &id);
printf("Name: ");
scanf("%s", name);
if(b_insert(b, id, name) == -1){
printf("fail to insert\n");
}
b++;
}
b_print(books, MAX);
return 0;
}
Main problem:
Allocate memory for p->name before using
strcpy(p->name, name);
using malloc:
p->name = malloc(10); //Or some other size
Other problems:
Remove the cast here:
p = (Book)malloc(sizeof(struct book));
Why? Here is the answer
if(b_insert(b, id, name) == -1){ will never be true.
Check the result of malloc to check if it was successful in allocating memory.
Check the return value of all the scanfs to see if it was successful in scanning data.
Add a length modifier to the second scanf to prevent buffer overflows:
scanf("%9s", name); /* +1 for the NUL-terminator */
You're not allocating space for name:
int b_insert(Book *b, int id, char *name){
Book p;
p = malloc(sizeof(struct book));
if (p != NULL)
{
p->name = malloc(strlen(name)+1); // It allocates space where the input name will be copied.
if (p->name != NULL)
{
p->id = id;
strcpy(p->name, name);
*b = p;
printf("success insert book:\n");
printf("\tID: %d Name: %s\n", (*b)->id, (*b)->name);
}
else return -1; // No space to allocate string
}
else return -1; // No space to allocate struct
return 0;
}
As mentioned before, allocate space for p->name. You should probably also use something different to read the book title, either scanf format %ms with a pointer to a char pointer, or %9s with your buffer, otherwise the title "war or peace" will also result in a segfault.
Here you create a static variable and the space for it is allocated automatically.
Book p;
You can allocate a space manually when you assign it to pointer, in this line it's not pointer but static variable.
p = (Book)malloc(sizeof(struct book));
What's more if you want to refer to attribute of static variable you should use "." instead of "->". So you have two option. Create a pointer and allocate a space for the structure and then you "->" oraz create static variable.
p->id = id;

Allocate memory for struct array element (string)

I am trying to read from keyboard and store the info in my struct book. The user is asked to enter the array size and I dynamically allocate the size to my book array. But when n > 1, the run time error is exc_bad_access. I don't know why it works when n = 1 and it doesn't work when n > 1.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct book
{
char *title;
char *author;
double price;
};
int main()
{
int n, i = 0;
printf("please enter the value for n\n");
scanf("%d", &n);
struct book *stack = malloc(sizeof(struct book) * n);
stack->author = malloc(sizeof(char) * 100);
stack->title = malloc(sizeof(char) * 100);
//allocate memory for book and struct members
while (i < n )
{
printf("Please enter the title\n");
scanf("%s", stack[i].title);
printf("Please enter the author\n");
scanf("%s", stack[i].author);
printf("Please enter the price\n");
scanf("%lf", &stack[i].price);
i ++;
}
free(stack);
return 0;
}
You're allocating enough structures for any size of elements you input, but not the space for the buffers:
struct book *stack = malloc(sizeof(struct book) * n);
stack->author = malloc(sizeof(char) * 100);
stack->title = malloc(sizeof(char) * 100);
// what about stack[1].author, stack[2].author and the rest??
This will work instead:
struct book *stack = malloc(sizeof(struct book) * n);
for (int i = 0; i < n; ++i) {
stack[i].author = malloc(sizeof(char) * 100);
stack[i].title = malloc(sizeof(char) * 100);
}
And remember to free your memory as well.
For each book in the stack, you have to allocate memory for the author and the title.
for(int i = 0; i < n; i++)
{
stack[i].author = malloc(sizeof(char) * 100);
stack[i].title = malloc(sizeof(char) * 100);
}
You are only allocating memory for the first element.
You need to allocate memory for
char *title;
char *author;
When you do struct book *stack = malloc(sizeof(struct book) * n); memory is allocated for char* pointers (4 or 8 bytes depending on platform you're building).
So you need to allocate some memory for inside values
while (i < n )
{
char *title = malloc(255 * sizeof(char));
printf("Please enter the title\n");
scanf("%s", title);
stack[i].title = title;
.....
i ++;
}

Reading file data to array of structures while allocating memory dynamically

I need help solving the code below. After the code is compiled using gcc it can be run like ./compiledFile inputFile.txt It should take the inputFile.txt read it while allocating memory dynamically for each variable in this case name and courseID, but my code is not working. The place that I don't understand the most and need help is allocating memory, inserting data into structures and printing the data like the example given below. By the look of this code you could tell that I am a newbie to c and dynamic memory allocation and structure in all.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct people
{
char* name[10];
char* courseID[15];
int grade;
};
void printData(struct people student[], int count);
int main(int argc, char* argv[])
{
FILE *in_file;
char buffer[30];
char *token, *del=",";
int count=0;
struct people student[20];
if(( in_file = fopen(argv[1], "r")) == NULL)
{
printf("unable to open the file");
}
while (fgets(buffer, sizeof(buffer), in_file))
{
student = malloc(sizeof(struct people));
token = strtok(buffer, del);
strcpy(student[count].name, token);
count++;
}
fclose(in_file);
printData(student, count);
}
void printData(struct people student[], int count)
{
int i;
for(i=0; i<count; i++)
{
printf("%s", student[i].courseID);
if (strcmp((student[i].name, student[i].courseID) > 0))
{
printf("%s %s", student[i].name, student[i].grade)
}
}
}
the data.txt file has the following content separated by a comman:
John,MATH 1324,90
David,SCI 1401,88
Omondi,MATH 1324,89
David,MATH 1324,90
when printed out it should look like the following:
MATH 1324
John 90
Omondi 89
David 90
SCI 1401
David 88
first of all, it would be great if you could also share what is the actual output or error you get while running this program.
most of the time dynamic memory allocation is used when we do not know the actual size of data elements, but here you have already fixed the size of struct people student as 20
struct people student[20];
this is absolutely fine, but then you do malloc in while loop
student = malloc(sizeof(struct student);
you have already alloted 20 locations using array declaration, now malloc is not required.
if you want to use dynamic memory allocation using pointers for learning purpose then you should first declare student as pointer to type struct people
struct people* student;
allocate memory dynamically in while loop
student=(struct people*) malloc(sizeof(struct people));
then access it
*(student+count)
hope this helps, if you still have doubts/problems edit the question and include the output/error you get while compiling/running this program.
Several issues with the Question code...
1) Definition of main():
int main(int argc, char* argv[])
It must return an integer. Add a return statement at the end of main(), and make a proper "CLEANUP" section:
printData(student, count);
CLEANUP:
if(in_file)
fclose(in_file);
return(0);
}
2) Better handling of fopen() error condition:
if(( in_file = fopen(argv[1], "r")) == NULL)
{
printf("unable to open the file");
goto CLEANUP;
}
And, initialize the in_file pointer:
int main(int argc, char* argv[])
{
FILE *in_file = NULL;
3) Next, the exact definition of student needs to be established. Is it a static array, or is a pointer to a dynamically allocated array? I will assume that you would like to use a dynamic array (given the question text). However, this assumption conflicts with the following line, which defines student as a static array:
struct people student[20];
Change it to:
struct people *student = NULL;
4) Now, the following question code allocates a new (separate) chunk of memory for each student:
student = malloc(sizeof(struct people));
However, what is needed is all the student records in one array, in the same chunk of memory. So, what is needed is to expand a chunk of memory to include student records as they are read, like this:
while (fgets(buffer, sizeof(buffer), in_file))
{
void *tmp = realloc(student, sizeof(struct people) * (count + 1));
if(NULL == tmp)
{
printf("realloc() failed.\n");
goto CLEANUP;
}
student = tmp;
token = strtok(buffer, del);
5) Take a look at the people structure:
struct people
{
char* name[10];
char* courseID[15];
int grade;
};
It appears that the question code has some difficulty when it comes to pointers & arrays. The code is attempting to define the name and courseID fields as both pointers, and arrays. Given that the question is to do with dynamically allocating stuff, I elect to go that direction. Hence, this structure should be changed to the following:
struct people
{
char *name;
char *courseID;
int grade;
};
6) So, each time through the loop, the student name will be placed in allocated storage, and pointed to by the .name field. So, change this:
token = strtok(buffer, del);
strcpy(student[count]->name, token);
count++;
}
to this:
token = strtok(buffer, del);
student[count].name = strdup(token);
count++;
}
7) I don't understand the intent of this line:
if (strcmp((student[i].name, student[i].courseID) > 0))
I am inclined to eliminate it.
8) The following line has flaws:
printf("%s %s", student[i].name, student[i].grade)
Change it to this to print the integer grade (and don't forget the ending semicolon):
printf("%s %d\n", student[i].name, student[i].grade);
The '\n' makes the output look better, one record per line.
9) Since student is a pointer to dynamically allocated memory (not a static array), change this:
void printData(struct people student[], int count)
to this:
void printData(struct people *student, int count)
10) Now, finish the task of parsing the data; from this:
token = strtok(buffer, del);
strcpy(student[count].name, token);
count++;
}
to this:
token = strtok(buffer, del);
student[count].name = strdup(token);
token = strtok(NULL, del);
student[count].courseID = strdup(token);
token = strtok(NULL, del);
student[count].grade = strtol(token, NULL, 10);
count++;
}
11) Now, to make life easier, sort the array. First by courseID, then by name:
...
count++;
}
/** Sort the array by coursID, then by name. **/
qsort(student, count, sizeof(*student), CmpStudentRecs);
printData(student, count);
...
Which will require an additional "Compare Student Recs" function:
int CmpStudentRecs(const void *recA, const void *recB)
{
int result = 0;
struct people *stuRecA = (struct people *)recA;
struct people *stuRecB = (struct people *)recB;
/** First compare the courseIDs **/
result=strcmp(stuRecA->courseID, stuRecB->courseID);
/** Second (if courseIDs match) compare the names **/
if(!result)
result=strcmp(stuRecA->name, stuRecB->name);
return(result);
}
12) Some finishing touches with the printData() function:
void printData(struct people *student, int count)
{
int i;
char *courseID = "";
for(i=0; i<count; i++)
{
if(strcmp(courseID, student[i].courseID))
{
printf("%s\n", student[i].courseID);
courseID = student[i].courseID;
}
printf("\t%s %d\n", student[i].name, student[i].grade);
}
}
Finished. Output:
SLES11SP2:~/SO> ./test data.txt
MATH 1324
David 90
John 90
Omondi 89
SCI 1401
David 88
SLES11SP2:~/SO>
SPOILER
Change the definition of people to:
struct people
{
char name[10];
char courseID[15];
int grade;
};
This assumes that name won't be longer than 9 characters and coursID won't be longer than 14 characters. If that is not true, change them accordingly.
The line:
student = malloc(sizeof(struct student);
is wrong in couple of ways.
student is already declared to be an array of people. You cannot assign it to point to memory allocated by malloc.
struct student is not a type.
That line can be removed.
The line
strcpy(student[count].name, token);
can cause problems if the length of token is longer than 10 (or whatever size you choose for name in people). The safer thing to do is use strncpy.
strncpy(student[count].name, token, 10);
student[count].name[9] = '\0';
You have not set the value of courseID anywhere. Yet, you are trying to print it in printData. You are doing the same thing for grade. You need to update the processing of input lines to set those correctly.
Change the while loop to:
while (fgets(buffer, sizeof(buffer), in_file))
{
token = strtok(buffer, del);
strncpy(student[count].name, token, 10);
student[count].name[9] = '\0';
token = strtok(NULL, del);
strncpy(student[count].courseID, token, 15);
student[count].courseID[14] = '\0';
token = strtok(NULL, del);
student[count].grade = atoi(token);
count++;
}
There are couple of syntax errors in printData. However, fixing those syntax errors does not take care of your printing requirements. It will be easier to print the data in the order that you want to if you sort the data. The following functions will help you sort the data.
int compareStudent(const void* ptr1, const void* ptr2)
{
struct people* p1 = (struct people*)ptr1;
struct people* p2 = (struct people*)ptr2;
return (strcmp(p1->courseID, p2->courseID));
}
void sortData(struct people student[], int count)
{
qsort(student, count, sizeof(struct people), compareStudent);
}
You can call sortData before calling printData or call sortData first in printData. The logic for printing the data needs to be updated a little bit. Here's an updated printData.
void printData(struct people student[], int count)
{
int i;
int j;
sortData(student, count);
for(i=0; i<count; ++i)
{
printf("%s\n", student[i].courseID);
printf("%s %d\n", student[i].name, student[i].grade);
for ( j = i+1; j < count; ++j )
{
if (strcmp(student[i].courseID, student[j].courseID) == 0)
{
printf("%s %d\n", student[j].name, student[j].grade);
}
else
{
i = j-1;
break;
}
}
}
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct people {
char name[10];//char *name[10] is array of pointer
char courseID[15];
int grade;
};
void printData(struct people student[], int count);
int main(int argc, char* argv[]){
FILE *in_file;
char buffer[30];
char *token, *del=",";
int count=0;
struct people student[20];
if((in_file = fopen(argv[1], "r")) == NULL){
printf("unable to open the file");
return -1;//It is not possible to continue the process
}
while (fgets(buffer, sizeof(buffer), in_file)){
//student = malloc(sizeof(struct people));//It is by securing an array already
token = strtok(buffer, del);
strcpy(student[count].name, token);
token = strtok(NULL, del);
strcpy(student[count].courseID, token);
token = strtok(NULL, del);
student[count].grade = atoi(token);
count++;
}
fclose(in_file);
printData(student, count);
}
int cmp(const void *a, const void *b){
const char *x = ((const struct people*)a)->courseID;
const char *y = ((const struct people*)b)->courseID;
return strcmp(x, y);
}
void printData(struct people student[], int count){
qsort(student, count, sizeof(struct people), cmp);//sort by courseID
char *prev = "";
int i;
for(i=0; i<count; i++){
if(strcmp(prev, student[i].courseID)!=0){
prev = student[i].courseID;
printf("\n%s\n", prev);
}
printf("%-9s %d\n", student[i].name, student[i].grade);
}
}

Resources