The code as posted has many errors. You can fix all them --- and even get the code to work this way --- but is not a good program.
Also I think that you are expecting some things that are not true.
About the questions and comments
My requirement is to add data of different datatypes into dynamically allocated structure memory
Well, to add data to structures you use assignment. Makes no difference if the struct
is static or dynamically allocated.
When I use scanf(), it is not taking inputs properly, even I included %*c to take up the new line character, '\n'
Do you have a text book? A manual? A reference site you believe in?
scanf()
is from the '70s and you can be sure it takes inputs properly.
- Do you know that
scanf()
returns an int
? What about test it just to see if something was read, instead of just blindly going ahead?
- The
*%c
will not "take up" a \n
. The newline is the one that makes scanf()
return. It is your program that has to consume the data properly
scanf()
was written to consume tabular data, like csv files with 1000's of lines of values, possibly separated by, well, commas --- as csv in Comma Separated Values. It was never meant to be used to read input data from the keyboard. At least not until instructors and authors started to teach using scanf()
this way. A bad move, I believe.
When the user enters spaces in between his first input, the scanf() is not taking the second argument, age as input.
Yes! By design scanf() skips white space --- spaces, tabs and newlines --- in the search for data to fill data as in the specifiers, those things that starts with %
and not with %%
.
How to use gets() to take integer input and use it in dynamically allocated memory
Well, do use. gets()
is obsolete and could not even compile. Use fgets()
.
I even tried using atoi() to convert char to int as well
This always work, but with const char*
not char
. Read the manual.
I need to dynamically allocate structure memory and add data of different data types into it
These are unrelated stuff.
about the code as posted
This is your definition of the data your program works it:
typedef struct node{
char name[15];
int age;
}details;
Why node
? It is not used. Not mentioned anywhere. If it is a node is a node of what? Some kind of data structure? A linked list? An array? A map? A queue? A tree?
But there is no such thing in the program yet...
In main
you have this
details* ptr = (details*)malloc(sizeof(details));
entry(ptr,n);
So one can think that your data sctructure is a dynamically allocated array of n
values...
If so, just write this. It is easier for you. It is called encapsulation and it is very simple, yet powerful.
See an example:
typedef struct
{
char name[15];
int age;
} Details;
typedef struct
{
unsigned cap; //capacity
unsigned size; // size now
Details* data; // and the data
} My_data;
So your unit of work is My_data
. At any time you have size
items and, sure, it is unsigned: makes not much sense have a size
of -42
for example.
- Do not write interactive code for a program that is still not working.
- Do not mix data entry with logic.
A C
example from the start
I will write a program like this one now, here, step by step, since this is a fairly common example, posted with fairly common mistakes, and a complete code can be useful to others.
**
EDIT: It is a giant post due to the copy and paste and interactive way of writing. Sorry for that: No TL;DR, just DR
how to create My_data
Sure, there are countless ways. But look this one:
My_data* so_create(unsigned capacity)
{
if (capacity == 0) return NULL; // error
My_data* cnt = (My_data*)malloc(sizeof(My_data));
if (cnt == NULL) return NULL; // error
cnt->size = 0; // sure it is empty
cnt->cap = capacity;
cnt->arr = (int*)malloc(capacity * sizeof(int));
if (cnt->arr == NULL)
{ // malloc for the array failed
free(cnt);
return NULL;
};
return cnt; // ok
}
Just return a pointer to one My_data
structure with the expected capacity, but empty. Or return NULL
for an error. Simple or not?
The size is inside, the capacity is inside, the data is inside.
one = so_create(400);
The line above returns a pointer to such struct
, empty but capable of holding 400
Detail
data.
[off topic]
Many people will tell you to not cast the pointer returned by mallloc()
. It is like a cult. But a cult from the 80's and 90's based on a book from '95 and a document called C FAQ. And even in these book and document are said that it may be such not an absolute fact.
A funny example from the C_FAQ: one reason for never cast the pointer is to be able to get a compiler error if the user forgets to include stdlib.h
in the code. If you do that your problem is bigger than a cast.
For the compiler it makes no difference. But for you, reading your code or, worst, reading code of others, this (cast the result) can be a life saver.
No surprise, this document was never updated, this book had no revisions, and as time passed --- decades --- many people saw that implicit things are not so good after all. Even things like auto
in C++
are not welcome in some circles, and authors like Scott Meyers and Herb Sutter write against is use.
See this modest 12-line code above and you will note thart you malloc
two types of things in a 5-line piece of code. Compare:
My_data* cnt = (My_data*)malloc(sizeof(My_data));
if (cnt == NULL) return NULL; // error
cnt->size = 0; // sure it is empty
cnt->cap = capacity;
cnt->arr = (int*)malloc(capacity * sizeof(int));
with
My_data* cnt = malloc(sizeof(My_data));
if (cnt == NULL) return NULL; // error
cnt->size = 0; // sure it is empty
cnt->cap = capacity;
cnt->arr = malloc(capacity * sizeof(int));
And may be you like the casts as a note to the reader of what is what. The argument of malloc
is just an expression that evaluates to a number. Or just a number. So anything you can do to tell what is what is there to help. If you invert the expressions for example, the use of a cast can be a warning for you before your program crashes...
Anyway, lost are the days when we could choose the way we write code and not our school or our employer or client. There are coding conventions everywhere.
[on-topic]
Back to the code...
Note that we have a way to create My_data
and now may be the time to think on how to destroy one. This is a good thing to do when writing C
code to allocate things. BEFORE writing any more code in the program write and possibly test the other side:
See
My_data* so_destroy(My_data* gone)
{
if (gone == NULL) return NULL; // safe
// we have an array to free
free(gone->arr); // free the array[]
free(gone); // free the sruc itself
return NULL;
}
A program like
int main(void)
{
My_data* a_test = so_create(42);
a_test = so_destroy(a_test);
return 0;
}
Can now be compiled and run. It is good for moral support. And to see that we can for sure free all data.
Why is this strange thing returning always NULL
? Well, it is a warranty that a pointer will not the left in the code pointing to deleted memory, since it is se to NULL
at the same time the struct
it points to is destroyed.
is there any chance we will not want to display the data?
No, there is not. We are learning and we want reassurance of things.
So, write it in the first place... And we will add a title because i makes one miserable to keep writing printf()
s to mark each test
int so_display(My_data* data, const char* title)
{
if (title != NULL) printf("%s", title);
if (data == NULL)
{
printf("\nArray does not exist\n");
return -1;
}
printf(
"\n[Array size is: %u of %u]:\n", data->size,
data->cap);
for (unsigned i = 0; i < data->size; i += 1)
printf(
" %4d: %s, %d\n", 1+i, data->arr[i].name,
data->arr[i].age );
printf(
"[end: %d listed, for a capacity of %d "
"records]\n\n",
data->size, data->cap);
return 0;
};
It is a simple thing. And it is handy to have an optional title. Now this program can run
int main(void)
{
My_data* a_test = so_create(42);
so_display(a_test, "still empty");
a_test = so_destroy(a_test);
return 0;
}
and shows
still empty
[Array size is: 0 of 42]:
[end: 0 listed, for a capacity of 42 records]
Good, since we did almost nothing and already have some results. But we can probably create, destroy and display this thing we did not have in fact yet. You know, moral support.
what about some data?
Sure, let us write a function that insert data into the array...
How to name such a thing? How to use such a thing?
Well, we want to insert a Detail
record --- Pascal naming --- into our My_data
thing:
int so_insert( Detail* thing, My_data* block);
Why return an int
? What about that great void
?
We want to always return something, like 0
for success and some negative value for error, a C
favorite.
Should it be complex? An implementation
int so_insert(Detail* thing, My_data* block)
{
if (thing == NULL) return -1;
if (block == NULL) return -2;
// full, obvious
if (block->size >= block->cap) return -3;
block->arr[block->size] = *thing;
block->size += 1;
return 0;
};
Detail
is only name
and age
so we can just copy. What can go wrong?
- no valid
Detail
: return -1
- no valid
My_data
: return -2
- array is full: return -3
We already have so_display
, and want to insert data but we will not go to something interactive. Interactive code is never a good plan.
What if we had a list of
- "student 1", age "11"
- "student 2", age "12"
- "student 3", age "13"
- "student 4", age "14"...
and so_insert()
this Detail
records until the array is full? Then we call so_display()
to see if its ok and the so_destroy()
to free the memory and for a beer we go...
Would it be complex? Well, not really...
Detail* so_factory()
{
Detail* student = (Detail*) malloc(sizeof(Detail));
if (student == NULL) return NULL;
static number = 1000; // starting number
student->age = number;
sprintf( student->name, "Student %6d", number); // char[15]
number = number + 1;
return student;
};
Just that: allocates a record to be inserted. The first one will have name= "Student 1"
, age=11
, and the next will be 2 and it is fine
Again for moral support, this should work:
int main(void)
{
My_data* a_test = so_create(42);
so_display(a_test, "still empty");
// get a record to insert
Detail* one = so_factory();
so_insert(one, a_test);
free(one); //already copied
so_display(a_test, "one record inserted");
a_test = so_destroy(a_test);
return 0;
}
And it does:
still empty
[Array size is: 0 of 42]:
[end: 0 listed, for a capacity of 42 records]
one record inserted
[Array size is: 1 of 42]:
1: Student 1000, 1000
[end: 1 listed, for a capacity of 42 records]
what about a full set?
Since we have so_factory
and each struct
has its own capacity and so_insert
returns 0
for ok, it can be easy: just loop until error:
int main(void)
{
My_data* a_test = so_create(4);
int res = 0;
while ( res == 0)
{ // loop until error
Detail* one = so_factory();
res = so_insert(one, a_test);
free(one); // already copied
};
so_display(a_test, "must be full now...");
a_test = so_destroy(a_test);
return 0;
}
and it shows
must be full now...
[Array size is: 4 of 4]:
1: Student 1000, 1000
2: Student 1001, 1001
3: Student 1002, 1002
4: Student 1003, 1003
[end: 4 listed, for a capacity of 4 records]
Not bad.
what about the user input?
Now we have the basics. If we can read data it is just a matter of calling so_insert
and gone we are.
There are countless way to do this. Let us just read one by one in a line. This is not important. We say: Name: get a name, We say Age and get age. We build a Detail
and so_insert()
it as we already did... Well, we will just copy so_factory()
and make some changes...
Is it complex? Let try
Detail* so_input()
{
Detail* student = (Detail*)malloc(sizeof(Detail));
if (student == NULL) return NULL;
printf(" Name: ");
char line[15];
char* p = fgets(line, sizeof(line), stdin);
if (p == NULL)
{
free(student);
return NULL; // fgets failed.
}
// remove a possible '\n' at the end
int l = (int) strlen(line) - 1;
if (line[l] == '\n') line[l] = 0;
strcpy(student->name, line);
printf(" Age: ");
p = fgets(line, sizeof(line), stdin);
if (p == NULL)
{
free(student);
return NULL; // fgets failed.
}
student->age = atoi(line);
return student;
}
Just as planned. Note that fgets
will add a \n
at the end of name unless the user uses all space. Read the manual.
This code will do a test:
int main(void)
{
My_data* a_test = so_create(4);
int res = 0;
Detail* one = so_input();
if (one == NULL) return -1;
so_insert(one, a_test);
free(one);
so_display(a_test, "one record was read...");
a_test = so_destroy(a_test);
return 0;
}
and we have
Name: a b c d e
Age: 42
one record was read...
[Array size is: 1 of 4]:
1: a b c d e, 42
[end: 1 listed, for a capacity of 4 records]
Sure, so_input
was copied from so_factory
.
I wrote this post right from the IDE, including the text itself, in order to show a way to go with this type of problem: use of a container in C
, since almost all exercises are based in some sort of container.
complete code
It is not well tested. It is just one way of doing this, not the way of doing this. But worked at first test and shows a way to proceed. With code and arguments. Hope it helps.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
char name[15];
int age;
} Detail;
typedef struct
{
unsigned cap; // capacity
unsigned size;
Detail* arr; // and the data
} My_data;
My_data* so_create(unsigned capacity);
My_data* so_destroy(My_data* gone);
int so_display(My_data* data, const char* title);
Detail* so_input();
int so_insert(Detail* thing, My_data* block);
Detail* so_factory(); // gets a numbered name/age pair
int main(void)
{
My_data* a_test = so_create(4);
int res = 0;
Detail* one = so_input();
if (one == NULL) return -1;
so_insert(one, a_test);
free(one);
so_display(a_test, "one record was read...");
a_test = so_destroy(a_test);
return 0;
}
// https://stackoverflow.com/questions/76993795/how-to-dynamically-allocate-structure-memory-and-add-data-of-different-data-type
My_data* so_create(unsigned capacity)
{
if (capacity == 0) return NULL; // error
My_data* cnt = (My_data*)malloc(sizeof(My_data));
if (cnt == NULL) return NULL; // error
cnt->size = 0; // sure it is empty
cnt->cap = capacity;
cnt->arr = (Detail*)malloc(capacity * sizeof(Detail));
if (cnt->arr == NULL)
{ // malloc for the array failed
free(cnt);
return NULL;
};
return cnt; // ok
}
My_data* so_destroy(My_data* gone)
{
if (gone == NULL) return NULL; // safe
// we have an array to free
free(gone->arr); // free the array[]
free(gone); // free the sruc itself
return NULL;
}
int so_display(My_data* data, const char* title)
{
if (title != NULL) printf("%s", title);
if (data == NULL)
{
printf("\nArray does not exist\n");
return -1;
}
printf(
"\n[Array size is: %u of %u]:\n", data->size,
data->cap);
for (unsigned i = 0; i < data->size; i += 1)
printf(
" %4d: %s, %d\n", 1 + i, data->arr[i].name,
data->arr[i].age);
printf(
"[end: %d listed, for a capacity of %d "
"records]\n\n",
data->size, data->cap);
return 0;
}
Detail* so_input()
{
Detail* student = (Detail*)malloc(sizeof(Detail));
if (student == NULL) return NULL;
printf(" Name: ");
char line[15];
char* p = fgets(line, sizeof(line), stdin);
if (p == NULL)
{
free(student);
return NULL; // fgets failed.
}
// remove a possible '\n' at the end
int l = (int) strlen(line) - 1;
if (line[l] == '\n') line[l] = 0;
strcpy(student->name, line);
printf(" Age: ");
p = fgets(line, sizeof(line), stdin);
if (p == NULL)
{
free(student);
return NULL; // fgets failed.
}
student->age = atoi(line);
return student;
}
int so_insert(Detail* thing, My_data* block)
{
if (thing == NULL) return -1;
if (block == NULL) return -2;
// full, obvious
if (block->size >= block->cap) return -3;
block->arr[block->size] = *thing;
block->size += 1;
return 0;
}
Detail* so_factory()
{
Detail* student = (Detail*)malloc(sizeof(Detail));
if (student == NULL) return NULL;
static number = 1000; // starting number
student->age = number;
sprintf(
student->name, "Student %6d", number); // char[15]
number = number + 1;
return student;
};