views:

680

answers:

8

I am converting an extremely large and very old (25 years!) program from C to C++.

In it there are many (very very many) places where I access a global one dimensional UBYTE array using a variety of integer indexes. Occasionally this index may be negative.

I sometimes, but not always, trapped this case and made sure nothing went wrong, but as a belt and braces measure I actually went to the trouble of making sure that there was another chunk of memory immediately preceding the array and filled it with the right values such that if I accidentally omitted to trap the negative number condition then a correct answer would still be fetched in the array access. This actually worked fine for many many years.

But now under C++ it seems that accessing an array with a negative number behaves differently and now I have a program behaving badly. I fixed one case of an unhandled negative number and the program appears to be working fine, but I am nervous that I have not trapped all the negative numbers and there may be problems ahead.

So my question now is, is there a way, at runtime, for me to detect any instances of accessing arrays with negative indexes? I'll be impressed if anyone can come up with an answer. If you're pretty certain it can not be done in any automated way then telling me that is valuable information too.

I should just add that I'm not really a C++ programmer (yet). So far all I've done is the absolute bare minimum (almost nothing) to get the program to compile under a C++ compiler. So if your answer involves fancy "experts only, C++ solutions", then please try and explain in words of one syllable or give me a link so I can look it up.

+2  A: 

the only way i can think of would be to wrap the array in a method that checks the index is positive.

wefwfwefwe
+4  A: 

Some kind of memory usage checker might work, particularly if you can force the array to its own page. I'd use valgrind on Linux, not sure what you'd use on Windows - purify?

Douglas Leeder
I would normally be the first to promote valgrind for something like this, but recently we had exactly this problem and it failed to find it. I do not know the internals of valgrind but my impression is that only reliably catches heap issues.
Richard Corden
+25  A: 

Can you replace the global one-dimensional ubyte array with an object with overloaded operator[]? Using the absolute value of the int input might solve some of your issues.

Edit: Depending on the usage pattern of your array (no pointer shenanigans), using an object with overloaded operator[] could actually be entirely transparent to the users of the array, hence my suggestion.

MadKeithV
Personally, I'd throw an exception instead of using the absolute value. (Fail early, fail loud. Using the absolute value of the index would merely continue the program with some more or less random value.)
DevSolar
Your comment is correct - though I was under the impression that the original app had "hacked" around the negative index issue by mirroring the array in memory around 0 - giving the kind of behaviour with negative indexes that absolute value might do as well.
MadKeithV
as I understand the question, the original version of the program hacked around it, but now he wants to be notified when negative indices occur, so he can actually *fix* the program. Which sounds like the right way to go. And then of course, an exception would be correct (although logging it as well, and probably firing an assert might be useful as well)
jalf
+1 For the suggestion of replacing the array with an object and operator[]. This is a good way of keeping track.
StackedCrooked
+3  A: 

Wrap it. In alternative, check if your compiler supports boundary checking. Worst case scenario, I would not put valid values in your array at negative numbers. I would put blatantly wrong values, so that you get a clearly wrong value when this occurs. You will not pinpoint the issue without a debugger, but at least you have a clear warning that somewhere, some code is doing it.

Stefano Borini
Does visual studio 2008 support boundary checking?
Mick
No idea, never used VS, but if it does not, then microsoft has a problem.
Stefano Borini
The problem is that C (and C++ by extension) definitely allow array index with a negative index, as long as you don't actually form an index outisde the array (!). Therefore you can't simply do boundary checks, not in Visual C++ nor in other C/C++ compilers.
MSalters
Since I guess it will do negative pointer arithmetic, doing so will be allowed because you are pointing to an arbitrary vmem area. As long as the compiler does not provide precode to check this as an unusual runtime issue. I remember that the IBM C compiler on the sp4 has this check.
Stefano Borini
+1  A: 

Personally I'd try to sort out why any of your indices could ever be negative; it's obvious that they will return an incorrect value so surely you shouldn't let the index to go negative?

Fix the code that allows them to go negative rather than trying to make a square plug fit into a circular hole (so to speak) ;)

Siyfion
1. The array access is super-speed critical so I'm reluctant to permanently wrap it all up in function calls.2. In a way accessing the array with negative numbers does make logical sense (in this specific domain) so allowing the original dodgy solution would be my favorite answer!
Mick
One of the nice things about C++ is that compilers inline it very aggressively. As long as everything is visible to the compiler, small functions *will* get inlined, so the overhead is exactly zero. Don't worry too much about wrapping it in function calls (as long as the full function definitions, not just declarations, are visible)
jalf
By the way, this information should go in the question, where everyone sees it, not in a comment. The question gives the impression that negative indices are errors that should be fixed.If negative indices are allowed in your problem domain, let us know that. That changes the solution completely. :)
jalf
I can see what you're saying there Mick, but as jalf says, most small "wrapping" functions will just get made to be inline with little (if any) noticable performance cost. If accessing the array with negative numbers makes sense, perhaps you should create a second "negative" array, and just switch which is accessed?
Siyfion
+2  A: 

Is there a way, at runtime, for me to detect any instances of accessing arrays with negative indexes?

Replace your global array by std::vector. Replace use of the array[index] syntax by

vector.at(index)

This does exactly what you ask for: it adds runtime array bound checks.

Wim Coenen
A: 

One thing you can do is you can check till what minimum value your indexes are going, and start filling your array from that index.

a[-i] = *(a - i)

so start with (a-i) as your base address instead of (a) so you will just shift your array and get desired values.

GG
+1  A: 

Do you know the bounds of the indices?

Reading the comment under Siyfion's answer, it seems like negative indices are allowed in your problem domain, and so there's no point in trying to force all indices to be positive. A better solution is to allow negative indices without breaking any language rules (which your C version also did btw. You were just lucky it didn't explode ;))

So if you know that the indices you're going to use are in the range -x to y, simply create an array of size x+y, and use a pointer to the xth element when accessing the array.

For example, assuming you need an array with indices -4 to 5:

int arr[10];
int* ptr = arr+4;
// use ptr when you need to index into the array:

ptr[-4]; // is valid, reads arr[0]
ptr[5]; // is valid, reads arr[9]

Of course the same could (and should) have been done in C.

Another piece of advice would be to not access the raw array directly. Define simple accessor functions instead. (for example, but not necessarily, by putting the array in a class. If you do put it in a class, you can overload operator[] so it'll even look like an array still)

The overhead of this is precisely zero (not "nearly zero, or "so close to zero you won't notice the difference", but zero. The generated code will be exactly the same as if you'd accessed the array directly), as the compiler will inline short functions, but it also allows you to insert extra checks so that you can verify in debug builds that you never go past the bounds of the array.

jalf