0

Goes without saying: Crash safety

Page 1 | Page 2 | Page 3 | Page 4

Uninitialized variables

Here’s another thing: Uninitialized variables are very likely to cause problems. If a variable is declared but not initialized, it will probably contain 0 when the plugin is run in the debugger. But in a release build, variables are not automatically initialized (due to speed reasons), so their value will be absolutely random (depending on the current contents of the memory at the time of declaration).

Here’s a simple example (the function is nonsense, but the problem becomes obvious):

#define RATIOMODE_THREE       1
#define RATIOMODE_SEVEN       2
#define RATIOMODE_THIRTEEN    3

Real SomeRatio(LONG mode)
{
  Real divisor;

  switch (mode)
  {
    case RADIOMODE_THREE:
      divisor = RCO 3.0;
      break;
    case RADIOMODE_SEVEN:
      divisor = RCO 7.0;
      break;
    case RADIOMODE_THIRTEEN:
      divisor = RCO 13.0;
      break;
  }

  return RCO 1.0 / divisor;
}

This will work fine, as long as “1”, “2”, or “3” (respectively the defines above) are being passed to the SomeRatio(). But imagine what happens, when some other value is passed: It might crash, it might return some random value. But it will never do what you expect it to do.

Initializing the variable fixes the crashes. If any other value than “1”, “2”, or “3” is passed to the function, it would simply return 1.0.

#define RATIOMODE_THREE       1
#define RATIOMODE_SEVEN       2
#define RATIOMODE_THIRTEEN    3

Real SomeRatio(LONG mode)
{
  Real divisor = RCO 1.0;    // Initialize the variable

  switch (mode)
  {
    case RADIOMODE_THREE:
      divisor = RCO 3.0;
      break;
    case RADIOMODE_SEVEN:
      divisor = RCO 7.0;
      break;
    case RADIOMODE_THIRTEEN:
      divisor = RCO 13.0;
      break;
  }

  return RCO 1.0 / divisor;
}

Even better would be to use an enum instead of defines. That way, passing invalid values for “mode” would not happen as easily.

enum RATIOMODE
{
  RATIOMODE_THREE    = 1,
  RATIOMODE_SEVEN    = 2,
  RATIOMODE_THIRTEEN = 3
};

Real SomeRatio(RATIOMODE mode)
{
  Real divisor = RCO 1.0;    // Initialize the variable

  switch (mode)
  {
    case RADIOMODE_THREE:
      divisor = RCO 3.0;
      break;
    case RADIOMODE_SEVEN:
      divisor = RCO 7.0;
      break;
    case RADIOMODE_THIRTEEN:
      divisor = RCO 13.0;
      break;
  }

  return RCO 1.0 / divisor;
}

Now take a look at this:

BaseObject* op;

// ...

// Allocate an object if "op" is NULL
if (!op)
{
  op = BaseObject::Alloc(Onull);
  if (!op) return NULL;
}

GePrint(op->GetName());

Where’s the danger in this code? In the debugger it will work fine, because “op” is automatically initialized with NULL. In a release build, this is very likely to crash. The reason: The pointer “op” is uninitialized and will probably contain some kind of random crap that is now interpreted as a memory address. Therefore, the Null Pointer check in line 6 will not work, no object will be allocated. Then, in line 12, the member function GetName() of an imaginary BaseObject that is just a bunch of random nonsense will be called. This will definitely crash.

To prevent all this from happening, simply initialize the pointer with NULL.

BaseObject* op = NULL;  // Initialize the pointer!

// ...

// Allocate an object if "op" is NULL
if (!op)
{
  op = BaseObject::Alloc(Onull);
  if (!op) return NULL;
}

GePrint(op->GetName());

Frank Willeke

worked with computers since more than 20 years | got hooked on computer graphics back on the AMIGA | started programming at the age of 13 | relesed some successful plugins for cinema 4d | started working for maxon computer gmbh in 2009 | now contributing to cinema 4d as a senior developer

making electronic music since 1993 | playing the electric guitar since 1995 age of 14 | first live gigs in 1997 | playing the bass guitar 2005 | playing keyboards and synths since 2012

experimenting with photography since 2003

Leave a Reply

Your email address will not be published. Required fields are marked *