469,958 Members | 1,914 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 469,958 developers. It's quick & easy.

Is it UB once it's an L-value?


Anything wrong with the following code?:
#include <cstdlib>

int main()
{
for (unsigned i = 0; i != 1000; ++i)
{
int *p = reinterpret_cast<int*>( std::rand() );
}
}
In the code, I'm creating an L-value pointer whose value is invalid.
However, the invalid memory address is never accessed.

Does the program exhibit Undefined Behaviour?

-Tomás
May 28 '06 #1
23 1900
On 2006-05-28 17:22, Tomás wrote:
Anything wrong with the following code?:
#include <cstdlib>

int main()
{
for (unsigned i = 0; i != 1000; ++i)
{
int *p = reinterpret_cast<int*>( std::rand() );
}
}
In the code, I'm creating an L-value pointer whose value is invalid.
However, the invalid memory address is never accessed.

Does the program exhibit Undefined Behaviour?


Can't see why, if just having an invalid pointer was enough to cause UB
then the following code would be a problem:

int main()
{
int *p;
return 0;
}

Erik Wikström
--
"I have always wished for my computer to be as easy to use as my
telephone; my wish has come true because I can no longer figure
out how to use my telephone" -- Bjarne Stroustrup
May 28 '06 #2
Tomás wrote:
int *p = reinterpret_cast<int*>( std::rand() ); Does the program exhibit Undefined Behaviour?


Yes, and sketching the result in hardware will illustrate why.

I can conceive of a CPU that efficiently stores pointers in special
registers, and which faults if a bit pattern copies into such a register
that doesn't refer to valid memory. So a compiler must compile your p
optimistically, and use this register even though you never dereference p.

This is among the reasons why accessing a deleted pointer is undefined.

I think the Motorola 68000 faults if an An register gets an odd number...

--
Phlip
http://c2.com/cgi/wiki?ZeekLand <-- NOT a blog!!!

May 28 '06 #3
Erik Wikström wrote:
Can't see why, if just having an invalid pointer was enough to cause UB
then the following code would be a problem: int *p;


You copied no value in. So in my register example, the compiler reserved the
register and did not change its current value. So the surrounding opcodes
must keep that value healthy.

--
Phlip
http://c2.com/cgi/wiki?ZeekLand <-- NOT a blog!!!
May 28 '06 #4
Phlip wrote:
Tomás wrote:
int *p = reinterpret_cast<int*>( std::rand() );

Does the program exhibit Undefined Behaviour?


Yes, and sketching the result in hardware will illustrate why.

[elaboration about a hypothetical implementation snipped]

No. The behavior is not undefined but implementation defined:

[5.2.10/5] (expr.reinterpret.cast)

A value of integral type or enumeration type can be explicitly converted to
a pointer. A pointer converted to an integer of sufficient size (if any
such exists on the implementation) and back to the same pointer type will
have its original value; mappings between pointers and integers are
otherwise implementation-defined.
Best

Kai-Uwe Bux
May 28 '06 #5
Phlip posted:

Yes, and sketching the result in hardware will illustrate why.

I can conceive of a CPU that efficiently stores pointers in special
registers, and which faults if a bit pattern copies into such a
register that doesn't refer to valid memory. So a compiler must
compile your p optimistically, and use this register even though you
never dereference p.

This is among the reasons why accessing a deleted pointer is
undefined.

I think the Motorola 68000 faults if an An register gets an odd
number...

But looking at it from an "official" viewpoint, why does it exhibit
undefined behaviour? Is it because I've turned an invalid pointer value
into an L-value?

Does the following code exhibit undefined behaviour:

#include <cstdlib>

int main()
{
reinterpret_cast<int*>( std::rand() );
}
-Tomás
May 28 '06 #6
Kai-Uwe Bux posted:

[5.2.10/5] (expr.reinterpret.cast)

A value of integral type or enumeration type can be explicitly
converted to a pointer. A pointer converted to an integer of
sufficient size (if any such exists on the implementation) and back to
the same pointer type will have its original value; mappings between
pointers and integers are otherwise implementation-defined.

My query has nothing to do with integer-to-pointer conversion -- it's to
do with storing an invalid pointer value in a pointer variable.
-Tomás
May 28 '06 #7
Kai-Uwe Bux wrote:
No. The behavior is not undefined but implementation defined:

[5.2.10/5] (expr.reinterpret.cast)

A value of integral type or enumeration type can be explicitly converted
to
a pointer. A pointer converted to an integer of sufficient size (if any
such exists on the implementation) and back to the same pointer type will
have its original value; mappings between pointers and integers are
otherwise implementation-defined.


He ain't converting from a pointer. rand() returns an int.

But while you have the Standard out, answer his next question; I don't know
the answer! ;-)

--
Phlip
http://c2.com/cgi/wiki?ZeekLand <-- NOT a blog!!!
May 28 '06 #8
Tomás posted:

Anything wrong with the following code?:

For the purpose of this example, assume that the resultant pointer value
gotten from converting the int is an actual valid pointer value (i.e. you
won't get any trapping values or whatever).
-Tomás
May 28 '06 #9
Tomás posted:

Anything wrong with the following code?:

Actually scrub all that. I've thought of a much better example.

Is the following UB:

int main()
{
double array[4];

double *p = array + 7;
}
And what about the following:

int main()
{
double array[4];

array + 7;
}

-Tomás
May 28 '06 #10
Phlip wrote:
Kai-Uwe Bux wrote:
No. The behavior is not undefined but implementation defined:

[5.2.10/5] (expr.reinterpret.cast)

A value of integral type or enumeration type can be explicitly converted
to
a pointer. A pointer converted to an integer of sufficient size (if any
such exists on the implementation) and back to the same pointer type will
have its original value; mappings between pointers and integers are
otherwise implementation-defined.


He ain't converting from a pointer. rand() returns an int.


Reread the first sentence of the quote. Also note the word "between" in the
last sentence (as opposed to "from pointers to integers").
Best

Kai-Uwe Bux

May 28 '06 #11
Tomás wrote:
Kai-Uwe Bux posted:

[5.2.10/5] (expr.reinterpret.cast)

A value of integral type or enumeration type can be explicitly
converted to a pointer. A pointer converted to an integer of
sufficient size (if any such exists on the implementation) and back to
the same pointer type will have its original value; mappings between
pointers and integers are otherwise implementation-defined.

My query has nothing to do with integer-to-pointer conversion -- it's to
do with storing an invalid pointer value in a pointer variable.


That is a good question. Technically, the standard defined valid pointer
values as follows:

[3.9.2/3]

A pointer to objects of type T is referred to as a ?pointer to T.? [Example:
a pointer to an object of type int is referred to as ?pointer to int? and a
pointer to an object of class X is called a ?pointer to X.? ] Except for
pointers to static members, text referring to ?pointers? does not apply to
pointers to members. Pointers to incomplete types are allowed although
there are restrictions on what can be done with them (3.9). A valid value
of an object pointer type represents either the address of a byte in memory
(1.7) or a null pointer (4.10). If an object of type T is located at an
address A, a pointer of type cv T* whose value is the address A is said to
point to that object, regardless of how the value was obtained. [Note: for
instance, the address one past the end of an array (5.7) would be
considered to point to an unrelated object of the array?s element type that
might be located at that address. ] The value representation of pointer
types is implementation defined. Pointers to cv-qualified and
cv-unqualified versions (3.9.3) of layout-compatible types shall have the
same value representation and alignment requirements (3.9).
So, the standard acknowledges the possibility of not-pointing valid pointer
values (e.g., past end values of arrays).

However, it also knows a way of invalidating pointer values:

[3.7.3.2/4]

If the argument given to a deallocation function in the standard library is
a pointer that is not the null pointer value (4.10), the deallocation
function shall deallocate the storage referenced by the pointer, rendering
invalid all pointers referring to any part of the deallocated storage. The
effect of using an invalid pointer value (including passing it to a
deallocation function) is undefined.
(Funny: deallocating an array seems to not invalidate the past-end pointer.)

Now, you create a pointer value in a way that is not really foreseen by the
standard. Without calling the deallocation function, technically, the value
should be valid. But I would guess that it's not the intend of the
standard. I would guess, the indent is that the behavior is undefined.
Also, I do not see a way of deducing this from the wording.
Best

Kai-Uwe Bux

May 28 '06 #12
Kai-Uwe Bux quoted:
...Except for
pointers to static members, text referring to ?pointers? does not apply to
pointers to members...
Translation: Someone should get brave and edit the Standard to replace
"pointer to member" with "virtual offset". Then we can take sentences like
these out, and have less verbiage in the Standard.
So, the standard acknowledges the possibility of not-pointing valid
pointer
values (e.g., past end values of arrays).


Only one-off-the-end.

--
Phlip
http://c2.com/cgi/wiki?ZeekLand <-- NOT a blog!!!
May 28 '06 #13
Phlip wrote:

I think the Motorola 68000 faults if an An register gets an odd number...


Nope. It faults if you use an odd address in the An register to access
anything larger than a byte.

Otherwise, how could a 68K handle the following snippet?

void f(char a[], const char *p)
{
a[0] = *p; // either this is an odd address
++p;
a[1] = *p; // or this is an odd address
}
May 28 '06 #14

"Tomás" <No.Email@Address> wrote in message
news:eJ******************@news.indigo.ie...
Tomás posted:

Anything wrong with the following code?:

Actually scrub all that. I've thought of a much better example.

Is the following UB:

int main()
{
double array[4];

double *p = array + 7;
}
And what about the following:

int main()
{
double array[4];

array + 7;


This creates an rvalue, then does nothing with it. Its not dereferenced the
pointer and has no side effects, so I believe this is safe.
}


I truely believe that UB only happens if you try to dereference the
behavior. I think a better question is: is this well defined behavior:

int main()
{
int SomeInt = 7;
int* p = SomeInt;

p += 100;
p -= 100;

std::cout << *p << std::endl;
}

Is that guaranteed to display 7? I would think yes.
May 28 '06 #15
Jim Langston posted:

int* p = SomeInt;

int *p = &SomeInt;
(I presume that was nothing more than a typo).

p += 100;
p -= 100;


Well given that overflow is undefined for signed integers, I would be less
than confident to make any presumptions about pointer arithmetic.

-Tomás
May 28 '06 #16
Jim Langston <ta*******@rocketmail.com> wrote:
I truely believe that UB only happens if you try to dereference the
behavior. I think a better question is: is this well defined behavior:

int main()
{
int SomeInt = 7;
int* p = SomeInt;

p += 100;
p -= 100;

std::cout << *p << std::endl;
} Is that guaranteed to display 7? I would think yes.


If p is so large that p += 100 causes an arithmetic overflow,
I think it's undefined.

S.
May 29 '06 #17
Tomás wrote:

int main()
{
double array[4];

array + 7;
}


This is definitely UB, see 5.6#5:
When an expression that has integral type is added to or
subtracted from a pointer
[...]
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.

May 29 '06 #18
Phlip schrieb:
Kai-Uwe Bux quoted:

...Except for
pointers to static members, text referring to ?pointers? does not apply to
pointers to members...

Translation: Someone should get brave and edit the Standard to replace
"pointer to member" with "virtual offset". Then we can take sentences like
these out, and have less verbiage in the Standard.


X-Post & F'Up2 comp.std.c++

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:st*****@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]

May 29 '06 #19

Old Wolf wrote:
Tomás wrote:

int main()
{
double array[4];

array + 7;
}
This is definitely UB, see 5.6#5:
When an expression that has integral type is added to or
subtracted from a pointer
[...]
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.


Very true.
Actually, the WG21 intended to allow implementations having bound
checkings. Knowing that they can overflow when a pointer which points
out of the "array" is formed by addition, except in the very special
case of pointers one past the last element. It means that if you add 1
to a pointer one past the last element of an array, it may throw an
implementation specific exception.

Kai-Uwe Bux wrote:
No. The behavior is not undefined but implementation defined:

[5.2.10/5] (expr.reinterpret.cast)

A value of integral type or enumeration type can be explicitly converted to
a pointer. A pointer converted to an integer of sufficient size (if any
such exists on the implementation) and back to the same pointer type will
have its original value; mappings between pointers and integers are
otherwise implementation-defined.
That's true.
In particular, it means that, if you have no access to the compiler
doc, or if you want to write perfectly portable code, you can't assume
anything on the behavior.From a standard point-of-view the behavior is undefined... But the behavior may be well-defined (or undefined) depending on the compiler's
documentation about this pointer mapping.
An implementation is free to say something like:
Converting integers whose alignment is not correct to pointers is
undefined.
Converting integers whose alignment is correct to pointers will yield
pointers whose representation bytes are exactly the same than these of
the integers, as described in another section of this doc.

Or, even:
If the integer which is not a multiple of the required alignment of a
type is reinterpret_casted to type*, the program will throw a bad_cast
exception

Furthermore, a "safe" implementation of C++ is possible (you know,
there are even C++ interpreters). In that implementation, the compiler
may save extra data about the type of objects, and keep track of the
fact that such integer actually contains a pointer-to-something... And
in that case, the implementation may simply say:
reinterpret_casting an integer to a pointer is forbidden, except if
this integer had been computed by a reinterpret_cast operation from a
pointer type.

You may say that it is illegal since two integer types might be equal
(as tested by operator==), but the reinterpret_cast would not behave
the same...
But it's legal, because there can be several representations of the
same integer (i.e. integers can have different bytes, and still be
equal).
For instance, it allows 1's complement integers.
Thus, this integer would contain... not 1 bit, but several bytes of
extra data which would not really participate to the actual
representation.

Note that it doesn't apply to char or unsigned char.

Jim Langston wrote: I truely believe that UB only happens if you try to dereference the
behavior. I think a better question is: is this well defined behavior:

int main()
{
int SomeInt = 7;
int* p = SomeInt;

p += 100;
p -= 100;

std::cout << *p << std::endl;
}

Is that guaranteed to display 7? I would think yes.

Of course not... Do you even know that signed integer overflow is UB.

For instance:
signed char c=127;
++c; // undefined behavior (assuming that MAX_CHAR is 127).

Similarly, pointers may have such problems (i.e. overflow in operations
that would do weird undefined things).

May 29 '06 #20
SuperKoko wrote:
Do you even know that signed integer overflow is UB.

For instance:
signed char c=127;
++c; // undefined behavior (assuming that MAX_CHAR is 127).


(There is no such thing as MAX_CHAR, I guess you mean CHAR_MAX).

This is assignment of an out-of-range value, and is implementation-
defined. Integer overflow would be:

int m = INT_MAX;
++m;

When evaluating your ++c, c is promoted to integer before
applying the ++ operator. The integer 127 can be incremented
to 128 successfully. Then when 128 is reassigned to c, the
out-of-range assignment occurs.

May 29 '06 #21
* Old Wolf:
SuperKoko wrote:
Do you even know that signed integer overflow is UB.

For instance:
signed char c=127;
++c; // undefined behavior (assuming that MAX_CHAR is 127).


(There is no such thing as MAX_CHAR, I guess you mean CHAR_MAX).

This is assignment of an out-of-range value, and is implementation-
defined. Integer overflow would be:

int m = INT_MAX;
++m;

When evaluating your ++c, c is promoted to integer before
applying the ++ operator. The integer 127 can be incremented
to 128 successfully. Then when 128 is reassigned to c, the
out-of-range assignment occurs.


++ is applied to lvalues. There's no such thing as promotion of
lvalues. However, the /result/ (after changing c), an rvalue, is promoted.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
May 30 '06 #22
Alf P. Steinbach wrote:
* Old Wolf:
SuperKoko wrote:

For instance:
signed char c=127;
++c; // undefined behavior (assuming that MAX_CHAR is 127).


When evaluating your ++c, c is promoted to integer before
applying the ++ operator. The integer 127 can be incremented
to 128 successfully. Then when 128 is reassigned to c, the
out-of-range assignment occurs.


++ is applied to lvalues. There's no such thing as promotion of
lvalues. However, the /result/ (after changing c), an rvalue, is promoted.


Let me rephrase then:

A constraint of prefix-++ is that the operand is a modifiable lvalue.
The actual operation consists of performing an rvalue conversion
(which results in an int), adding 1 to it, and then assigning this
rvalue back to the original lvalue, which causes the out-of-range
assignment.

(See 5.3.2 and 5.17 for references).

May 31 '06 #23
* Old Wolf:
Alf P. Steinbach wrote:
* Old Wolf:
SuperKoko wrote:
For instance:
signed char c=127;
++c; // undefined behavior (assuming that MAX_CHAR is 127).
When evaluating your ++c, c is promoted to integer before
applying the ++ operator. The integer 127 can be incremented
to 128 successfully. Then when 128 is reassigned to c, the
out-of-range assignment occurs.

++ is applied to lvalues. There's no such thing as promotion of
lvalues. However, the /result/ (after changing c), an rvalue, is promoted.


Let me rephrase then:

A constraint of prefix-++ is that the operand is a modifiable lvalue.
The actual operation consists of performing an rvalue conversion
(which results in an int), adding 1 to it, and then assigning this
rvalue back to the original lvalue, which causes the out-of-range
assignment.

(See 5.3.2 and 5.17 for references).


I don't find anything about rvalue conversion there. But then, I fail
to see how, for a non-volatile c, your description could be effectively
different from just adding 1 to c, which is what the standard
proscribes. So I think that for a non-volatile c it's a legitimate
point of view, but the rvalue conversion is IMHO (humble because failing
to see ~X isn't seeing X) an unnecessary complication.

For a volatile c the situation is a little more complicated.

But also there I think (vaguely! ;-)) it's possible to express that
point of view so that it works.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
May 31 '06 #24

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

reply views Thread by ss | last post: by
6 posts views Thread by Bob Bedford | last post: by
27 posts views Thread by Jacob Jensen | last post: by
5 posts views Thread by ShaunO | last post: by
3 posts views Thread by DarkPilgrim | last post: by
reply views Thread by rainxy | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.