• Advertisement
Sign in to follow this  

why does this work

This topic is 4334 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

And now for something completely different: This works while I think it should crash: why? OK, i have an array containing items of the class test. The array is called "proef" and has 40 elements. test contains a bool that stores if a test was started. "isgestart" returns this bool. Now I want to start the first test that was not started, so I write: void starttest(){ int i=0; system("cls"); while (proef.isgestart()){i++;} cout << "Proefpersoon" << i+1 << endl <<endl; proef.start(); } works fine, but when I start more than 40 tests, it still seems to work... PS I know system("cls") is a dirty way of cleaning the screen, but i need this program by wednesday.

Share this post

Link to post
Share on other sites
It's because C++ doesn't perform bounds checking. It'll quite happily write to any index you tell it to. If the number is quite large, that'll probably cause your program to crash, as it attempts to write to memory it doesn't have permission to write to. But for 41, 42, 43, etc. it may well "work". That doesn't mean it's a good idea, though: it might work today and not work tomorrow.

Share this post

Link to post
Share on other sites
Short answer: it doesn't really. This invokes one instance of "undefined behaviour", and anything the computer can do is technically allowed by the standard to happen - including seeming to work, crashing the computer, or emailing the contents of your pr0n folder to the government. (Admittedly, that last one is rather less likely.)

Longer answer: like Sneftel said, C++ doesn't provide (generate in the compiled code) bounds checking. So when 'i' reaches 40, the program is happy to consider a chunk of memory just past the end of your array, the same size as an element, as if the data there represented a valid element. And then perform the 'isgestart()' code, using a pointer to that memory as the this-pointer.

At that point, what happens depends on what values happened to be stored in the memory there, and whether or not the system will allow you to access that memory . Modern OSes provide memory protection so that if you try to use memory that doesn't "belong" to your program, it will just bail out right away. However, memory close to your array is likely to "belong" anyway - often it is holding some other variable in your program, or something that was pushed on the stack, etc. Sometimes it becomes possible to change other variables that way, and cause a mysterious crash sometime later - very difficult to debug.

This is one of the many reasons to avoid arrays - and also null-terminated strings - as much as possible, preferring the standard library containers, or when appropriate other wrappers like Boost::Array; and to make sure to iterate in "safe" ways. In the sample code, a for-loop is more appropriate, since you have an easy way to specify where the loop bounds are. You can also make use of a standard library 'algorithm':

#include <algorithm>
// in addition to your other includes.

// Here I am using 'persoon' as the type of the proef[] elements
void starttest(){
persoon* first_not_started = find_if(proef, proef + 40,
// find_if is in namespace std of course
// The '40' here is simply the number of elements in your array.
// What you gain by doing it this way is not having to worry about a counter
// variable like 'i'; when you are nesting loops that can prevent errors like
// for (int i = 0; i < 10; ++i) {
// for (int j = 0; j < 10; ++i) { <-- oops!
// and it also means you don't have to think so much about the logic of the
// iteration.
// Also, if you later change 'proef' to be a std::vector for example, then
// all you need to do is change the bounds to (proef.begin(), proef.end()
// and it will work again - plus, then you are not limited in the number of
// elements :)
if (first_not_started == proef + 40) {
// There was nothing found :(
cout << "I don't really know Dutch" << endl; // ;)
// With the std::vector, you could handle this by push_back()ing another
// persoon and starting it. But then, you probably wouldn't need all this
// isgestart/start logic then anyway; just hold as many things as you need
// to start, and not keep any unstarted instances.
} else {
cout << "Proefpersoon" << first_not_started - proef + 1 << endl <<endl;

Share this post

Link to post
Share on other sites
thanks a lot guys!

I just thought there was bouds checking in C++...

I'll look into the algorithm library. Sorry about the dutch in the code by the way, but I'm working together on this thing with someone who knows little about programming and less about Englisch (yes, they exist), so it's easier to explain with dutch variables...

Share this post

Link to post
Share on other sites
Sign in to follow this  

  • Advertisement