0

I have a struct:

typedef struct
{
  int a;
  char str[9];
} myStruct;

I have a global variable of this struct:

myStruct globalStruct;

Then I have a function that is called pretty regularly and is the only real way the globalStruct gets changed:

static void myFunc()
{
  myStruct tmp;
  setStruct(&tmp);
  globalStruct = tmp;
  return;
}

The setStruct sometimes changes both fields in the struct and sometimes it only changes myStruct.a. The issue I am seeing is that even though tmp is declared on what I thought would be the stack it is actually keeping the value from the previous function call and if I don't set myStruct.str it will just put the previous value in globalStruct. I understand this problem can be solved by just doing memset(tmp, 0x0, sizeof(tmp)) my question is more why is this happening to begin with? I thought the memory in tmp would have been freed after the end of the function.

cigien
  • 57,834
  • 11
  • 73
  • 112
Mavese
  • 131
  • 10
  • 4
    Using values of uninitialized non-static local variable invokes *undefined behavior*. – MikeCAT May 07 '21 at 17:00
  • 1
    Since tmp is uninitialized, it might contain anything--including, and not unlikely depending on your runtime ABI--whatever happened to be in that memory last time it was used, such as the last time you called that function from the same stack frame. Yes, memory is "freed" after use--but that doesn't mean it changes in any way. – Lee Daniel Crocker May 07 '21 at 17:04
  • 1
    @MikeCAT: Using the values of uninitialized non-static local objects is not undefined behavior. The values are indeterminate, which is different. There is a rule that the behavior is undefined if the object could have been declared `register`, but that is not the case here because its address is taken. – Eric Postpischil May 07 '21 at 17:06
  • @EricPostpischil [N1570](http://chimera.roma1.infn.it/SP/COMMON/iso-iec-9899-1990.pdf) J.2 Undefined behavior says "The value of an object with automatic storage duration is used while it is indeterminate (6.2.4, 6.7.9, 6.8)." No condition about `register` is here. – MikeCAT May 07 '21 at 17:08
  • Thank you guys so much! – Mavese May 07 '21 at 17:13
  • @MikeCAT: J.2 is a non-normative summary; it is not part of the actual rules of the C standard. It omits details. It can be used to help locate things in the standard but should not be relied on for engineering. The actual normative rule is in 6.3.2.1 2: “… If the lvalue designates an object of automatic storage duration that could have been declared with the `register` storage class (never had its address taken), and that object is uninitialized (not declared with an initializer and no assignment to it has been performed prior to use), the behavior is undefined.” – Eric Postpischil May 07 '21 at 17:13
  • @EricPostpischil Got it with reading this: [c - (Why) is using an uninitialized variable undefined behavior? - Stack Overflow](https://stackoverflow.com/questions/11962457/why-is-using-an-uninitialized-variable-undefined-behavior) – MikeCAT May 07 '21 at 17:26

1 Answers1

3

myStruct tmp; creates a “new” myStruct and does not initialize it. The C standard does not define its value (which is the values of its members).

When neither myFunc nor setStruct assigns a value to a member, the value of that member is indeterminate. A compiler is free to take the value from whatever was left on the stack in some previous function call.

Initializing tmp with memset will not result in globalStruct = tmp; keeping the values of the members not changed by setStruct. It will set those members to zero. To keep the values of members that are unchanged, you could initialize tmp from globalStruct:

static void myFunc()
{
  myStruct tmp = globalStruct;
  setStruct(&tmp);
  globalStruct = tmp;
  return;
}

Another possibility is you could just update globalStruct directly:

static void myFunc()
{
  setStruct(&globalStruct);
}
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312