I have a struct Student. I initialise it inside the main function, and i try to add students inside a function. It doesn't work, but if i put the same code into the main function, it works normally. What am i doing wrong?
struct Student
{
char* name;
};
void addStudent(struct Student *student, int *n)
{
(*n)++;
if(*n != 1)
{
student = realloc(student, (*n) * sizeof(struct Student));
}
printf("Insert name : ");
student[*n-1].name = malloc(25 * sizeof(char));
scanf("%s", student[*n-1].name);
for(int i=0;i<*n;i++)
{
printf("name: %s\n", student[i].name);
}
}
main:
int main() {
int n=0;
struct Student *student = malloc(1 * sizeof(struct Student));
while(1)
{
addStudent(student, &n);
}
}
tl;dr: You need your argument to be of struct Student ** since you are using malloc()/realloc() inside a function.
As you are aware, when an argument is passed in a function, its value is copied so that any changes made inside the function will not be visible to the calling code. We overcome this by passing a pointer to the value we want to modify: we do not change the actual value of our argument, we are only messing with the memory location our pointer argument points to.
In your code, you are reallocating space inside your function, which means that the value of the pointer itself is modified. So, this change is "discarded" after the end of the function and your main never sees the reallocated pointer (note that the allocated space is still "there", meaning that your program takes that resource, you have just no pointer referencing it after the end of your function which is a memory leak).
The solution: make the argument of type pointer to the pointer you want to reallocate, i.e. struct Student **.
The changes you need to do are the following:
void addStudent(struct Student **pstudent, int *n) {
(*n)++;
if(*n != 1){
*pstudent = realloc(*pstudent, (*n) * sizeof(struct Student));
}
(*pstudent)[*n-1].name = malloc(25 * sizeof(char));
scanf("%s", (*pstudent)[*n-1].name);
for(int i=0;i<*n;i++)
{
printf("name: %s\n", (*pstudent)[i].name);
}
}
and in your main() just call your function as:
addStudent(&student, &n);
Related
I'm learning pointers and struct now and I'm a bit confused. Particularly, I want to make a function which returns a pointer to a struct. My code is compiling, but it isn't working. After I type the number of students, it asks for name and age (without reading name), and after I type the age, it closes.
#include <stdio.h>
#include <stdlib.h>
struct details
{
char name[100];
int age;
};
struct details * details_pointer(int n)
{
struct details pointer_x[n];
struct details *pointer = pointer_x;
for (int i=0; i<n; i++)
{
printf("Student %d:\n", i);
printf("name:\n");
fgets(pointer[i].name, 100, stdin);
printf("age:\n");
scanf("%d", pointer[i]. age);
}
return pointer;
}
int main()
{
int n;
printf("Type the number of persons:\n");
scanf("%d", &n);
struct details *student = details_pointer(n);
printf("\nName: %s\n", (*student).name);
printf("Age: %d\n", (*student).age);
system("pause");
return 0;
}
The problem here is that you're returning a structure variable (pointer_x) that's allocated locally on the stack of the inner details_pointer() function, but this memory is no longer reserved for you once it returns. This means you get (at best) garbage.
You have to either allocate memory in the function and return it (and remember to free it!) or pass the data to the function to fill it in.
void get_details(int n, struct details p[n])
{
for (int i = 0; i < n; i++)
{
// do stuff with p[i]
}
}
int main()
{
...
scanf("%d", &n);
struct details students[n];
get_details(n, students);
printf("\nName: %s\n", students[0].name);
...
}
I generally prefer passing data to the function when possible just because it simplifies memory management.
EDIT: Two notes about your existing details_pointer() function.
1) In fgets(pointer[i].name, 100, stdin), it's a good practice to derive the number of bytes from the size of the array if it's known; in this case it is, so recommend fgets(pointer[i].name, sizeof(pointer[i].name), stdin). Yours is not incorrect, but it's easier to maintain should the sizes change in the future.
2) the scanf("%d", pointer[i].age) needs to take the address of the .age to stuff its value in there; you're passing a number instead of an address, which is for sure incorrect.
Ok, so if the assignment requires dynamic allocation, then that's what we have to do.
Just specifying an array implicitly allocates the space while the function is running, but it disappears when the function returns. You'll need to allocate it specifically.
Revisiting your code:
struct details *details_pointer(int n)
{
struct details *students = (struct details *)malloc(n * sizeof(struct details));
.... do stuff with students[0] and the like
return students;
}
This uses the malloc() - memory allocate - library function to get you some space, and it remains valid until you release it. The number of bytes is how many things you want (n) times how many bytes in one thing (sizeof(struct details))
Later:
int main()
{
struct details *student = details_pointer(n);
.. do stuff
free(student);
}
This now uses that memory, and releases it back to the OS when you're done.
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;
}
I have written the following code to resize an array if the an item is going to go out of range when storing it. This code works as intended.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//! Our location struct for storing locations
struct location
{
char loc_name[35];
char description[85];
float latitude;
float longitude;
};
void runMenu();
void add_location(struct location** p_location_array, int* p_array_size, int* p_current_size);
void resize_array(struct location** p_location_array, int* p_array_size);
void print (struct location* p_array, int p_current_size);
// Print out the main menu to the user.
void runMenu()
{
printf("[A]dd additional Locations\n");
printf("[P]rint the current list of locations\n");\
printf("[Q]uit the program\n");
}
//! Resize the array to two times it's origional size.
void resize_array(struct location** p_location_array, int* p_array_size)
{
// Allocate enough space for two times the size of the array
int new_size = 2 * (*p_array_size);
struct location* new_location_array = malloc(new_size * sizeof(struct location));
if (!new_location_array)
{
printf ("Cannot add more elements heap has exhausted all space\n");
exit(1);
}
// Copy the old array to the new array.
memcpy(new_location_array, *p_location_array, ((*p_array_size ) * sizeof(struct location)));
// We will update the current size of the array for later checking.
*p_array_size = 2 * (*p_array_size);
// We have a copy of the old array so we can free it.
free(*p_location_array);
// The contents of the pointer reference get the array we malloced in this function
*p_location_array = new_location_array;
}
//! Add a new location to our array. If the array isn't large enough resize it then insert the new struct.
void add_location(struct location** p_location_array, int* p_array_size, int* p_current_size )
{
// Get the users input
struct location new_location;
printf("Enter the new location name\n ");
fscanf(stdin, "%s", new_location.loc_name);
printf("Enter a description of the location\n");
fscanf(stdin, "%s", new_location.description),
printf("Enter the latitude\n");
fscanf(stdin, "%f", &new_location.latitude);
printf("Enter the longitude\n");
fscanf(stdin, "%f", &new_location.longitude);
// Check to see if the size is correct.
if (*p_array_size <= *p_current_size)
{
// If not the correct size resize the array
resize_array(p_location_array, p_array_size);
}
// Insert our sruct
(*p_location_array)[*p_current_size] = new_location;
}
//! Loop over and print out the locations
void print (struct location* p_array, int p_current_size)
{
int i;
for (i = 0; i < p_current_size; i++)
{
struct location current = p_array[i];
printf("%s : %s : %f : %f\n", current.loc_name, current.description, current.latitude, current.longitude);
}
}
int main()
{
char choice = ' ';
short control = 1;
int size;
int currentSize = 0;
printf("Enter the inital size of the array\n");
scanf(" %d", &size);
// Make a new struct array from the heap
struct location* m_location_array =
malloc(size * sizeof(struct location));
// Make sure we have a valid chunk of the heap.
if (!m_location_array)
exit(1);
while(control)
{
runMenu();
scanf(" %c", &choice);
switch (choice)
{
case 'a':
case 'A':
// Do Add additional
add_location(&m_location_array, &size, ¤tSize);
currentSize++;
break;
case 'p':
case 'P':
// Do printing
print (m_location_array, currentSize);
break;
case 'Q':
case 'q':
control = 0;
break;
default:
printf("Invalid input\n");
}
}
// clean up after ourselves.
free (m_location_array);
return 0;
}
However, when I originally wrote this function I thought it would be possible to just pass in a the pointer to the array instead of a reference to the pointer like so:
void resize_array(struct location* p_location_array, int* p_array_size)
Calling this function without the reference to pointer threw a segfault indicating the memory was being doubly freed. Is that because the pointer when passed to the function someway gets freed and reallocated? Furthermore, why is it necessary to pass a pointer by reference like this? Even if the pointer is a copy of the original pointer wouldn't it still point to the same piece of memory? Any points in the correct direction is much appreciated.
You gave a pointer to the function and you have a call to free there. So the memory is freed. After that using that pointer causes undefined behaviour and you may not use it.
Modifying the pointer variable inside the function doesn't change the pointer outside the function. That is why you need a pointer to the pointer so that you can modify the pointer variable outside the function.
Even if the pointer is a copy of the original pointer wouldn't it still point to the same piece of memory?
Yes, and that is the point: it will keep on pointing to the same place unless you change it. If you do a new malloc it will point to a completely different place.
Also a hint: realloc might be worth checking out.
If you pass pointer to function it is a copy of original one. Then even if you assign to this pointer inside the function, like
p_location_array = new_location_array;
the original pointer (that outside the function) still has unchanged value. So in case the original pointer pointed to some memory area and you have passed it to the function
void resize_array(struct location *p_location_array, int* p_array_size)
and you have called free() inside thefunction and assigned NULL to the pointer, after your function has returned the original pointer will comapre as not NULL.
// warning, changed prototype
void resize_array(struct location *p_location_array, int* p_array_size);
struct location *loc = malloc(size * sizeof(struct location)); // assume loc = 0x12345678
if (loc == NULL) EXIT_FAILURE;
// change pointer inside the function
// assign NULL to the pointer
resize_array(loc, size_p);
if (loc != NULL)
free(loc); // this will be called, loc is still 0x12345678,
// double free, UB
I have a structure in c and I want to allocate memory for an array of my structure,
for this I'd created a function.
The problem is that only first element of array is working.
If I do allocation in main, it is working.
Here is my code:
typedef struct {
int id;
char* name;
}Car;
void read(Car**cars){
int n,i;
char name[50];
printf("Cars:"); scanf("%i",&n);
*cars = (Car*) malloc(n * sizeof(Car));
for(i=0;i<n;i++) {
printf("\nCar Name[%i]: ",i); scanf("%s",name);
(*cars[i]).id = i; //when i>0 then It crash here...
(*cars[i]).name = (char*) malloc(strlen(name)+1);
strcpy((*cars[i]).name, name);
printf("Cars Name -> %s ", (*cars[i]).name);
}
}
int main() {
Car *cars = NULL;
read(&cars);
return 0;
}
what I am doing wrong?
(*cars)[i].id = i; //when i>0 then It crash here...
This should work for you.
explanation:
you allocate n elements to the value of the pointer to the cars pointer.
but you try to derefference the value of the ith pointer to a cars element.
With my correction you derefference the firsts pointer's ith cars id. what is bad coded but should do what you want.
But what you probabbly want to do is asigning multiple pointers which at all point to a single car pointer instead ;)
than your crashing line could stay at it is.
So I'm trying to learn C right now, and I have some basic struct questions I'd like to clear up:
Basically, everything centers around this snippet of code:
#include <stdio.h>
#include <stdlib.h>
#define MAX_NAME_LEN 127
typedef struct {
char name[MAX_NAME_LEN + 1];
unsigned long sid;
} Student;
/* return the name of student s */
const char* getName (const Student* s) { // the parameter 's' is a pointer to a Student struct
return s->name; // returns the 'name' member of a Student struct
}
/* set the name of student s
If name is too long, cut off characters after the maximum number of characters allowed.
*/
void setName(Student* s, const char* name) { // 's' is a pointer to a Student struct | 'name' is a pointer to the first element of a char array (repres. a string)
char temp;
int i;
for (i = 0, temp = &name; temp != '\0'; temp++, i++) {
*((s->name) + i) = temp;
}
/* return the SID of student s */
unsigned long getStudentID(const Student* s) { // 's' is a pointer to a Student struct
return s->sid;
}
/* set the SID of student s */
void setStudentID(Student* s, unsigned long sid) { // 's' is a pointer to a Student struct | 'sid' is a 'long' representing the desired SID
s->sid = sid;
}
I've commented up the code in an attempt to solidify my understanding of pointers; I hope they're all accurate.
Also, I have another method,
Student* makeAndrew(void) {
Student s;
setName(&s, "Andrew");
setStudentID(&s, 12345678);
return &s;
}
which I'm sure is wrong in some way... I also think my setName is implemented incorrectly.
Any pointers? (no pun intended)
This is very wrong. If you insist on not using strcpy do something like this (not tested)
int iStringLength = strlen(name);
for (i = 0; i < iStringLength; i++) {
s->name[i] = name[i];
}
but make sure that the length is not longer than your array size.
This is also wrong
Student* makeAndrew(void) {
Student s;
setName(&s, "Andrew");
setStudentID(&s, 12345678);
return &s;
}
because the s object is destroyed when the function exits - it is local to the function scope and yet you return a pointer to it. So if you try to access the struct using this pointer it will not be valid as the instance no longer exists. If you want to do this you should dynamically allocate it using malloc . Alternatively do not return a pointer at all and use the alternative option of #Andrew .
In your "another method" you are locally declaring Student s, which will dynamically allocate space (usually on the stack) and you are returning that address on completion.
However, that stack-space will be released on the return, so there is no guarantee that the data is uncorrupted - in fact the likelyhood is that it will be!
Declare Student s in the call to your method, and pass the pointer to makeAndrew:
void makeAndrew(Student *s) {
setName( s, "Andrew");
setStudentID( s, 12345678);
}
...
Student s;
makeAndrew( &s );
...
Your function makeAndrew returns pointer to a local variable. It is only valid before the scope ends, so as soon as the function finishes, it will change when the memory gets overwritten - i. e. almost instantly. You would have to allocate it dynamically (using Student *s = new Student;, or if you really want to stick to pure C, Student *s = malloc (sizeof Student );, and then free it outside the function after it is not needed to avoid memory leak.
Or do it as Andrew suggested, it's less error-prone.
I would change the makeAndrew() function to just return a struct, not a pointer to a struct to correct the error with respect to returning a pointer to a temporary variable:
Student makeAndrew(void)
{
Student s;
setName(&s, "Andrew");
setStudentID(&s, 12345678);
return s;
}
Student aStudent = makeAndrew();
Your setName does have an error with respect to temp, which should be a char *, since you are incrementing it in your loop to point to another character in the input c-string. I think it was missing the null termination as well. And as you mention in your comment, there should be a check for overflow of the name char array in Student:
void setName(Student* s, const char* name) { // 's' is a pointer to a Student struct |
// 'name' is a pointer to the first element of a char array (repres. a string)
const char *temp;
int i;
for (i = 0, temp = name; *temp != '\0' && i <= MAX_NAME_LEN; temp++, i++)
{
*((s->name) + i) = *temp;
}
s->name[i] = '\0';
}
You can use strncpy to simplify setName:
void setName2(Student *s,const char *name)
{
#include <string.h>
strncpy(s->name, name,MAX_NAME_LEN);
s->name[MAX_NAME_LEN] = '\0';
}