Is this pointer arithmetics safe?

Hi,
I hope my problem is well apparent from the (simplified example) code below...

// by safeness I mean "working well at any std-compliant compiler"

Thanks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

#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

	static const ptrdiff_t N = 10;

	double * x = new double[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;
}
Last edited on
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
oh yes, I am aware of to begin with but I did not care
- sorry

... so there is no way to cheat the compiler with that fake array of N - damn!
so there is no way to cheat the compiler
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.

If you want an array of N, allocate one.
Last edited on
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.
Last edited on
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?
Last edited on
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

(I do not see any relevance to casting here)

Perhaps I missed your point - no flame :)
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.
Last edited on
My point is that they allow you to override the type system ... anyway I can see all this is going nowhere fast.
Last edited on
My point is that the idea that "you can always override the compiler and impose your will" is fundamentally wrong. C is not assembly language.
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).
Well guys, we are getting a bit OT ...

So to summarize:

Question :
Is this pointer arithmetics safe?

Answer :
NO

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

Source :
ISO/IEC 14882:2003, 5.7 [expr.add] 5
Last edited on
Topic archived. No new replies allowed.