tags:

views:

2114

answers:

15
+7  A: 

g++ does not check for array bounds, and you may be overwriting something with 3,4 but nothing really important, if you try with higher numbers you'll get a crash.

You are just overwriting parts of the stack that are not used, you could continue till you reach the end of the allocated space for the stack and it'd crash eventually

EDIT: You have no way of dealing with that, maybe a static code analyzer could reveal those failures, but that's too simple, you may have similar(but more complex) failures undetected even for static analyzers

Arkaitz Jimenez
+3  A: 

It's undefined behavior as far as I know. Run a larger program with that and it will crash somewhere along the way. Bounds checking is not a part of raw arrays (or even std::vector).

Use std::vector with std::vector::iterator's instead so you don't have to worry about it.

Edit:

Just for fun, run this and see how long until you crash:

int main()
{
   int array[1];

   for (int i = 0; i != 100000; i++)
   {
       array[i] = i;
   }

   return 0; //will be lucky to ever reach this
}

Edit2:

Don't run that.

Edit3:

OK, here is a quick lesson on arrays and their relationships with pointers:

When you use array indexing, you are really using a pointer in disguise (called a "reference"), that is automatically dereferenced. This is why instead of *(array[1]), array[1] automatically returns the value at that value.

When you have a pointer to an array, like this:

int array[5];
int *ptr = array;

Then the "array" in the second declaration is really decaying to a pointer to the first array. This is equivalent behavior to this:

int *ptr = &array[0];

When you try to access beyond what you allocated, you are really just using a pointer to other memory (which C++ won't complain about). Taking my example program above, that is equivalent to this:

int main()
{
   int array[1];
   int *ptr = array;

   for (int i = 0; i != 100000; i++, ptr++)
   {
       *ptr = i;
   }

   return 0; //will be lucky to ever reach this
}

The compiler won't complain because in programming, you often have to communicate with other programs, especially the operating system. This is done with pointers quite a bit.

Hooked
I think you forgot to increment "ptr" in your last example there. You've accidentally produced some well-defined code.
I guess you should have ptr++ in for loop in edit3
seg.server.fault
Haha, see why you shouldn't be using raw arrays?
Hooked
+3  A: 

You are certainly overwriting your stack, but the program is simple enough that effects of this go unnoticed.

Paul Dixon
Whether the stack is overwritten or not depends on the platform.
Chris Cleeland
+26  A: 

Welcome to every C/C++ programmers bestest friend: Undefined Behavior.

There is a lot that is not specified by the language standard, for a variety of reasons. This is one of them.

In general, whenever you encounter undefined behavior, anything might happen. The application may crash, it may freeze, it may eject your CD-ROM drive or make demons come out of your nose. It may format your harddrive or email all your porn to your grandmother.

It may even, if you are really unlucky, appear to work correctly.

The language simply says what should happen if you access the elements within the bounds of an array. It is left undefined what happens if you go out of bounds. It might seem to work today, on your compiler, but it is not legal C or C++, and there is no guarantee that it'll still work the next time you run the program. Or that it hasn't overwritten essential data even now, and you just haven't encountered the problems that is going to cause yet.

As for why there is no bounds checking, there are a couple aspects to the answer:

  • An array is a leftover from C. C arrays are about as primitive as you can get. Just a sequence of elements with contiguous addresses. There is no bounds checking because it is simply exposing raw memory. Implementing a robust bounds-checking mechanism would have been almost impossible in C.
  • In C++, bounds-checking is possible on class types. But an array is still the plain old C-compatible one. it is not a class. Further, C++ is also built on another rule which makes bounds-checking non-ideal. The C++ guiding principle is "you don't pay for what you don't use". If your code is correct, you don't need bounds-checking, and you shouldn't be forced to pay for the overhead of runtime bounds-checking.
  • So C++ offers the std::vector class template, which allows both. operator[] is designed to be efficient. The language standard does not require that it performs bounds checking (although it does not forbid it either). A vector also has the at() member function which is guaranteed to perform bounds-checking. So in C++, you get the best of both worlds if you use a vector. You get array-like performance without bounds-checking, and you get the ability to use bounds-checked access when you want it.
jalf
Hey, I was correct!
Hooked
@Jaif : we have been using this array thing for so long, but still why are there no test to check such simple error ?
seg.server.fault
C++ design principle was that it shouldn't be slower than the equivalent C code, and C doesn't do array bound checking. C design principle was basically speed as it was aimed for system programming. Array bound checking takes time, and so is not done. For most uses in C++, you should be using a container rather than array anyway, and you can have your choice of bound check or no bound check by either accessing an element via .at() or [] respectively.
KTC
@seg Such a check costs something. If you write correct code, you don't want to pay that price. Having said that, I've become a complete convert to std::vector's at() method, which IS checked. Using it has exxposed quite a few errors in what I thought was "correct" code.
anon
@seg.server.fault: Because c++ lets you program close to the metal, and automatic bounds checking 1) costs cycles and 2) gets in the way of some clever hack that you should avoid using. This is a feature, not a bug. If you want a bounds checking array-like object, create one.
dmckee
Because C++ lets the programmer decide what is an error. I don't know how other languages do bounds checking, but if it is at runtime, there would be an associated overhead. C++ is often used for application, game, and OS development, where speed is necessary.
Hooked
added a few notes on *why* there is no bounds checking
jalf
@Hooked: Actually, C++ is very clear on what is an error. Out of bounds accesses are clear errors. The C++ standard just does not specify the *consequences* of writing erroneous code.
jalf
In general, whenever you encounter undefined behavior, anything might happen. The application may crash, it may freeze, it may eject your CD-ROM drive or make demons come out of your nose. It may format your harddrive or email all your porn to your grandmother. Made me laugh :)
seg.server.fault
I believe old versions of GCC actually launched Emacs and an a simulation of Towers of Hanoi in it, when it encountered certain types of undefined behavior. Like I said, *anything* may happen. ;)
jalf
Everythings already been said, so this only warrants a small addendum. Debug builds can be very forgiving in these circumstances when compared to release builds. Due to debug information being included in debug binaries, there's less of a chance that something vital is overwritten. That's sometimes why the debug builds seem to work fine whilst the release build crash.
Rich
+3  A: 

C or C++ will not check the bounds of an array access.

You are allocating the array on the stack. Indexing the array via array[3] is equivalent to *(array + 3), where array is a pointer to &array[0]. This will result in undefined behavior.

One way to catch this sometimes in C is to use a static checker, such as splint. If you run:

splint +bounds array.c

on,

int main(void)
{
    int array[1];

    array[1] = 1;

    return 0;
}

then you will get the warning:

array.c: (in function main) array.c:5:9: Likely out-of-bounds store: array[1] Unable to resolve constraint: requires 0 >= 1 needed to satisfy precondition: requires maxSet(array @ array.c:5:9) >= 1 A memory write may write to an address beyond the allocated buffer.

Karl Voigtland
Correction: it's already been allocated by the OS or another program. He is overwriting other memory.
Hooked
Saying that "C/C++ will not check the bounds" isn't entirely correct - there's nothing precluding a particular compliant implementation from doing so, either by default, or with some compilation flags. It's just that none of them bother.
Pavel Minaev
+2  A: 

Undefined behavior working in your favor. Whatever memory you're clobbering apparently isn't holding anything important. Note that C and C++ do not do bounds checking on arrays, so stuff like that isn't going to be caught at compile or run time.

John Bode
No, Undefined behavior "works in your favor" when it crashes cleanly. When it appears to work, that's about the *worst* possible scenario.
jalf
You are entirely correct; poor wording on my part.
John Bode
A: 

Read about buffer overflow.

Nick D
A: 

Run this through Valgrind and you might see an error.

As Falaina pointed out, valgrind does not detect many instances of stack corruption. I just tried the sample under valgrind, and it does indeed report zero errors. However, Valgrind can be instrumental in finding many other types of memory problems, it's just not particularly useful in this case unless you modify your bulid to include the --stack-check option. If you build and run the sample as

g++ --stack-check -W -Wall errorRange.cpp -o errorRange
valgrind ./errorRange

valgrind will report an error.

Todd Stout
Actually, Valgrind is quite poor at determining incorrect array accesses on the stack. (and rightfully so, the best it can do is mark the entire stack as a valid write location )
Falaina
@Falaina - good point, but Valgrind can detect at least some stack errors.
Todd Stout
+1  A: 

When you initialize the array with int array[2], space for 2 integers is allocated; but the identifier array simply points to the beginning of that space. When you then access array[3] and array[4], the compiler then simply increments that address to point to where those values would be, if the array was long enough; try accessing something like array[42] without initializing it first, you'll end up getting whatever value happened to already be in memory at that location.

Edit:

More info on pointers/arrays: http://home.netcom.com/~tjensen/ptr/pointers.htm

Nathan Clark
A: 

when you declare int array[2]; you reserve 2 memory spaces of 4 bytes each(32bit program). if you type array[4] in your code it still corresponds to a valid call but only at run time will it throw an unhandled exception. C++ uses manual memory management. This is actually a security flaw that was used for hacking programs

this can help understanding:

int * somepointer;

somepointer[0]=somepointer[5];

yan bellavance
+4  A: 

Using g++, you can add the command line option: -fstack-protector-all.

On your example it resulted in the following:

> g++ -o t -fstack-protector-all t.cc
> ./t
3
4
/bin/bash: line 1: 15450 Segmentation fault      ./t

It doesn't really help you find or solve the problem, but at least the segfault will let you know that something is wrong.

Richard Corden
A: 

Hint

If you want to have fast constraint size arrays with range error check, try using boost::array, (also std::tr1::array from <tr1/array> it will be standard container in next C++ specification). It's much faster then std::vector. It reserve memory on heap or inside class instance, just like int array[].
This is simple sample code:

#include <iostream>
#include <boost/array.hpp>
int main()
{
    boost::array<int,2> array;
    array.at(0) = 1; // checking index is inside range
    array[1] = 2;    // no error check, as fast as int array[2];
    try
    {
       // index is inside range
       std::cout << "array.at(0) = " << array.at(0) << std::endl;

       // index is outside range, throwing exception
       std::cout << "array.at(2) = " << array.at(2) << std::endl; 

       // never comes here
       std::cout << "array.at(1) = " << array.at(1) << std::endl;  
    }
    catch(const std::out_of_range& r)
    {
        std::cout << "Something goes wrong: " << r.what() << std::endl;
    }
    return 0;
}

This program will print:

array.at(0) = 1
Something goes wrong: array<>: index out of range
lionbest
A: 

As I understand, local variables are allocated on stack, so going out of bounds on your own stack can only overwrite some other local variable, unless you go oob too much and exceed your stack size. Since you have no other variables declared in your function - it does not cause any side effects. Try declaring another variable/array right after your first one and see what will happen with it.

Vorber
A: 

When you write 'array[index]' in C it translates it to machine instructions.

The translation is goes something like:

  1. 'get the address of array'
  2. 'get the size of the type of objects array is made up of'
  3. 'multiply the size of the type by index'
  4. 'add the result to the address of array'
  5. 'read what's at the resulting address'

The result addresses something which may, or may not, be part of the array. In exchange for the blazing speed of machine instructions you lose the safety net of the computer checking things for you. If you're meticulous and careful it's not a problem. If you're sloppy or make a mistake you get burnt. Sometimes it might generate an invalid instruction that causes an exception, sometimes not.

Jay
A: 

Hello, for this problem you can use BugFighter C/C++ that can be downloaded from www.bugfighter-soft.com

It is indipendent from plattform.

It works with all compiler because it is a simple parser tha introduce test code.

It is a Windows exe file, so you must copy the source file on a Pc with Windows, modify it with BugFighter and than copy it back to your Linux/Unix project.

Than you can compile it again and run it.

I tried it with Visual C++ and Eclipse and it worked well, even with multidimensional array.

On the BugFighter web site there some video and example that you can download. Some video are also available on Youtube.

Bye

Luca

include

using namespace std; int main() { int array[2]; array[0] = 1; array[1] = 2; array[3] = 3; array[4] = 4; cout << array[3] << endl; cout << array[4] << endl; return 0; }

When I compile and run this program, I am not getting any error (no error during compile and during runtime).It simple prints 3 and 4. Why ? AFAIK It should be not possible to do array[3] = 3 and array[4] = 4.