How to allocate memory in struct for char* fields - c

How to allocate memory for my char * fields in struct ?
My struct:
struct student{
int score;
char* name;
char* surname;
};
int main(){
struct student st[];
int i;
int n = 5;
for(i = 0; i < n; i++){
printf("Score: \n");
scanf("%d", &st[i].score);
printf("Name \n");
scanf("%s", &st[i].name);
printf("Surname \n");
scanf("%s",&st[i].surname)
}
}
How to malloc to char* name and char* surname ?
I must have an array of struct in form struct student st[].
I don't know, how do this rationally.
void initialise_student( struct student *st, char* name, char* surname)
{
st->name = ( strlen( name ) + 1);
st->surname = (strlen( surname ) +1 );
}
int main(){
int i;
int n = 5;
struct student *st[n] = initialise_student();
for(i = 0; i < n; i++){
printf("Score: \n");
scanf("%d", &st[i].score);
printf("Name \n");
scanf("%s", &st[i].name);
printf("Surname \n");
scanf("%s",&st[i].surname);
}
How to match this ?

For example
#include <stdlib.h>
#include <string.h>
//...
struct student st[1];
char *name = "Marek";
char *surname = "Piszczaniuk";
st[0].name = malloc( strlen( name ) + 1 );
strcpy( st[0].name, name );
st[0].surname = malloc( strlen( surname ) + 1 );
strcpy( st[0].surname, surname );
st[0].score = 100;
You can write separate functions to set the data members name and surname for an element of the array.
For example
_Bool set_name( struct student *st, const char *name )
{
st->name = malloc( strlen( name ) + 1 );
_Bool success = st->name != NULL;
if ( success )
{
strcpy( st->name, name );
}
return success;
}

You need to write an initialise_student(struct student* s) function which calls malloc on the char* members. Perhaps this function also takes the name and surname char* pointers of which you take deep copies?
You then call this function with every member of the st array.
Don't forget to build a corresponding free_student(struct student* s) function.

If you are already using scanf, you can tell the function to allocate the sting for you by using %ms instead of %s, like this:
char *name;
if (scanf("%ms", &name) != 1) {
fprintf(stderr, "error: could not read name\n");
exit(1);
}
EDIT This may not be a valid solution—I forgot that %ms is in POSIX only, not in ISO C.

Related

C - "error: Cannot access memory at address" occurs

#include <stdio.h>
#include <string.h>
typedef struct birth{
char *name;
char time[12];
}birth;
void swap(struct birth *a, struct birth *b){
struct birth tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
int main(){
int n;
birth list[100], *p, *q;
scanf("%d", &n);
getchar();
for(p = list; p < list + n; p++){
scanf("%s %s", &p->name, &p->time);
}
for(p = list; p < list + n - 1; p++){
for(q = p + 1; q < list + n; q++){
if(strcmp(p->time, q->time) > 0){
swap(p ,q);
}
else if(strcmp(p->time, q->time) == 0){
if(strcmp(p->name, q->name) > 0){
swap(p ,q);
}
}
}
}
for(int i = 0; i < n; i++){
printf("%s %s\n", list[i].name, list[i].time);
}
return 0;
}
I am solving the problem of receiving n, which means the number of students, repeating the number of students, receiving the student's name and date of birth, and printing the names in advance if the date of birth is the same.
However, there was no answer, so I checked using the debugger in vcode, and when I received the input, the date of birth was well entered, but the name was not.
You are trying to read a string using a char pointer that was never initialized.
typedef struct birth{
char *name;
char time[12];
}birth;
...
scanf("%s %s", &p->name, &p->time); // error, &p->name points to nowhere
You should either allocate memory yourself or declare it as a fixed size char array. It would be best to check the string boundaries too:
#define S_NAME 12
#define S_TIME 12
typedef struct birth{
char name[S_NAME];
char time[S_TIME];
}birth;
...
// read string with safety guard
if (fgets(p->name, S_NAME, stdin) != NULL) {
// read name successfully
}
if (fgets(p->time, S_TIME, stdin) != NULL) {
// read time successfully
}

How to allocate memory for char strings in a struct array?

I am about to create a programm which stores data of students in a list.
My question is, how to shall I allocate memory for each char string in my struct array.
My code is down below. If there're some other mistakes, please correct me.
#include <stdio.h>
#include <stdlib.h>
#define DATA 10;
#define NAME 10;
typedef struct{
int id;
char *givenname;
char *familyname;
} students;
int main()
{
int answer;
int incr = 0; // Index for students in the list
int datalen = DATA;
int namelen = NAME;
students *studentlist;
studentlist = malloc(datalen * sizeof(students)); // Allocate memory for first ten students
if(NULL == studentlist){
printf("Error: Couldn't allocate memory\n");
exit(0);
}
for(incr = 0; incr < datalen; incr ++){
printf("Add student to the list? Yes(1) No(2)\n");
scanf("%d", &answer);
if(answer != 1){
break;
}
studentlist[incr]->givenname = malloc(namelen * sizeof(char)); // Allocate memory for each name
studentlist[incr]->familyname = malloc(namelen * sizeof(char));
printf("Insert ID: ");
scanf("%d", &studentlist[incr].id);
printf("Insert given name: \n");
scanf("%s", studentlist[incr].givenname);
printf("Insert family name: \n");
scanf("%s", studentlist[incr].familyname);
}
free(studentlist);
free(studentlist.givename);
free(studentlist.familyname);
return 0;
}
Reference of some elements is wrong:
studentlist[incr]->givenname
It should be:
studentlist[incr].givenname
Allocation of strings seems fine.
Your freeing code needs change:
free(studentlist);
free(studentlist.givename);
free(studentlist.familyname);
You need to free studentlist.givename and studentlist.familyname in a loop and then free studentlist at the end:
for(incr = 0; incr < datalen; incr ++){
free(studentlist[incr].givename);
free(studentlist[incr].familyname);
}
free(studentlist);

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;

C array of structs segmentation fault

I am trying to make a dynamic array of structs, and I can successfully add one struct to it. But any more structs I add cause a segmentation fault. Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PEOPLE_BLOCK 4
struct Person {
char *first_name;
char *last_name;
unsigned int age;
};
int add_person(struct Person **people, size_t *people_size, size_t *population, struct Person p) {
if ((sizeof(struct Person) * *population) > *people_size) {
return -1;
}
if ((sizeof(struct Person) * (*population + 1)) >= *people_size) {
*people_size = *people_size + sizeof(struct Person) * PEOPLE_BLOCK;
*people = realloc(*people, *people_size);
if (!*people) {
return -1;
}
}
*people[*population] = p;
++*population;
return 0;
}
int main(int argc, char const *argv[]) {
size_t population;
size_t people_size;
struct Person *people, timn, batman;
population = 0;
people_size = sizeof(struct Person) * PEOPLE_BLOCK;
people = malloc(people_size);
timn.first_name = "Timn";
timn.last_name = "Timothy";
timn.age = 38;
add_person(&people, &people_size, &population, timn);
printf("Person 0's first name: %s\n", people[0].first_name);
batman.first_name = "Bat";
batman.last_name = "Man";
batman.age = 42;
add_person(&people, &people_size, &population, batman);
printf("Person 1's first name: %s\n", people[1].first_name);
free(people);
return 0;
}
I'd appreciate any help on why this is happening, thanks!
The problem resides with this line :
*people[*population] = p;
Change it to:
(*people)[*population] = p;
Why are the parenthesis requried?
The compiler has rules of operator precedence. When applying them, it sees your code as this:
*(people[*population]) = p;
which is not what you intended. Given a pointer-to-pointer Type **pp,
*pp[n] = value;
means "take the n'th pointer starting at pp, and assign value at the location dereferenced from the address that pointer holds. In other words, it means essentially this:
Type *p = pp[n];
*p = value;
What you really want is something that does this:
Type *p = *pp;
p[n] = value;
and that is what (*pp)[n], distinguishing the dereference of the pointer to pointer, gives you. Without that, you're using an invalid pointer, leading to your fault.
Not sure whether this answer will help, but anyway.
I don't understand your code, what you are trying to do.
You directly use the number of elements, a pointer to the first person, and the maximum number of elements. You'll probably have a lot of problems passing that all around.
You're storing literal strings directly in your structs, which means that in a real case (using no literals) that would result in memory leaks.
Here is my take. I've made PEOPLE_BLOCK smaller for testing reasons.
Hope this helps.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PEOPLE_BLOCK 2
typedef struct _Person {
char *first_name;
char *last_name;
unsigned int age;
} Person;
typedef struct _VectorPeople {
Person * people;
size_t num;
size_t max;
} VectorPeople;
void init(VectorPeople *v)
{
v->max = PEOPLE_BLOCK;
v->num = 0;
v->people = (Person *) malloc( sizeof(Person) * v->max );
}
void clear(VectorPeople *v)
{
// Clear persons
Person * it = v->people;
while( ( it - v->people ) < v->num ) {
free( it->first_name );
free( it->last_name );
++it;
}
// Clear vector
v->max = v->num = 0;
free( v->people );
v->people = NULL;
}
void add(VectorPeople *v, Person *p)
{
// Reserve
if ( v->num >= v->max ) {
v->max += PEOPLE_BLOCK;
// Realloc
v->people = realloc( v->people, v->max * sizeof(Person) );
if ( v->people == NULL ) {
exit( -1 );
}
}
// Copy strings
p->first_name = strdup( p->first_name );
p->last_name = strdup( p->last_name );
// Insert
v->people[ ( v->num )++ ] = *p;
}
int main(int argc, char const *argv[]) {
VectorPeople vp;
Person timn;
Person batman;
Person bond;
Person superman;
init( &vp );
timn.first_name = "Timn";
timn.last_name = "Timothy";
timn.age = 38;
add( &vp, &timn );
batman.first_name = "Batn";
batman.last_name = "Man";
batman.age = 42;
add( &vp, &batman );
bond.first_name = "James";
bond.last_name = "Bond";
bond.age = 45;
add( &vp, &bond );
superman.first_name = "Super";
superman.last_name = "Man";
superman.age = 45;
add( &vp, &superman );
int i = 0;
for(; i < vp.num; ++i ) {
printf( "Person: %s, %s.\n", vp.people[ i ].last_name, vp.people[ i ].first_name );
}
clear( &vp );
return 0;
}
There were a number of errors in your code. One thing to keep in mind, when you dynamically allocate memory, you are responsible for keeping track of it and freeing it when you no longer need it (otherwise, you will leak memory like a sieve).
In your code, you attempt to create an array of structs holding pointer to an array of characters. The char * pointers are NOT allocated and cannot simply be assigned in the manner you attempt. strdup can help, but you have just allocated memory, so free it when you are done with it.
Attempting to allocate an array of structs with varying (unknown) lengths of first_name and last_name requires that you keep track of every allocation. In some sense, you are better off declaring people as pointer to pointer to Person This allows iteration over your people without having to store the population somewhere allowing you to iterate until the first NULL pointer is encountered.
Likewise, creating a typedef to your struct can greatly cut down on the number of times you write sizeof (struct Person). It keeps the code clean and helps you think though the pointer haze.
Here is an example using a pointer-to-pointer-to-struct of what I think you intended to do. It is followed below by an implementation using only a pointer to struct. Evaluate both and decide which implementation you prefer:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXPOP 128
typedef struct {
char *first_name;
char *last_name;
unsigned char age;
} Person;
Person *add_person (Person ***ppl, Person p, size_t *pop, size_t *max);
Person **realloc_person (Person **ppl, size_t *n);
void free_person (Person *p);
void free_person_names (Person *p);
int main (void) {
size_t population = 0;
size_t maxp = MAXPOP;
size_t i = 0;
Person timn, batman;
Person **people = calloc (MAXPOP, sizeof *people);
if (!people) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}
timn.first_name = strdup ("Timn");
timn.last_name = strdup ("Timothy");
timn.age = 38;
add_person (&people, timn, &population, &maxp);
free_person_names (&timn);
printf("\nPerson 0\n first name: %s\n last name : %s\n age : %hhu\n",
people[0]->first_name, people[0]->last_name, people[0]->age);
batman.first_name = strdup ("Bat");
batman.last_name = strdup ("Man");
batman.age = 42;
add_person (&people, batman, &population, &maxp);
free_person_names (&batman);
printf("\nPerson 1\n first name: %s\n last name : %s\n age : %hhu\n",
people[1]->first_name, people[1]->last_name, people[1]->age);
for (i = 0; i < population; i++)
free_person (people[i]);
free (people);
return 0;
}
/* add a person to an array of pointers to Person */
Person *add_person (Person ***ppl, Person p, size_t *pop, size_t *max)
{
if (*pop == *max)
*ppl = realloc_person (*ppl, max);
if (!((*ppl)[*pop] = malloc (sizeof ***ppl)))
return NULL;
size_t i = (*pop)++;
(*ppl)[i]-> first_name = strdup (p.first_name);
(*ppl)[i]-> last_name = strdup (p.last_name);
(*ppl)[i]-> age = p.age;
return (*ppl)[i];
}
/* realloc an array of pointers to Person setting memory to 0. */
Person **realloc_person (Person **ppl, size_t *n)
{
Person **tmp = realloc (ppl, 2 * *n * sizeof *ppl);
if (!tmp) {
fprintf (stderr, "Error: struct reallocation failure.\n");
// return NULL;
exit (EXIT_FAILURE);
}
ppl = tmp;
memset (ppl + *n, 0, *n * sizeof *ppl); /* memset new ptrs 0 */
*n *= 2;
return ppl;
}
/* free memory for a Person */
void free_person (Person *p)
{
if (!p) return;
if (p->first_name) free (p->first_name);
if (p->last_name) free (p->last_name);
free (p);
}
/* free only names of Person (for temp structs) */
void free_person_names (Person *p)
{
if (!p) return;
if (p->first_name) free (p->first_name);
if (p->last_name) free (p->last_name);
}
Note: updated to correct ppl start address on reallocation.
Using only Array of Person
While not inherently different than using a pointer to pointer to Person using a simple pointer to Person eliminates the ability to iterate over your array until a NULL or (empty) pointer is encountered. The following is an implementation of the same code using only an array of Person:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXPOP 128
typedef struct {
char *first_name;
char *last_name;
unsigned char age;
} Person;
Person *add_person (Person **ppl, Person p, size_t *pop, size_t *max);
Person *realloc_person (Person *ppl, size_t *n);
void free_person_names (Person p);
int main (void) {
size_t population = 0;
size_t maxp = MAXPOP;
size_t i = 0;
Person timn, batman;
Person *people = calloc (MAXPOP, sizeof *people);
if (!people) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}
timn.first_name = strdup ("Timn");
timn.last_name = strdup ("Timothy");
timn.age = 38;
add_person (&people, timn, &population, &maxp);
free_person_names (timn);
printf("\nPerson 0\n first name: %s\n last name : %s\n age : %hhu\n",
people[0].first_name, people[0].last_name, people[0].age);
batman.first_name = strdup ("Bat");
batman.last_name = strdup ("Man");
batman.age = 42;
add_person (&people, batman, &population, &maxp);
free_person_names (batman);
printf("\nPerson 1\n first name: %s\n last name : %s\n age : %hhu\n",
people[1].first_name, people[1].last_name, people[1].age);
for (i = 0; i < population; i++)
free_person_names (people[i]);
free (people);
return 0;
}
/* add a person to an array of pointers to Person */
Person *add_person (Person **ppl, Person p, size_t *pop, size_t *max)
{
if (*pop == *max)
*ppl = realloc_person (*ppl, max);
size_t i = (*pop)++;
(*ppl)[i].first_name = strdup (p.first_name);
(*ppl)[i].last_name = strdup (p.last_name);
(*ppl)[i].age = p.age;
return ppl[i];
}
/* realloc an array Person setting memory to 0. */
Person *realloc_person (Person *ppl, size_t *n)
{
Person *tmp = realloc (ppl, 2 * *n * sizeof *ppl);
if (!tmp) {
fprintf (stderr, "Error: struct reallocation failure.\n");
// return NULL;
exit (EXIT_FAILURE);
}
ppl = tmp;
memset (ppl + *n, 0, *n * sizeof *ppl); /* memset new ptrs 0 */
*n *= 2;
return ppl;
}
/* free only names of Person (for temp structs) */
void free_person_names (Person p)
{
if (p.first_name) free (p.first_name);
if (p.last_name) free (p.last_name);
}
Output
$ ./bin/struct_add_person
Person 0
first name: Timn
last name : Timothy
age : 38
Person 1
first name: Bat
last name : Man
age : 42
One problem is the last argument of add_person() to be specific, the argument '(struct Person) p'. When 'timn' and 'batman' are passed into the add_person() function, they are passed as a copy of the original structure. In the add_person() structure, that data is actually on the stack and is volatile outside the scope of the function. Try changing the last argument to a pointer.

parse a string for the required value

I have a data as shown below, it contains the name of person and age respectively, her i have shown just 3 person names and the respective age, i can also have many names with respective age in that string.
I want to parse this string and store the name and corresponding age in a structure, can u please tell me how to parse this name and age. Ex: I want to get Allan 35 and store it in the below structure in name and age field respectively.So and forth for all the names present in that string.
How to parse the string, i tried strstr() but it was of no use for me. Pls can anyone tell how to parse this string.
struct data_base{
char *name;
int age;
};
char data[] = "Name Allan Age 35 Name John Age 50 Name Jim Age 20 ....."
You can use strtok to extract the tokens.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct data_base{
char *name;
int age;
};
#define NAME_TAG "Name "
#define AGE_TAG "Age "
char* getName(char **p){
int tag_size = strlen(NAME_TAG);
if(strncmp(*p, NAME_TAG, tag_size)==0){
char *agep = strstr(*p += tag_size, AGE_TAG);
int len = agep - *p -1;//-1: for space before "Age"
char *name = calloc(len + 1, sizeof(char));
strncpy(name, *p, len);
*p = agep;
return name;
}
return NULL;
}
int getAge(char **p){
int tag_size = strlen(AGE_TAG);
if(strncmp(*p, AGE_TAG, tag_size)==0){
int age = (int)strtol(*p += tag_size, p, 10);
if(**p == ' ')
*p += 1;
return age;
}
return -1;
}
struct data_base *parse(char *data, size_t *size){
char *p = data;
struct data_base *dbp=NULL;
*size = 0;
while(*p){
*size += 1;
dbp = realloc(dbp, sizeof(struct data_base)*(*size));
dbp[*size -1].name = getName(&p);//this is treated as format is correct
dbp[*size -1].age = getAge(&p);
}
return dbp;//realloc(dbp, sizeof(struct data_base)*(*size));
}
int main(void){
char data[] = "Name Allan Age 35 Name John Age 50 Name Jim Age 20";
struct data_base *dbp;
size_t i, size;
dbp = parse(data, &size);
//check print
for(i = 0; i < size ;++i){
printf("Name: %s, Age: %d\n", dbp[i].name, dbp[i].age);
}
//deallocate
return 0;
}
You might want to use sscanf. You'll need %s format for person's name and %d for age.
u can use two pointer to get the begin and end of a word,
use pointer p by strstr to get the place of "Name", then forth 5 to get the begin place of the name , then pointer q by strchr with space to get the end of the name, then you can use strncpy of snprintf to get the exactly name
Create function to handle each record, using sscanf().
// Scan string, return >0 if successful, 0 if done, else error code
int JZ_ScanDB(const char *data, int *Index, struct data_base *Dest) {
if (Dest) {
Dest->age = 0; // Form default answer
Dest->name = 0;
}
if (!data || !Index || !Dest) return -1; // Gremlins in your code
if ((*Index < 0) || (*Index > strlen(data))) return -2; // More gremlins
if (data[*Index] == '\0') return 0; // we are done
char Name[1000];
int retval = sscanf(&data[*Index], "Name %s Age %d %n", Name, &Dest->age, Index);
if (retval != 2) {
return -3;
}
Dest->name = strdup(Name);
return 0;
}
void DoIt() {
char data[] = "Name Allan Age 35 Name John Age 50 Name Jim Age 20";
int i = 0;
struct data_base Person;
int ret;
while ((ret = JZ_ScanDB(data, &i, &Person)) > 0) {
// do something with Person, watch for memory management
}
if (ret) {
// deal with unusual reason for stopping
}
}

Resources