Showing posts with label Programming Language Paradigms. Show all posts
Showing posts with label Programming Language Paradigms. Show all posts

Saturday, December 28, 2013

Ignore Divide By Zero


Generally, this is a mistake.  In almost all cases it is better for the run-time to terminate the program (perhaps first generating an assertion). 

Note that I am not saying that you should be oblivious to the possibility of division by zero. I am simply saying that trying to recover from it at the point of the error is usually a mistake.

Conventional Wisdom

When I first began learning about programming the first thing that was drummed into us was that it is not enough to get your program working. You also need to cater for error conditions.

The first example my lecturer gave of error-handling was division by zero. 

Floating Point Division By Zero

I am talking about integer divide by zero, not floating point, unless otherwise stated. However, the same arguments generally apply to floating point calculations.

The major difference is that floating point division by zero usually will not terminate the program but generate an infinite result. (Most implementations nowadays provide floating point numbers that include positive and negative infinity).

The guidance was simply to make sure it cannot occur. A few years later I became an aficionado of defensive programming and my policy became that whenever I used division I needed to add extra code to check for the possibility of the divisor being zero and somehow recover from the situation - usually simply by setting the result of the operation to zero.

Most experienced C programmers take this approach, but I have found that this is almost always the wrong approach. Let's first look at the possible situations where division by zero may occur then consider each one in more detail.

  • a bug causes the divisor to take a zero value when it should never be zero
  • a zero divisor resulting from user input
  • incorrect data from an external source
  • in rare cases division by zero may be mathematically valid and handled specially

Bugs

Most of the time the problem occurs due to bugs in other parts of the code that have slipped through. It is often argued that the problem should be detected and handled something like this:


  if (numRequests > 0)
    aveTime = totalTime / numRequests;
  else
    aveTime = 0;        // Bad idea


The problem with this is that the bug is now silently hidden. Perhaps this is good for the final release of the software but is certainly not good when debugging and testing. It's better to find and fix the bug than to cover it up. This is the problem of defensive programming which I talked about in a previous post.

Personally, I would just leave the test out altogether and let the run-time system terminate the program. This is the fail fast approach. But I would also add an assertion, especially if using floating point values, since some implementations may not terminate but instead generate infinity, which is probably not what was desired.


  assert(numRequests > 0);
  aveTime = totalTime / numRequests;


This should be adequate with good design, thorough testing and software that is adequately verifiable (see my post on verifiability), but with badly written software you may not be certain that a bug has not slipped through, so the only alternative is to try to recover. But often continuing with a strange value may cause subsequent problems or even data-corruption.  In the above case it may be that aveTime should always be greater than zero.

Having studied a great deal of these situations I found that it can be very difficult to decide on a value that makes sense for the continued safe and sensible operation of the software. For example, it may make most sense to set aveTime to some very large value (since mathematically speaking, dividing by zero produces an infinite result); but if both totalTime and numRequests are both zero then aveTime probably should also be zero.


  if (numRequests > 0)
    aveTime = totalTime / numRequests;
  else
  {
    assert(0);          // Don't hide this bug in debug/test

    // Try to recover sensibly in case a bug was never found
    if (totalTime == 0)
      aveTime = 0;
    else
      aveTime = INT_MAX;
  }


The other alternative in C++ is to throw a software exception and let the software recover at a higher level.

User Input

Sometimes a calculation can be the result of user input. It is important to validate user input when it is entered. Not validating can cause inconsistencies in that data which later leads to problems like division by zero.


  for (;;)
  {
    int numberOfItems = GetNumberOfItemsFromUser();
    if (numberOfItems > 0)
      break;
    DisplayErrorMessage("You must have at least one item");
  }
  .
  .
  aveCost = totalCost / numberOfItems;  // No divide by zero here


Of course, a lot of things could happen between the input validation and the use of the value. If it is at all possible that the value could be corrupted or input bypassed then the previous section (Bugs) again applies.

Bad Data

A lot of programs carefully validate user input but assume that data from other sources is valid. Unless you are sure that the data is valid, for example by using a CRC then you should validate data when you receive it.

Data can be corrupted due to many things like hardware or software failure or human error.  In software with security implications, deliberate tampering may be an issue and a CRC is not sufficient - use a cryptographic checksum like SHA1.

Expected

In very rare cases division by zero may not actually be an error condition, in which case you may need to handle it especially. This is the reason that IEEE floating point numbers allow for infinite numbers. Generally, this sort of code would be for a specialized scientific or mathematical purpose and would use floating point numbers anyway.

Otherwise, it could be achieved like this:


  if (elapsedTime == 0)
  {
    isInfiniteSpeed = true;
    speed = -1;
  }
  else
  {
    speed = distance / elapsedTime;
    isInfinite = false;
  }


Of course, later code that used speed would need to also check isInfiniteSpeed before using speed.

Conclusion

Generally, it is a mistake to detect and try to recover from a divide by zero error.  Except for the very unusual situation where it is not an error-condition (see Expected section above) then it indicates there was a problem earlier such as a bug, corrupt data, or user input that was not validated.

With verifiable and thoroughly tested software the problem should not happen. Trying to recover is actually detrimental as it may hide a bug which would normally be found in testing.

For poorly written software (not well-written and not easily verifiable) it might be worthwhile trying to recover from the problem. It would also be necessary for fail safe systems where software termination could have dire consequences.

The problem with trying to recover is that there may not be a reasonable value to use in the circumstance. It is conventional to use a value of zero but often this is the worst possible value to use.

The most important point is that if you do recover from a divide by zero error that you do not hide the fact that there is a defect in the code. During debugging and testing the software should generate an exception or make it immediately obvious that there is a problem with the code. For released software the problem should be detected and reported - for example, to a monitored error log file.

Defensive Programming


Note that this post is not about how to use defensive programming.  That has been covered in depth (for example in Code Complete  by Steve McConnell).  I am just attempting to more accurately describe what it is, why it was invented, and problems with it.

What It Is?
Defensive programming or defensive coding is a style of writing computer software that attempts to be more resilient in the event of unexpected behavior.  This unexpected behavior is generally considered to be a result of existing bugs in the software but could be due to other problems such as corrupted data, hardware failures, or even bugs introduced by later software changes.  Generally, the code tries do do the most sensible thing with little or no performance penalty and without adding new error-conditions.

History
The first time I ever encountered the term "defensive programming" was in K&R (The C Programming Language, 1st Edition, by Kernighan and Ritchie).  After extensive searching I can find no earlier references to the term.  The term probably derives from the term "defensive driving" which came into common use in the early 1970's a few years before K&R was written.

It is mentioned twice in the index of K&R.  On page 53 it clearly refers to making code resilient to bugs, but on page 56 it talks about writing code in a way that reduces the likelihood of future code changes introducing bugs.  In any case many books since have used the term "defensive programming" to mean making the code resilient in the presence of bugs, for example The Pragmatic Programmer by Andrew Hunt and Dave Thomas (which talks about "coding defensively" in the chapter entitled "Pragmatic Paranoia"), and others.  Even before that many software professionals, myself included, have used the term in this way since at least the mid-1980's.

Disagreement About Definition

Despite the term being fairly clearly understood for more than 20 years the exact definition of the term has recently become blurred after several (generally non-peer-reviewed) articles and blogs have appeared on various web sites.  For example, the current Wikipedia article, and several sites that quote it, makes "defensive programming" sound like an approach to error-handling.   Error-handling can be related to defensive programming but they are definitely not the same thing; and one is not a subset of the other (see below).

Another, well-regarded and often referenced article, entitled simply Defensive Programming has a very high ranking on Code Project.  This is an excellent and worthy article in its own right, but it is not just about defensive programming.  By its own admission it is about "... techniques useful in catching programming errors ...".  As we will see below defensive programming has the opposite effect - it tends to hide errors not catch them.  This article discusses many things and should be more accurately called something like "Good Coding Practices".

Error Handling vs Defensive Programming

The distinction between error-handling and defensive programming is not very clear in the minds of many programmers. I will explain the difference.

Error-handling detects and handles situations where something goes wrong that you know is possible, however unlikely.  In contrast, defensive programming attempt to cater for problems that are assumed to be "impossible".  There are two problems with this distinction that can cause confusion.

The first problem is that it can depend on circumstances whether something is impossible or not.  For example, if a function is private to a module or program you may be able to ensure that it is always passed valid arguments; but if it is part of a public library you cannot be certain that it will never be passed bad data.  In the first case you can program defensively to ensure that the function does something sensible even though you know it is "impossible" that this will happen.  In the latter case you might add error-handling in case bad data is passed to the function.

So whether you choose to program defensively or add explicit error-handling depends on the scope of the software that you control.  I discuss this further below under Scope.

The second problem is that there can be borderline cases where it is debatable whether something should be considered impossible.  Consider this spectrum of scenarios for a hypothetical program that can be given invalid data:


  1. The program accepts data directly from the user and the user may enter invalid data.
  2. The programs accepts data from a text file that has been typed in by a person.
  3. The program accepts data from an XML file (machine or manually generated).
  4. The program reads a binary data file which was created by another program.
  5. The program reads a binary file that was written by itself.
  6. The program reads a binary file that  includes a CRC to check it has not been corrupted.
  7. The program reads a temporary binary file that it only created moments before.
  8. The program reads a memory mapped file that it created.
  9. The program reads from a local variable (ie, in memory) that it just wrote to.


At what point does invalid data become "impossible"?  Personally, I would say that it is "impossible" that a data file has become corrupted and still generates the same CRC (see scenario 6).  However, if security of the data is important you have to consider that the file was deliberately tampered with (in which case a cryptographic checksum such as SHA1 should be used).

However, I know that a lot of software assumes that binary data files are always valid (scenario 4 or 5).  Much software will behave erratically if binary data files have become corrupted.

Of course, I think anyone would agree that you have to assume that the value of a local variable you just wrote to (see scenario 9) cannot change.  However, even in that case a hardware error, deliberate tampering, or some other problem could change memory unexpectedly.

So it is not always clear when you need to have explicit error-handling code and when you should simply program defensively.

Example

The archetypal example of defensive programming occurs in just about every C program ever written, where the terminating condition is written as a test for inequality ( < ) rather than a test for non-equality ( != ).  For example, a typical loop is written like this:


  size_t len = strlen(str);
  for (i = 0; i < len; ++i)
      result += evaluate(str[i]);


rather than this:


  size_t len = strlen(str);
  for (i = 0; i != len; ++i)
      result += evaluate(str[i]);


Clearly both of these should do exactly the same thing since the variable 'i' is only ever incremented and can never skip having the same value as 'len'.  So then why are loop termination conditions always written in the first manner?

First, the consequences of the "impossible" condition are bad, probably resulting in all sorts of undesirable consequences in production software, such as an infinite loop or a memory access violation.  The "impossible" condition may occur for any number of reasons such as:

  • bad hardware or a stray gamma ray photon means that one of the bits of 'i' is flipped randomly
  • another errant process (in a system without hardware memory protection) or thread changes memory that does not belong to it
  • bad supervisor level code (ie, the operating system or a device driver) changes memory
  • the 'evaluate' function has a rogue pointer that changes the value of 'i'
  • the 'evaluate' function corrupts the stack frame pointer and the location of 'i' is now at some random place on the stack
  • later code changes introduce bugs, for example:
      for (i = 0; i != len; ++i)
      {
          while (!isprint(str[i]))           // bad code change means that 'i' may never be equal to 'len'
              ++i;
          result += evaluate(str[i]);
      }


Of course, the last few, caused by bugs in the software, are the most common, which is why defensive programming is usually associated with protecting against bugs.

Culture of C

There are also two other aspects of the C language that affect how and when defensive programming is used - namely the emphasis on efficiency and the approach to error handling.

Looking at efficiency first -- it is one of the fundamental premises of C that it assumes the programmer knows what they are doing.  The language does not protect from possible mistakes, as other languages try to do.  For example, it is easy to write past the end of an array in C - but if all array access had bounds checking applied (by the compiler) then it would run more slowly even for perfectly safe code.

Due to this emphasis on efficiency, defensive programming is only used when it has little or no performance penalty.  This is typified in the above example since a "less than" operation is normally just as fast as a "not equal" one.

The other aspect is the approach to error-handling in C.  Errors in C are generally handled by using error return values.  It is not unusual for C code to be dominated by error-handling, so error-conditions are ignored if they are considered unlikely to occur - eg, nobody ever checks the error return value from printf().  (In fact, error return values are often ignored when they should not be, but that is for another discussion.)

So, if "unlikely" errors are not generally handled it makes no sense for "impossible" conditions to be handled as errors since this would add to the existing error handling burden.  (This is covered in more detail in item 10 of my Code Project article at http://www.codeproject.com/Articles/357065/Ten-Fallacies-of-Good-C-Code.)  Of course, in languages with exception handling, many such "impossible" conditions can be easily handled by throwing a "software exception".

Scope

A lot of the confusion about defensive programming comes about because the scope of control is not always clearly defined.  For example, if you have a function that takes a string (const char *) parameter you may want to assume that you are never passed a NULL pointer if it never makes sense to do so.  If it is a private function you may be able to always ensure it; but if it's use is outside the scope of your control then you can't assume that unless you clearly document that a NULL pointer may not be used.

In any case even if you consider the condition to be impossible it is wise to allow for the possibility using defensive programming.  Many functions do this by simply returning if unexpectedly passed a NULL pointer.  (Again, note that this is different to error-handling since no error value is generated.)

So any discussion of defensive programming must clearly define the scope of the code being considered.  This is one problem with the Wikipedia article on defensive programming.

Symptoms

When using buggy software the symptoms of defensive programming are seen often (but may be dismissed as operator error).  I think everyone has at some time seen software that did something a little strange, like flash a window, ignore a command, or even display a message about an "unknown error".  Usually this is caused by a bug which caused a problem from which the software attempted to recover.

This recovery can sometimes be successful but usually results in the program limping along.  In the worst case it can silently cause massive problems like data loss or corruption.  (After seeing something like this, I generally save my data and restart the software to ensure it is not in some weird state.)

Problems with Defensive Programming

By now it must be pretty clear that defensive programming has a major problem.  It hides the presence of bugs.

Some people may think it is good to hide bugs.  Certainly, for released software in use, you don't want to force the user to deal with a problem that they do not understand.  On the other hand blindly continuing when something be broken can be dangerous.  Also, some attempt should be made to notify someone of the problem - at least write an error message to a log file.

What is worse, though, is that defensive coding has been known to hide bugs during development and testing.  Nobody can argue that this is a good thing.  The alternative is to use what has been called "offensive programming" and sometimes "fail fast".  This means to make sure someone knows about problems rather than hiding them.

I do use defensive programming so that unexpected or impossible situations are handled in release builds; but add assertions that check for the impossible situations so that bugs do not sneak through.  I also do most testing use the debug build (so that assertions are used), except for final acceptance testing.  For some critical things I also explicitly add error-handling code, since assertions are removed in release builds.

Standard C Library

Here are two more examples of how defensive programming is used, taken from the standard C library.
A nasty problem that occurs in far too many C programs is caused by buffer overruns.  This mostly happens when copying or building a string and the size of the the output buffer is exceeded.  In the name of defensive programming it is recommended to use string functions that take a buffer length (strncpy(), strncat(), snprintf(), etc).  This avoids the buffer overrun, but hides the (possible) problem that the string was truncated.

Reports often require data nicely formatted into columns.  This is usually achieved in C using the minimum field width of printf() format-specifiers.  For example,  to print numbers in a column five characters wide you would use the "%5d" specifier.  If the integer is too big for the field then C just prints the extra characters anyway even though this will ruin your columns.  (Contrast this with other languages like Fortran where field overflow results in silent truncation of numbers, which has caused some very nasty problems.)  This is an example of defensive programming since when presented with an unexpected situation the code tries to do something sensible.

Exercise

Finally, here is something for you to think about.  The standard C library includes a function that takes a string of digits and returns an integer called atoi.

If you are not familiar with atoi(), it does not return any error code but stops when it encounters the first unexpected character.  As an example atoi("two") just returns zero.


Is the behavior of atoi() an example of defensive programming? Why?

How could it be improved?

Long Identifiers Make Code Unreadable

The first fallacy I covered was that of using very long identifiers.  I thought this debate had been settled a long time ago and it was agreed that using identifiers that are too long makes code hard to read.  I only added it to the article because every now and then I see some code that uses really long variable names, so I thought there must still be a few people around who still think it is a good idea.

So I was very surprised to get quite a bit of feedback objecting to this "fallacy".  It seems that the idea (and the idea that code can be self-describing) is having a resurgence.  For example, I have just been reading the book "Clean Code" by Robert C. Martin which also seemed to say that really long variable names are a good idea.  So I think I need to explain this in more detail for those who are still unconvinced.

Initial Problem

I guess the problem all began because K&R and other books used example code with very short names, often single characters. This is not necessarily a bad style in that context since these examples are small and often demonstrate abstract concepts.

The real problem started when many C programmers emulated this style everywhere, even in much larger and more practical software and for variables with much greater scope.  (In these situations longer and more descriptive variable names should, of course, be used as they are more easily seen, remembered and differentiated.)

The reaction to this problem of use of poor variable names was to mandate (eg, in coding standards) that variable names should fully describe the purpose of the variable.  This was an overreaction.

Self Describing Code

Another contributing factor was that many authors have promoted the idea that code should be self-describing, rendering the use of comments unnecessary.  First it is argued that well-written code should not need any explanation. However, the main justification was really that comments are sometimes out of date or just plain wrong.

The problems with the idea of self-describing code are many.  First, it is much easier to describe many things in plain English than to contort the code in an effort to make things clearer.  Trying to make the code self-describing actually can make it harder to read, at least for the casual reader.

I will say that comments on every line that simply reflect what the code is doing are worse than useless, but comments at the start of blocks of code or for every function can make it much easier to quickly understand what is going on.  In a large program occasional explanatory comments can be an absolute godsend, allowing you to quickly hone in on what you are looking for.

Also, just because comments are often incorrect does not mean that comments are inherently bad.  Some comments are useless and can be removed but sometimes the comments should simply be improved not removed.  I often think that the idea of self-describing code was invented by someone who had just read too many bad comments.

Finally, the truly unfortunate thing is that many programmers used the idea as justification for not adding any comments to code.  I have read a lot of code in my time and invariably the worst code has no comments and the best has several (or at least a few).  Rightly or not, when I see code with no comments I immediately assume it is of poor quality.

Clean Code

I have been reading this book by Robert (Uncle Bob) Martin and have found many useful ideas in it.  The book actually has a whole chapter devoted to naming of identifiers (Chapter 2: Meaningful Names) which I find ridiculous in itself since most of the ideas presented are bleedingly obvious and really not worth anyone reading let alone putting to paper.  However, what I really did not like was his ideas on the length of identifiers.

Actually the book seems to contradict itself.  For example, on page 18 it says that an identifier should describe "why it exists, what it does, and how it is used", but on page 30 "Shorter names are generally better than longer ones, so long as they are clear."  I guess these statements are too vague to be contradictory but they are at least confusing.  Perhaps, his code example will clarify... 

    int  d;
    int elapsedTimeInDays;

According to Uncle Bob the first line is bad but the second is good.  I agree with that, but what about the obvious:

    int days;

I actually don't mind the name "elapsedTimeInDays" (though it is close to exceeding my rule of thumb of a maximum of about 15 characters), but that would depend on how it is used.   If it is used many times within a few lines of code a shorter name (like "days") will make the code much easier to scan.

But what I really don't like is:

    "If a name requires a comment, then the name does not reveal its intent." - page 18
    "A long descriptive name is better than a long descriptive comment."  - page 39

This brings us to my main point that it is generally best to use a shorter name and a comment when declaring a variable.

Long Names vs Comments

Another idea of Uncle Bob's is that code should read like a good novel, rather than some technical document.  I agree completely.  Taking the analogy even further: declaring a variable in a program is like introducing a new character in a novel.  In a novel the author might take a paragraph or two to introduce a new character, but thenceforth he or she would be referred to by first or last name.  In fact the main character of a story would be referred to as "he" or "she" most of the time.  The point is that each time a character is mentioned, the author does not fully describe that character again or even use their full name.  (Of course, characters should have names that are memorable and not similar to the names of other characters in the story to avoid confusion.)

Similarly, in a program a variable is introduced when it is declared.  A comment should appear at this point that fully describes the purpose of the variable and how it is used.  Thereafter you need to be able to refer to the variable by a name that is meaningful enough that it is easily remembered what it is for, but it must not be too long that it makes reading the code tedious.  Contrary to what Uncle Bob would have us believe, the name should not try to include every interesting thing about the variable.

Compiler Limits
Originally C compilers typically only supported identifiers that were different in their first eight characters (and some linkers limited the length of external variables to 6 characters).  When the C standard lifted this limit to 63 characters many programmers took this as meaning they should use much longer names.  However, the real reason the length was increased was to alleviate problems with machine generated code.

DRY
A final point is that having lots of information in an identifier contravenes a fundamental principle of good software: DRY (don't repeat yourself).  Every time you use the long, overly descriptive identifier you are repeating yourself.  (Even Uncle Bob mentions the DRY principle.)

Conclusion
When you try to put too much information into a variable name it makes the code hard to read especially if that variable is used often.  It is far better to put all that information in one place - in a comment where the variable is declared.

Initialization issues in Programming

The Problem

Uninitialized variables are a perennial problem in C.  Writing through uninitialized (but not necessarilly NULL) pointers is particularly nasty, as it can overwrite memory anywhere (notwithstanding hardware memory protection).  Of course, any variable can be uninitialized so we want to ensure that all variables are set to some (correct) value before they are actually used.

Years of bad experiences with this problem means that experienced C programmers often use a form of defensive programming where steps are taken to avoid the possibility of using memory that has not been set to some value.  For example, I have seen malloc() #defined so that the real malloc() is bypassed in favour of calloc().  The standard library function calloc() is useful because it initializes the allocated memory with zero, but it is not always appropriate.

Defensive Programming

Defensive programming is part of the culture of C.  For example, it is common to see code like this:


  for (i = 0; i < 10; ++i)   // defensive
    a[i] = f(i);

rather than:


  for (i = 0; i != 10; ++i)  // non-defensive
    a[i] = f(i);

Now these should do the same thing unless by some strange action the variable 'i'

Note that I use the literal 10 for simplicity. In real code you would not use a magic number but something like sizeof(a).
somehow skips having the value 10, in which case the non-defensive code will go into an infinite loop corrupting memory until the program terminates due to a memory exception or something else nasty happens.  How could  'i'  possibly do this?  Well stranger things have happened.  It may be due to errant behavior of the function f().

So which is better?  Well, at least when debugging, the non-defensive code is definitely better as the defensive code will mask the symptoms of the bug in f() and you may never be aware of it.

For a release build the defensive code is arguably better.  But surely it is better to find and fix the bug (a nasty one probably lurking in f()) than try to recover (perhaps unsuccesfully) from it?

Don't get me wrong.  I think defensive programming is useful.  It is preferable to use software that might behave a little strangely once in a while, as long as it doesn't crash and lose all your data.  However, as we saw, defensive programming can hide bugs.  So you might find that you were using a word processor which did some odd things but seemed to recover and allowed you to save your document - but you might not discover till much later that the document you saved was actually corrupted.

The best strategy is to be defensive and (at least in debug builds) also try to detect the bugs you are defending against.  I use code like this:


  for (i = 0; i < 10; ++i)
    a[i] = f(i);
  assert(i == 10);

Uninitialized Variables

To get back to the point of this article consider a function like this:


void g()
{
  int i;
  ....
  i = f();
  ...
  use(i);
  ...

The worry is that the initialization of  'i' is bypassed somehow and then 'i' is used with an invalid value.  Personally, I think, as far as possible, variables should be initialized with their proper value when they are declared.


  int i = f();
  ...
  use(i);
  ...

but this is not always possible in conventional C (at least in the more commonly used C-89) which requires you to declare variables at the start of a compound statement.

You can still have the problem in C++ if the initial value of all members is not known at the time of construction.
Luckily, this is not required in C++ and is even actively discouraged.  A greater part of the problem is that convention (and many C coding standards) require all local variables to be declared at the start of the function.

Debugability

The result is that many C programmers take the shotgun approach:


void g()
{
  int i = 0; // shotgun initialization
  ....
  i = f();
  ...
  use(i);
  ...

The only thing is that zero is not the correct initial value for 'i'.  The only good to come from this is that this makes the value of  'i' deterministic, since otherwise it could contain any random value that happened to be left over in the memory that is used for the local variables.

Making the code deterministic makes it more debugable (see my previous blog post on software quality attributes).  So it can save time in tracking down a bug, because it is easy to reproduce.  However, it can also make the code less verifiable since it can hide the symptoms of the bug.  It may be better to initialize the variable to some strange value so that it is obvious when it has not been initialized properly.

This is the reason that the Microsoft VC++ debug code initializes all local variables with the hex value 0xCC (uninitialized heap memory is filled with 0xCD).  This obviates the need to do shotgun initialization in that environment since the run-time behavior cannot randomly change.

Disadvantages

We saw that shotgun initialization has the advantage in increasing code debugability, but there are many disadvantages?

First, it can make the bug harder to detect (the software is less verifiable).  If you are debugging and 'i' has the (incorrect) value of zero you may think that it has been set correctly.  But if it contained 0xCCCCCCCC your interest should be immediately aroused, and it's more likely to trigger an assertion or a run-time error.

Another problem is when you initialize to some arbitrary value it is harder for the compiler to tell you that something is wrong.  Many compilers will attempt to warn if you use a variable that has not been initialized.

Further, if the code that uses 'i' is removed at a later date it will prevent many compilers from warning of an unused local variable.  Because 'i' is initialized it is not flagged as being unused.

However, my biggest concern is that the code becomes confusing.  The initialization of 'i' to zero serves no purpose and may confuse someone reading the code.  One of the biggest code smells is finding redundant code.  I have often found that such redundancy inhibits changes to the code and can itself lead to bugs.

Last (and least), it is less efficient to write the same memory twice - once with zero and then again with the real initial value.  One of the few things I dislike about C# is that you cannot avoid having an array initialized to zeros, even if you are going to set the elements to some other value.  For a large array this can take an appreciable amount of time.

The Bug is The Problem

The bottom line is that rather than trying to handle bugs gracefully, the first priority is not to have bugs.  Shotgun initialization can even hide bugs and makes the code harder to understand (and hence modify).  There are few, if any, advantages to using it and these are far outweighed by the disadvantages.

Zero in Software Development

Zero may be nothing to you, but it has caused many problems through history.  In computer programming it is particularly important since incorrect handling can cause minor bugs or worse.  In fact, in a well-documented disaster a rocket was lost due to a software defect related to the incorrect use of zero in a Fortran program.

I will give some examples of the sort of things that can go wrong if you don't handle zero properly.  But first a brief history.

History of Zero

First, we should distinguish between zero as a digit and zero as an integer.  The symbol for zero can just be used as a digit, ie, a placeholder in a positional based numbering system (eg, the zero in 307 represents that there are no "tens").  A symbol for an empty position was first used by the Babylonians.  But the idea of the integer zero was not invented till later by the Mayans and independently in India.

The Sumerians were probably the first to use a positional number system but not having a symbol for zero created problems.  If you wanted to send a message to the market that you had 30 goats to sell there was no way to distinguish between 30 and 3.  A farmer could avoid this problem by instead selling 29 or 31 goats but as numbers became more important the lack of a zero became problematic.

The Babylonians started with a similar system to the Sumerians, but eventually used the notation of a small mark to signify an empty position in a number.  Greek astronomers were the first to use a circle for zero as a placeholder, but it was not until centuries later that Indian mathematicians included zero as a full integer like one, two etc.  One reason that mathematicians probably avoided zero was the problem of what is the result of dividing a number by zero or the even weirder idea of 0/0 (zero divided by zero).

Zero in Algorithms

Sometimes having empty input makes no sense.  For example, how do you take the average of zero numbers?


  int average(int[] array)
  {
      int sum = 0;
      for (int i = 0; i < array.size(); ++i)
          sum += array[i];
      return sum/array.size();
  }

The above code will have a divide by zero error at run-time when presented with an empty array.  We can easily fix this:


  int average(int[] array)
  {
      if (array.size() > 0)
      {
          int sum = 0;
          for (int i = 0; i < array.size(); ++i)
              sum += array[i];
          return sum/array.size();
      }
      else
          return 0;  // May be more appropriate to return -1 to indicate an error, or throw an exception
  }

Now suppose we want to find the sum of an array of numbers.  Having just coded the above average() function we might be tempted to write:


  int sum(int[] array)
  {
      if (array.size() > 0)
      {
          int sum = 0;
          for (int i = 0; i < array.size(); ++i)
              sum += array[i];
          return sum;
      }
      else
          return 0;
  }

However, in this case, taking the sum of zero numbers does make sense.  The code should simply be written as:


  int sum(int[] array)
  {
      int sum = 0;
      for (int i = 0; i < array.size(); ++i)
          sum += array[i];
      return sum;
  }

[Actually, doing it the first way would be better in Fortran as we will see below.]

I will now look at a few ways some programming languages do not handle zero well.

Fortran

Fortran was the first programming language I learnt and you would expect that I would have a certain fondness for it.  Unfortunately, I always found it very peculiar.  One of the stupidest things in Fortran is that a FOR loop is always executed at least once.  This has resulted in countless bugs where boundary conditions were not handled properly.  To avoid bugs you usually have to wrap the FOR loop in an IF statement which protects against the empty condition.

Pascal

Soon after learning Fortran I learnt Pascal and it was an enormous relief.  It is much simpler to understand and elegant and, of course, it handles FOR loops correctly.  However, I later came to appreciate that it has some deficiencies when compared to C-like languages.  Ranges in Pascal are specified using inclusive bounds, so for example, the range "1..10" means the integers from 1 to 10 inclusive.  You can express a range with one element such as "1..1", but how do you express a range with zero elements?


A common mathematical notation is to express ranges with square brackets when the ends are included and round brackets when the ends are excluded.  Many things are simplified when an inclusive lower bound and an exclusive upper bound are used.  Some people refer to this as using "asymmetric bounds".  For example, the set of numbers from zero (inclusive) to one (exclusive) is shown as [0, 1).

Now you might wonder why you want to express a range with no elements.  Actually, it is often very important to be able to do this sort of thing.  Many algorithms work on data sets that may have zero elements as a degenerate case.  If these situations are not handled then the code will fail under situations like being presented with empty input.

It is better for many reasons to use asymmetric bounds for ranges (as is conventional in C), and I will discuss these reasons in a later post.  But one of the main advantages is that you can easily specify an empty (ie, zero-length) range.

Brief

In my first job I used to edit my C code in a word processor (WordStar), and this was a very painful experience.  Soon after its release (1985?) I started using the programmer's editor called Brief which was simply wonderful in comparison.  (Perhaps the best thing was the unlimited undo which was not restricted to changes in the text but other things like changes to the selection and layout - in fact the Undo for my hex editor, HexEdit, is modelled closely on it.)

Brief allowed you to "mark" text and copy or append it to its internal "clipboard".  There were several ways of marking text including "non-inclusive" mode.  Non-inclusive mode was very useful as it used the asymmetric bounds pattern mentioned above.  This allowed you to mark the start and one past the end of a block of text for further processing.

I found non-inclusive mode very useful for creating macros in Brief's built-in programming language.  I created several macros that built up text in the clipboard by appending to the clipboard in non-inclusive mode.  But there was one major problem - copying or appending to the clipboard generated an error when the start and end of the marked range were at the same place.  That is, you could not copy a zero-length selection.

I even wrote to the creators of Brief (who were obviously very clever guys for creating such a brilliant tool).  The reply I got was basically that there could be no use for a selection of zero length.  Of course, they were wrong as many of my macros failed under situations where they attempted to append a zero-length selection to the clipboard.

The moral is that even very clever people may not appreciate the importance of zero.

C

The good things about the C language is that in almost all cases zero is handled correctly.  Experienced C programmers, usually reach the point where they need only give cursory consideration to boundary conditions.  Generally, if you write the code in the simplest, most obvious, way it will just work under all inputs with no special code for boundary conditions.

Even the syntax of the language adopts the "zero is a normal number" approach.  For example, in C the semi-colon is used as a statement terminator (rather than a statement separator as in Pascal).  A compound statement which contains N statements will have N semi-colons - so a compound statement with no nested statements is not a special case.

Consider compound statements in Pascal and C:

Pascal:

  begin statement1; statement2 end;     (* 2 statements, 1 semi-colon *)
  begin statement1 end;                       (* 1 statement,  0 semi-colons *)
  begin end;                                         (* 0 statements, can't have -1 semi-colons! *)
C:

  { statement1; statement; }             /* 2 statements, 2 semi-colons */
  { statement1;  }                            /* 1 statement,  1 semi-colon */
  { }                                               /* 0 statements, 0 semi-colons */

How can this really be that important?  This could be important is if you have software that generates C code.  In some circumstances you may need to generate a compound statement containing no nested statements.  The syntax of C means you don't need to handle the boundary condition specially.

Also if you have ever edited much Pascal code you will know how painful it is to add (or move) statements at the end of a compound statement.  You have to go to the end of the previous line and add a semicolon.  Worse, if you forget to do so, as usually happens, you get syntax errors on the next build.

Note that C does not take this to extremes though.  Commas in function parameter lists are separators not terminators.


  func(param1, param2);   // 2 parameters, 1 comma
  func(param1);                // 1 parameter, 0 commas
  func();                           // 0 parameters, can't have -1 commas!

But you can use commas as terminators in array initialisation, though the last comma is optional.  This makes it easy to edit arrays, such as appending and reordering the elements.


  float array[] = {
    1.0,
    2.449489742,
    3.141592653,
  };

Malloc Madness

Given C's very good behaviour when it comes to zero it was very surprising to find a certain proposal in the draft C standard in the late 1980's.  Some on the C standard committee were keen to have malloc(0) return NULL.

All C compilers until then based their behaviour on the defacto standard, ie the behaviour of the original UNIX C compiler created by Dennis Ritchie (often referred to as K&R C).  This behaviour was for malloc(0) to return a valid pointer into the heap.  Of course, you could not do anything with the pointer (except compare it to other pointers) since it points to a zero-sized object.

In my opinion, anyone on the committee who was in favour of malloc(0) returning NULL showed such a lack of understanding of the C language and the importance of correctly handling zero that they should have immediately been asked to leave.  At the time I scanned a fair bit of source code I had already written and it revealed that more than half the calls (about 100 from memory) to malloc() could have been passed a value of zero under boundary conditions.

In other words there were calls to malloc() like this:


  data_t * p = malloc(nelts * sizeof(data_t));

where the algorithm relied on nelts possibly having the value zero and the return pointer p not being NULL in this case.

The main problem with the proposal is that malloc() can also return NULL when memory is exhausted.  Lots of existing code would have to be changed to distinguish the two cases where NULL could be returned.  So code like this:


  if ((ptr == malloc(n)) == NULL)
      // handle error

would have to be changed to:


  if ((ptr == malloc(n)) == NULL && n > 0)
      // handle error

However, a further problem with the proposal was that it would be then impossible to distinguish between different zero-sized objects.  Of course, a simple workaround would be:


  #define good_malloc(s) malloc((s) == 0 ? 1 : (s))

Of course, there is no point in creating a macro to fix a broken malloc() when malloc() should behave correctly.

There is an even more subtle problem.  Can you see it?

Luckily, sense (partially) prevailed in that malloc(0) is not required to return NULL.  There was some sort of compromise and the behaviour is "implementation-defined".  Fortunately, I have never heard of a C compiler stupid enough to return NULL from malloc(0).

Sunday, November 3, 2013

DYNAMIC MEMORY ALLOCATION

Memory allocation operator new :

T *p; //declare p as a pointer
p= new T // p is the address of Memory for data type T
int *ptr1; // size of int is 2
long *ptr2; // size of long is 4
ptr1= new int;
ptr2= new long;



by default, the content in memory have no initial value. If such a value is desired, it must be supplied as a parameter when the operator is used:

p=new T(value);
ptr2= new long(1000000)


Dynamic array allocation :
  
    p=new T [ n] ; // allocate an array of n items of type T
Example:
    long *p;
    p=new long [ 50] // allocate an array of 50 long integers
       if (p==NULL)
    {
       cerr<< "Memory allocation error!<< endl;
    exit(1); // terminate the program
    }


The memory deallocation operator delete:


T *.p, *q; // p and q are pointers to type T
p=new T; // points to a single item
q new T[ n] ; //points to an array of elements
delete p; // deallocates the variable pointed by p
delete [ ] q; // deallocates the entire array pointed by q


Allocation of object data :

Example:



template <class T>
class DynamicClass
{ private:
// variable of type T and a pointer to data of type T
T member1;
T *member2;

public:
//constructor with parameters to initialize member data
DynamicClass(const T &m1, const T &m2)
// copy constructor : create a copy of the input object
DynamicClass(const DynamicClass<T> & obj)
// some methods...
....
//assignment operator
DynamicClass<T> operator=(const DynamicClass<T> rhs)
//destructor
~ DynamicClass(void)
}

// class implementation
//constructor with parameters to initialize member data
DynamicClass<T>::DynamicClass(const T &m1, const T &m2)
{
// parameter m1 initializes static member
member1=m1;
//allocate dynamic memory and initialize it with value m2
member2=new T(m2)
cout << "Constructor:"<<member1<<'/'<<*member2<<endl;


Example: The following statements define a static variable staticObj and a pointer variable dynamicObj.The static Obj has parameters 1 and 100 that initialize the data members:

//Dynamic Class object
DynamicClass<int> staticObj(1,100)


In the following, the object DynamicObj points to an object created by the new operator. Parameters 2 and 200 are supplied as parameters to the constructor:

//pointer variable
DynamicClass<int> *DynamicObj;
//allocate an object
DynamicObj=new DynamicClass<int>(2,200)



Running the program results in;

Constructor: 1/100
Constructor: 2/200


Deallocation Object Data: The Destructor

Consider the function F that creates a DynamicClass object having integer data
    void DestroyDemo(int m1,int m2)
 
   {DynamicClass<int> obj(m1,m2);

    }

Upon return from DestroyDemo obj is destroyed; however the process does not deallocate the dynamic memory associated with the object:




Dynamic data still remains in the system memory. For effective memory management, we need to deallocate the dynamic data within the object at the same time the object being destroyed. We need to reverse the action of the constructor, which originally allocated the dynamic data. The C++ language provides a member function, called the destructor, which is called by the compiler when an object is destroyed. For DynamicClass, the destructor has the declaration:


~ DynamicClass(void);

The character "~ " represents "complement", so ~ DynamicClass is the complement of a constructor. A destructor never has a parameter or a return type. For our sample class, the destructor is responsible to deallocate the dynamic data for member2.


// destructor: deallocates memory allocated by the constructor
template <class T>
DynamicClass<T>:~ DynamicClass(void);
{cout<<"Destructor:"<<member1"<<'/'<<member2<<endl;
delete member2;
}


The destructor is called whenever an object is deleted. When a program terminates, all global objects or objects declared in the main program are destroyed. For local objects created within a block, the destructor is called when the program exits the block.

Example :

void DestroyDemo(int m1, int m2)
{DynamicClass<int> Obj(m1,m2) ¬--------------- Constructor for Obj(3,300)
¬------------------------------------- Destructor for Obj
void main(void)
{DynamicClass<int> Obj1(1,100), *Obj2; ¬--------- Constructor for Obj1(1,100)
Obj2=new DynamicClass<int>(2,200); ¬------------ Constructor for *Obj2(2,200)
DestroyDemo(3,300);
delete Obj2; ¬------------------------------- Destructor for Obj2
¬-------------------------------------- Destructor for Obj1


running the program results in the output:

Constructor: 1/100
Constructor: 2/200
Constructor: 3/300
Destructor: 3/300
Destructor: 2/200
Destructor: 1/100


Assignment and initialization:
Assignment and initialization are basic operation that apply to any object. The assignment Y=X causes a bitwisecopy of the data from object X to the data in object Y. Initialization creates a new object that is a copy of another object. The operations are illustrated with objects X and Y.


// initialization
DynamicClass X(20,50), Y=X;
//creates DynamicClass objects X and Y
// data in Y is initialized by data in X
// assignment
Y=X;
//data in Y is overwritten by data in X


Special consideration must be used with dynamic memory so that unintended errors are not created. We must create new methods that handle object assignment and initialization.

Assignment Issues:




The assignment statement of B=A causes the data in A to be copied to B

member1 of B=member1 of A //copies static data from A to B
member2 of B=member2 of A //copies pointer from A to B


Example:
    void F(void)
    {DynamicClass<int> A(2,3), B(7,9);
       B=A
    }
After execution of DynamicClass<int> A(2,3), B(7,9);


 After execution of B=A we have;


although it was desired;


Solution is Overloading the assignment operator...

    // Overloaded assignment operator = returns a reference to the current object
    template <class T>
    DynamicClass<T>& operator= (const DynamicClass <T>& rhs)
    {//copy static data member from rhs to the current object
       member1=rhs.member1
    // content of the dynamic memory must be same as that rhs
       *member2=*rhs.member2;
       cout <<"Assignment Operator: "<<member1<<'/'<<*member2<<endl
    return *this;
    //reserved word this is used to return a reference to the current object
    }

Initialization Issues:

Object initialization is an operation that creates a new object that is a copy of another object. Like assignment, when the object has dynamic data, the operation requires a specific member function,called the copy constructor.


DynamicClass<int> A(3,5), B=A; //initialize object B with A


The declaration of A creates an object B whose initial data are member1=3 and *member2=5. The declaration of creates an object with two data members that are then structured to store the same data values found in A.
In addition to performing initialization when declaring objects, initialization also occurs when passing an object as a value parameter in a function. For instance, assume function F has a value parameter X of type DynamicClass<int>.


DynamicClass<int> F(DynamicClass<int> X) // value parameter
{DynamicClass<int> obj;
.....
return obj
}


When calling block uses object A as the actual parameter, the local object X is created by copying A:


DynamicClass<int> A(3,5), B(0,0); //declare objects
B=F(A) //call F by copying A to X


When the return is made from F, a copy of obj is made, the destructor for the local object X and obj are called, and the copy of obj is returned as the value of the function

Creating a copy constructor:

In order to properly handle classes that allocate dynamic memory, C++ provides the copy constructor to allocate dynamic memory for the new object and initialize its data values
The copy constructor is a member function that is declared with the class name and a single parameter. Because it is a constructor, it does not have a return value


//copy constructor: initialize new object to have the same data as obj.
template <class T>
DynamicClass<T>:: DynamicClass(const DynamicClass<T>& obj)
{// copy static data member from obj to current object
member1=obj.member1;
//allocate dynamic memory and initialize it with value *obj.member2
member2=new T(*.member2);
cout<<"Copy Constructor:"<<member1<<'/'<<member2<<endl;
}


If a class has a copy constructor, it is used by the compiler whenever it needs to perform initialization. The copy constructor is used only when an object is created.
Despite their similarity, assignment and initialization are clearly different operations. Assignment is done when the object on the left-handside already exists. In the case of initialization, a new object is created by copying data from an existing object.
The parameter in a copy constructor must be passed by reference. The consequence of failing to do so may result in catastrophic effects if it is not recognized by the compiler. Assume we declare the copy constructor


DynamicClass(DynamicClass<T> X)


The copy constructor is called whenever a function parameter is specified as call by value. In the copy constructor, assume object A is passed to the parameter X by value


DynamicClass(DynamicClass <T>X)
A


Since we pass A to X by value, the copy constructor must be called to handle the copying of A to X. This call in turn needs the copy constructor, and we have an infinite chain of copy constructor calls. Fortunately, this potential trouble is caught by the compiler, which specifies that the parameter must be passed by reference. In additiion, the reference parameter X should be declared constant, since we certainly do not want to modify the object we are copying.
    # include <iostream.h>
    # include "dynamic.h"
    template <class T>
       DynamicClass<int> Demo(DynamicClass<T> one, DynamicClass<T>& two, T m)
    { DynamicClass<T> obj(m,m);
       return obj;
    }
    void main()
    { DynamicClass<int> A(3,5), B=A, C(0,0);
       C=Demo(A,B,5);


    }

Running the program results in;


Constructor: 3/5 // construct A
Copy Constructor: 3/5 // construct B
Constructor: 0/0 // construct C
Copy Constructor: 3/5 // construct one
Constructor: 5/5 // construct obj
Copy Constructor: 5/5 // construct return object for Demo
Destructor: 5/5 // destruct obj upon return
Destructor: 3/5 // destruct A upon return
Assignment Operator: 5/5 // assign return object of Demo to C
Destructor: 5/5 // destruct return object of demo
Destructor: 5/5 // destruct C
Destructor: 3/5 // destruct B
Destructor: 3/5 // destruct A