Q:

Is accessing data outside an array incorrect if you don't use it?

In an algorithm I'm writing, I can have the following (simplified of course)
int a[3] = {1,2,3};
int b = a[3];

when the index used to fill b overflows, I never use the value of b. Is the code still incorrect? Do I have to make an explicit boundary check?

A:

This code has Undefined Behavior whether or not you use b. Why? Because a[3] is equivalent to *(a+3) by definition. And here's a quote from the standard that proved that *(a+3) is in itself undefined, regardless of whether the value is stored, used, or left alone.

When an expression that has integral
  type is added to or subtracted from a
  pointer, the result has the type of
  the pointer operand. If the pointer
  operand points to an element of an
  array object, and the array is large
  enough, the result points to an
  element offset from the original
  element such that the difference of
  the subscripts of the resulting and
  original array elements equals the
  integral expression. In other words,
  if the expression P points to the i-th
  element of an array object, the
  expressions (P)+N (equivalently,
  N+(P)) and (P)-N (where N has the
  value n) point to, respectively, the
  i+n-th and i−n-th elements of the
  array object, provided they exist.
  Moreover, if the expression P points
  to the last element of an array
  object, the expression (P)+1 points
  one past the last element of the array
  object, and if the expression Q points
  one past the last element of an array
  object, the expression (Q)-1 points to
  the last element of the array object.
  If both the pointer operand and the
  result point to elements of the same
  array object, or one past the last
  element of the array object, the
  evaluation shall not produce an
  overﬂow; otherwise, the behavior is
  undeﬁned.

A:

Still incorrect, still undefined behaviour. Do the bounds check.
int b = *(a + 3); // dereferencing beyond the array bound.

A:

Reading a[3] already causes undefined behaviour. As undefined behaviour is never locally limited, this can already lead to your harddisk drive being formatted or your computer emerging to a giant, flesh-eating zombie.
In reality, it will usually just work. But it's easy to make up a case where the end of the array marks the end of a mapped memory region, so accessing one element beyond would cause a segmentation fault. This is certainly not the case for an array of int on the stack and neither with most heap implementations, but you shouldn't rely on it.
(Whether taking the address of &a[3] is undefined behaviour as well is heavily disputed.)

