#include <iostream>
#include <cassert>
#include <vector>
// ---------------------------------------------------------------------------
std::vector<double> vect; // global
// ---------------------------------------------------------------------------
double f(double * x, int xpos)
{
// ! Is indexing safe here when calling with $ (tmp-i) + 1 $ (-> line 45)
return ( x[xpos + 1] - x[xpos - 1] ) * vect[xpos];
}
// ---------------------------------------------------------------------------
int _tmain(int argc, _TCHAR* argv[])
{
// INIT
staticconst ptrdiff_t N = 10;
double * x = newdouble[N];
for (int i = 0; i != N; ++i) { vect.push_back(i); x[i] = 10*i; }
// DOIT
// example 1 : safe use of $ f $
for (int i = 1; i != N-1; ++i) std::cout << f(x, i) << std::endl;
std::system("PAUSE");
// example 2 : questionalbe use of $ f $
double tmp[3];
// I dont wanna pass negative pointer value at line 45
// - or even may I ?
assert(tmp - (double*)(NULL) >= N);
for (int i = 1; i != N-1; ++i) {
tmp[0] = x[i-1] / 2;
tmp[2] = x[i+1] / 2;
std::cout << f( (tmp-i) + 1, i ) << std::endl; // line 45
}
std::system("PAUSE");
// FINISH
delete[] x;
return 0;
}
to begin with, std-compliant compilers don't have _tmain or _TCHAR, define ptrdiff_t in stddef.h or equivalently, std::ptrdiff_t in cstddef, and system() in the header stdlib.h (or, equivalently, std::system in cstdlib)
as for pointer arithmetic, it is only defined within the bounds of an array
specifically, tmp - (double*)NULL is undefined (you can only subtract two pointers that point to the elements of the same array) tmp - i is undefined for any i greater than zero
The thing about C and C++ is you can always override the compiler and impose your will. I can't think of a single rule that cannot be legitimately bypassed as far as the compiler is concerned. That's primarily why C has not been replaced as a systems programming language in 40 years.
But with that power comes the responsibilty to not corrupt or crash your applications or its environment.
Your pointer arithmetic will allow you index to a memory location (relative to tmp). But you have the responsibility of ensuring that you don't try to derference that pointer if it's out of bounds.
In your example, you can treat tmp as an array N big. But if it's really just 3 big, you'll be referencing memory that doesn't belong to tmp. The content of that memory cannot be determined apriori in a general way. We call this undefined behaviour. We think this is bad in the same way shooting yourself in the foot is bad.
you can always override the compiler and impose your will
this is usually like with compilers, but not like with the standard... :)
Following Cubbi I did some more search and I am afraid Cubbi is right : Pointer arithmetics is undefined when going out of the alocated range - read the arithmetics itself...
It means even *((tmp-3)+3)=0 is undefined bhv. Explicitly, standard allows such a weird compiler that it would be undefined (I seriously doubt such a compiler exists when dealing with std C-style pointers, but anyway...)
ISO/IEC 14882:2003, 5.7 [expr.add] 5
...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 overflow; otherwise, the behavior is undefined.
I was trying to explain why the pointer arithmetic was undefined.
The standard does allow you to override the various type checking rules. What do you think is happening when you use const_cast, reinterpret_cast, friend?
I see it not as much compiler issue as standard issue... I mean it is not undefined because you can possibly access an invalid element, but the compiler is generally not required that arithmentics outside the scope of the allocated field has any meaning at all.
as in my example above: *((tmp-3)+3) is not allowed even though *(tmp+(-3+3)) is perfectly OK
The standard does allow you to override the various type checking rules. What do you think is happening when you use const_cast, reinterpret_cast
What happens in those casts is specified in excruciating detail, because in many important situations, the behavior is undefined. You can circumvent the static type checking system to get invalid code to compile, but it won't make it work reliably.
When you use reinterpret_cast or const_cast or friend you are overriding the language's type system and imposing your will (and taking responsibility for the consequences).
Reason :
pointer arithmetic is not guaranteed by std outside the range of <first; one-past-the-last> element of the array object even for intermediate results, even though the final result is within the valid range and even though the arithmetic operations themselves may make sense