This question already has answers here:
error: function returns address of local variable
(8 answers)
Closed 4 years ago.
I've been trying to write a program to solve a problem (ex. 19, Chapter 10 'C How to Program' 8th Ed, Deitel & Deitel), but I'm having a lot of trouble trying to identify the source of an issue I'm having.
I've been trying to pass some data to a function 'setData', which assigns the various values passed in to the members of a structure 'HealthProfile'. This seems to be happening successfully, and the function returns a pointer to a struct object to main. The pointer is then passed to function 'printStruct', and this is where the problem occurs. Each time the pointer is passed to the function, the function seems to be altering the values stored in each of the structure members, but I don't know why. I'm not trying to alter the member values, the point of passing the pointer to the structure to each function is so that the functions have access to the values contained in the members (my actual program has other functions, but I haven't included them because I'm still working on them, plus the issue I'm having is illustrated by function 'printStruct' alone.
Can anyone tell me where I've gone wrong?
I have tried a lot of different things, but nothing seems to work. I suspect that maybe the solution to the problem is that I should be passing a pointer to a pointer to the functions instead of a pointer, but I haven't had any luck in trying to fix the program this way. I also thought maybe I should be declaring the structure members as constant, but again no luck.
I've included a few printf statments in main to illustrate that the value of the pointer hasn't changed, but the value of the members of the structure have after the first call of function 'printStruct' (if printStruct is called a second time, a segmentation fault occurs).
#include <stdio.h>
typedef struct {
char *firstName;
char *lastName;
char *gender;
int birthDay, birthMonth, birthYear;
double height, weight;
} HealthProfile;
HealthProfile * setData(char first[20], char last[20], char gender[2],
int BirthDay, int BirthMonth, int BirthYear,
double Height, double Weight);
void printStruct(HealthProfile * variablePtr);
int main(void)
{
char FirstName[20], LastName[20], Gender[2];
int age, BirthDay, BirthMonth, BirthYear, maxRate = 0, targetRate = 0;
double bmi, Height, Weight;
HealthProfile *variablePtr;
puts("\n** Health Profile Creation Program **");
printf("\n%s\n\n%s", "Enter First Name", "> ");
scanf("%s", FirstName);
printf("\n%s\n\n%s", "Enter Last Name", "> ");
scanf("%s", LastName);
printf("\n%s\n\n%s", "Enter Gender (M/F)", "> ");
scanf("%s", Gender);
printf("\n%s\n\n%s", "Enter date of birth (dd/mm/yyyy)", "> ");
scanf("%d/%d/%d", &BirthDay, &BirthMonth, &BirthYear);
printf("\n%s\n\n%s", "Enter Height (m)", "> ");
scanf("%lf", &Height);
printf("\n%s\n\n%s", "Enter Weight (kg)", "> ");
scanf("%lf", &Weight);
variablePtr = setData(FirstName, LastName, Gender, BirthDay,
BirthMonth, BirthYear, Height, Weight);
printf("Address pointer: %p\n", variablePtr);
printf("Address pointer (deref): %p\n", variablePtr->firstName);
printf("Address pointer (deref): %p\n", variablePtr->lastName);
printStruct(variablePtr);
printf("Address pointer (deref): %p\n", variablePtr->firstName);
printf("Address pointer (deref): %p\n", variablePtr->lastName);
/* printStruct(variablePtr); */
}
HealthProfile * setData(char first[20], char last[20], char gender[2],
int BirthDay, int BirthMonth, int BirthYear,
double Height, double Weight)
{
HealthProfile profile, *profilePtr;
profilePtr = &profile;
profile.firstName = first;
profile.lastName = last;
profile.gender = gender;
profile.birthDay = BirthDay;
profile.birthMonth = BirthMonth;
profile.birthYear = BirthYear;
profile.height = Height;
profile.weight = Weight;
return profilePtr;
}
void printStruct(HealthProfile * variablePtr)
{
printf("\n%s%s\n%s%s\n%s%s\n%s%d/%d/%d\n%s%.2lfm\n%s%.1lfkg\n",
"First Name: ", variablePtr->firstName,
"Last Name: ", variablePtr->lastName,
"Gender: ", variablePtr->gender,
"DOB: ", variablePtr->birthDay, variablePtr->birthMonth,
variablePtr->birthYear,
"Height: ", variablePtr->height,
"Weight: ", variablePtr->weight);
}
Based on the way I've written the code, I was expecting the structure pointer passed to 'printStruct' not to be changed after the member values are printed. I would think I could call the function multiple times with no alteration to member values, but after just one call things are changed.
The Problem here is, that your pointer points to an address on the stack, which means, it's 'lifetime' or scope ends, when the Function setData returns. The next calls' stackframe overwirtes in part or whole the place in memory where your pointer points to. This leads to random and sometimes possibly correct output.
To solve this either allocate memory in the heap, instead of pointing to the address of a local variable ( malloc ) or declare a local variable im Main() and pass a pointer to setData.
Both solutions will prevent the issue you Are having.
Your problem is the time local variables are valid:
Both function arguments and local variables are only present in memory as long as the corresponding function is being executed. When the function has finished, the variables become invalid and may be overwritten with other data.
Now let's look at the following part of your code:
... setData( ... )
{
HealthProfile profile, *profilePtr;
profilePtr = &profile;
...
return profilePtr;
}
profilePtr contains a pointer to the local variable profile. As soon as the function setData has finished, this variable is no longer valid and may be overwritten.
The pointer profilePtr (returned by the function) will point to the memory where the variable profilePtr was located before. In other words: The value of the pointer profilePtr also becomes invalid because it points to a variable which no longer exists.
Maybe you have luck and the memory is not needed and the variable is not overwritten. But with a certain probability the function printf will need that memory and overwrite that (no longer valid) variable.
You might try this:
variablePtr = setData( ... );
printf("BirthDay (first time): %d\n", variablePtr->BirthDay);
printf("BirthDay (second time): %d\n", variablePtr->BirthDay);
With a high probability the following will happen:
printf will need the memory occupied by profile and therefore overwrite the data. However, in both lines above the value of BirthDay will first be read from the structure before the function printf is actually called.
Therefore the first printf will print the correct value of BirthDay while the second printf will print a wrong value.
Related
This question already has answers here:
Can a local variable's memory be accessed outside its scope?
(20 answers)
Closed 3 years ago.
I created a structure in a header file to store student information. I initialize these member variable in a function called createStudent(). The createStudent() function returns a pointer to a newly initialized student. Now, when I test that this all works I get some unexpected results.
The code compiles and runs, however, only the first access of the students information returns useful values. In the code below I print out the students age first and I get the correct age, but the gpa and num values are garbage values. However, if I comment out the print statement for the students age, the gpa value is correct and the num value is still garbage. It seems that only the first access returns goof values. Now I'm fairly certain this is an issue to do with my pointers and memory allocation, I just don't know what the problem is.
I initially thought that memory was being freed up after the first access (I don't know why this would happen but that what seemed to be happening). So I tried to use malloc in order to make sure there was memory available at all times storing the students info, but the results did not change.
I have also tried re-assigning age in the createStudent() function after gpa and num were assigned, but age still gets the corrected value and gpa and num have garbage values.
If you guys need any more information concerning this please let me know.
Header File:
typedef struct Students{
int num; //holds the students number
char name[256]; //holds the students name
int age; //holds the students age
int gpa; //holds the students grade point average
}Student;
Student * createStudent();
Implementation file:
Student * createStudent(){
Student newStud;
Student *studPtr;
newStud.age = 10;
newStud.gpa = 5;
newStud.num = 307234;
studPtr = &newStud;
return studPtr;
}
Main File:
int main(int argc, char *argv[]){
int retCode = 0;
Student *stdPtr = createStudent();
printf("The students age is: %d\n", stdPtr->age);
printf("The students gpa is: %d\n", stdPtr->gpa);
printf("The students number is: %d\n", stdPtr->num);
return retCode;
}
As mentioned in the comments, the allocated memory for Student newStud only lives as long as your function is executed. as soon as it returns, the memory is freed again.
If you want to return the address of a created structure, you have to allocate the memory by yourself:
Student * createStudent(){
Student *studPtr = malloc(sizeof(Student));
studPtr->age = 10;
studPtr->gpa = 5;
studPtr->num = 307234;
return studPtr;
}
Do not forget to free the allocated memory and check the return value of malloc
So, this is my code:
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
struct person{
char name[18];
int age;
float weight;
};
int main()
{
struct person *personPtr=NULL, person1, person2;
personPtr=(struct person*)malloc(sizeof*(struct person));
assert (personPtr!=NULL);
personPtr = &person1; // Referencing pointer to memory address of person1
strcpy (person2.name, "Antony"); //chose a name for the second person
person2.age=22; //The age of the second person
person2.weight=21.5; //The weight of the second person
printf("Enter a name: ");
personPtr.name=getchar(); //Here we chose a name for the first person
printf("Enter integer: ");
scanf("%d",&(*personPtr).age); //Here we chose the age of the first person
printf("Enter number: ");
scanf("%f",&(*personPtr).weight); //Here we chose the weithgt of the first person
printf("Displaying: "); //Display the list of persons
printf("\n %s, %d , %.2f ", (*personPtr).name, (*personPtr).age,(*personPtr).weight); //first person displayed
printf("\n %s, %d , %.2f",person2.name, person2.age, person2.weight); //second person displayed
free(personPtr);
return 0;
}
I get two errors and I don't know why. Firstly, I don't think that I allocated the memory right, the first error is on the next line:
personPtr=(struct person*)malloc(sizeof*(struct person));
It says that:
[Error] expected expression before ')' token
The second error that I get is on the line
personPtr.name=getchar();
Why cant I assign a name using getchar for a structure? The error is:
[Error] request for member 'name' in something not a structure or union
sizeof*(struct person) is a syntax error. It is seen by the compiler as an attempt to apply the sizeof operator to *(struct person). Since you can't dereference a type, the compiler complains. I think you meant to write the following:
personPtr = malloc(sizeof *personPtr);
It's the idiomatic way to allocate whatever personPtr is pointing to. Now the type is specified only where the pointer is defined, and that's a good thing. You also don't need to cast the result of malloc, since void* is implicitly convertible to any pointer type.
The second error is two-fold:
name is a fixed sized array. You cannot assign to an array using the assignment operator. You can only assign to each individual element.
getchar returns a single character, not a string as you seem to expect. To read a string, you can use scanf("%17s", personPtr->name). The 17 is the size of your buffer - 1, to protect against buffer overflow when scanf adds a NUL terminator to the string.
Assignment requires using a function to input data into a struct
typedef struct AC {
char code[4];
double cost;
unsigned day;
} AC;
My function so far is
int main(){
AC *ac;
input(&ac);
}
void input(AC * ac)
{
printf_s("Input code (3 characters), cost, and day (0 = Sun 6 = Sat) separated by spaces: \n");
gets(ac->code);
printf_s("\nCode: %s\n", ac->code);
scanf_s(" %lf", ac->cost);
printf_s("\nCost: %.2lf\n", ac->cost);
scanf_s(" %i", ac->day);
printf_s("\nDay: %i\n", ac->day);
}
The gets for the airport code works fine, but the scanf for cost and day don't seem to do anything; using breakpoints, it seems like the function isn't placing the values scanf gets into the struct. Instead, cost just remains garbage code after that part is done.
Don't worry about error checking or anything, I'm just interested in how to make the function properly take the input values and put them in the struct.
You've got undefined behavior all over. The problem is that there is no actual space for the AC struct you're populating. It's a simple change. Replace AC *ac; with AC ac;. That changes ac from a pointer to an actual struct with backing memory.
use & .
Like scanf_s(" %i", &ac->day);
A string is character array ( char * or char [] ) So it does not require &. But non-arrays require it.
Also you should declare AC ac; (as Michael Arbers pointed out first) then pass the address ( &ac) when calling the function call. That is because you are passing address and declared the function to take pointer to AC (AC *ac).
In C language, when you declare an address variable (pointer) you use * . When fetching the address you use & .
So the primary objective here is to take input from the user and store it in an array where each element in the array is a struct srecord. I would like to be able to retrieve the strings fname and lname as well as the score. This is crucial because I am going to also design other methods that will calculate the average of all students in the array and tell which students have the highest or lowest score.
For example in fill_in_srecord_array, if I wanted to print out the information in a[i] after running fill_in_srecord, would this be the proper line?
printf("%s %s: Score= %d\n", a[i].fname, a[i].lname, a[i].score);
But this does not compile, so what is wrong here?
Is my fill_in_srecord method working properly and actually filling in the array properly?
For future reference, what is the best way to access variables from a struct being stored in an array?
#include <stdio.h>
#include <string.h>
struct srecord {
char fname[20]; /* first name */
char lname[20]; /* last name */
int score;
};
void fill_in_srecord(struct srecord *r){
struct srecord new_student; //declare a new student record
r = &new_student; //assign a value to the pointer
printf("Enter student first name: "); //request input
scanf("%s", r->fname);
printf("First: %s",r->fname);
printf("\nEnter student last name: ");
scanf("%s", r->lname);
printf("Last: %s",r->lname);
printf("\nEnter student score: ");
scanf("%d", &(r->score));
printf("Score: %d\n", r->score);
}
void fill_in_srecord_array(struct srecord a[], int len){
a[len];
//struct srecord *p; //srecord pointer
for(int i = 0; i<len; i++) {
fill_in_srecord(&a[i]);
}
}
int main(){
struct srecord students[2];
fill_in_srecord_array(students, 2);
exit (0);
}
The problem here is that in the fill_in_srecord function you do
struct srecord new_student;
r = &new_student;
This is problematic for three reasons:
First is that new_student is a local variable, and it will go out of scope and disappear once the function returns. Any pointers to it will be stray pointers and using them will lead to undefined behavior.
The second problem actually makes the first problem moot, because when you pass a value to a function in C the values are copied and the function only gets a copy. Modifying a copy (like e.g. r = &new_student) will of course not modify the original.
The third problem is that when the function is called, you pass a pointer to a valid and existing instance of the srecord structure. There's simply no need for the new_student variable or the reassignment of r inside the function. Modifying r directly will be enough.
So the solution is simply to not have the two problematic lines.
There's another thing as well, the statement a[len]; that you have in the fill_in_srecord_array function it doesn't really do anything. But if it did anything it would lead to undefined behavior because you would index the array a out of bounds.
Right now you were making changes to local variable , which is not accessible out of function block and changes made to it are not done on the variable in calling function itself .
When you pass address of a[i] to function ,and if you make changes to that in function ,a[i] will be modified in the calling function itself . Because the changes will be made directly to content at its address , that is to itself .
What you need to do is write your function like this -
void fill_in_srecord(struct srecord *r){
/* struct srecord new_student; //declare a new student record */
/* r = &new_student; //assign a value to the pointer */
printf("Enter student first name: "); //request input
scanf("%s", r->fname);
printf("First: %s",r->fname);
printf("\nEnter student last name: ");
scanf("%s", r->lname);
printf("Last: %s",r->lname);
printf("\nEnter student score: ");
scanf("%d", &(r->score));
printf("Score: %d\n", r->score);
}
i'm obviously not blaming printf here, i probably messed up my mem allocations and access but i can't understand where i did wrong. the program crash on the second printf in main. It also crash on the third if i comment the second one. Actually it crash whenever i access p after the first printf !
Can someone explains me what i am doing wrong ?
Thanks a lot.
typedef struct
{
char * firstname;
char * lastname;
int age;
} person;
person * new_person(char * firstname, char * lastname, int age)
{
person p;
int lf = strlen(firstname);
int ll = strlen(lastname);
p.firstname = (char *)malloc(++lf * sizeof(char));
p.lastname = (char *)malloc(++ll * sizeof(char));
strcpy(p.firstname, firstname);
strcpy(p.lastname, lastname);
p.age = age;
return &p;
}
int main()
{
person * p = new_person("firstname", "last", 28);
printf("nom : %s ; prenom : %s ; age : %d\n", p->lastname, p->firstname, p->age);
printf("nom : %s ; prenom : %s ; age : %d\n", p->lastname, p->firstname, p->age);
printf("nom : %s ; prenom : %s ; age : %d\n", (*p).lastname, (*p).firstname,(*p).age);
return 0;
}
You're returning the address of a local variable.
You can either modify your new_person to take an argument (a pointer to a person) or you can malloc one inside the function and manipulate that.
The person you declared in your function goes out of scope when the function returns. Everything that happens to it after that is undefined. It might coincidentally keep its value for a while, but you should not depend on this. When you make your call to printf, the stack grows and overwrites the old location of your person with new stuff.
I think the issue is in this line:
return &p;
Notice that you are returning a pointer to a local variable. This results in undefined behavior, since as soon as the function returns, the local variable p no longer exists. As a result, reading or writing that pointer will read or write garbage data.
The fact that this doesn't immediately crash is an artifact of how the compiler generates code. Chances are, the first time you call printf, it reuses space that was previously used for p in a way that, by sheer coincidence, works out fine. However, after the function returns, its stack frame has clobbered the old memory for p. As a result, the second call to printf is reading garbage data left behind from the call to printf, hence the crash.
(Specifically: when you pass the parameters, it copies the pointers to the strings onto the stack, so when printf runs, it's probably trashing the original pointers, but using the copies. The second call then loads garbage pointers from the expired printf stack frame, since it lives at the same address that p used to live in.)
To fix this, consider changing p to a pointer to a person and then using malloc to allocate it. That way, the memory persists beyond the function call, so this crash should go away.
Hope this helps!
person p;
// stuff
return &p
This is wrong. After the function returns, the local variable is leaving the scope - its address will be invalid. You have to allocate the structure on the heap:
person *new_person(char *firstname, char *lastname, int age)
{
person *p = malloc(sizeof(*p));
p->firstname = strdup(firstname);
p->lastname = strdup(lastname);
p->age = age;
return p;
}
The problem is in the function new_person. You create person p on the stack and return its address. You need to allocate person* p = new person(.....