0

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.

2 Answers2

0

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.

billdoor
  • 1,999
  • 4
  • 28
  • 54
0

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.

Martin Rosenau
  • 17,897
  • 3
  • 19
  • 38