 |
BorlandTalk.com Borland discussion newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Petar Popara Guest
|
Posted: Thu Mar 10, 2005 8:13 am Post subject: operator ++ doubts (2) |
|
|
I would like to thank everybody who replayed on my previous post (with the
same title like this post). I live in a different time zone, so I couldn't
be involved in previous discussion. My post was inspired by some code of
some other author which I found in a project I have inherited. It was only
simplification of that code, so I would like to ask you again to also
analise this (original) code:
1.
int b[2] = {0xf0, 0x0f};
int i=0;
int t = b[i++] | b[i++];
Will it *allways* (and on every compiler) evaluate to t=0xf0 and i=2 or
behaviour is undefined?
2.
int i = 0;
t = ((m_inputBuffer[i++] << 4) & 0x30) | (m_inputBuffer[i] >> 4);
Will "i" on the right side *ever* be 1?
Any links to Stroustrup's books/articles or C/C++ faqs that would backup
your comments are welcome (very much).
Thank you very much in advance.
|
|
| Back to top |
|
 |
Wiljo Guest
|
Posted: Thu Mar 10, 2005 9:51 am Post subject: Re: operator ++ doubts (2) |
|
|
Petar Popara wrote:
| Quote: | I would like to thank everybody who replayed on my previous post (with the
same title like this post). I live in a different time zone, so I couldn't
be involved in previous discussion. My post was inspired by some code of
some other author which I found in a project I have inherited. It was only
simplification of that code, so I would like to ask you again to also
analise this (original) code:
1.
int b[2] = {0xf0, 0x0f};
int i=0;
int t = b[i++] | b[i++];
Will it *allways* (and on every compiler) evaluate to t=0xf0 and i=2 or
behaviour is undefined?
2.
int i = 0;
t = ((m_inputBuffer[i++] << 4) & 0x30) | (m_inputBuffer[i] >> 4);
Will "i" on the right side *ever* be 1?
Any links to Stroustrup's books/articles or C/C++ faqs that would backup
your comments are welcome (very much).
Thank you very much in advance.
After much deliberation and reviewing the other posts of the previous thread, I |
must agree that modifying and object twice in a single expression results in
Undefined Behaviour. It is compiler dependant, and it's result even depends on
optimiziations turned on or off.
If "i" were a class, and an operator++ was written for it. it would look
something like this:
class myInt
{
int value;
public: myInt( void ) { value = 0; }
myInt( int Value ) { value = Value; }
int operator++( void ); //* pre increment
int operator++( int ); //* post increment
myInt & operator=( int Value );
};
int myInt::operator++( void ) //* pre increment
{
value++;
return value;
}
int myInt::operator++( int ) //* post increment
{
int result = value;
value++;
return result; //* returns prior value
}
myInt & myInt::operator=( int Value )
{
value = Value;
return *this;
}
void testFunc( void )
{
myInt i = 0;
int result = ++i + ++i;
}
This piece of code already behaves different then to when "i" were just an int.
It is more likely that this piece of code finishes with "i" being 2, and
"result" being 1. And result has another value altogether than with "i" being
just an int. I must conclude that the value of "result" must be considered
Undefined, it might be 0, 1 or 2. Whichever way you look at it, it is best to
avoid these kind of constructs. Use only one increment per object per expression
if you still want to write compact code. If a second increment is necessary
write it on a seperate line altogether. Keep in mind which of the two operators
is used either pre- or post-increment. This doesn't really matter when the
increment is alone on a seperate line, but it's behaviour does matter within a
"complex" expression.
Wiljo.
|
|
| Back to top |
|
 |
Wiljo Guest
|
Posted: Thu Mar 10, 2005 11:10 am Post subject: Re: operator ++ doubts (2) |
|
|
Wiljo wrote:
| Quote: | This piece of code already behaves different then to when "i" were just
an int. It is more likely that this piece of code finishes with "i"
being 2, and "result" being 1.
myInt i = 0; |
int result = ++i + ++i;
Here offcourse result will be 3, because of the pre-increments. Both ++
operators are now member functions, which are called successively. The order of
the calls depends on the compiler, but that doesn't really matter in this
instance. "i" will be incremented twice. The first call returns 1, and the
second call returns 2. Result will be 3 (( 1 + 2 ) or ( 2 + 1 )).
myInt i = 0;
int result = i++ + i++;
Here result will be 1. The first post-increment (and that could be the latter)
returns 0, and the second (and that could be the first) then returns 1. So
either way (0 + 1) or (1 + 0) will result in 1. It is still not clear in which
order the compiler handles this expression, but because of "i" now being a class
the result will be what we expect. (offcourse we need to know how the post- or
pre-increment behaves).
Although a class places the compiler in some kind of straight jacket, and makes
the evaluation more precise, it still doesn't mean that this approach is better
than the first. When "i" is just an int the result of the evaluation is
Undefined. Although the result is no longer Undefined when "i" is a class, the
same must apply to it anyway. From the original line (result = ++i + ++i) or
(result = i++ + i++) it is not evident that classes are being used. The line
stays Undefined, because the reader of the code no longer knows what to expect.
I hope this clarifies it for everyone.
These constructs should be avoided at all times.
Wiljo.
|
|
| Back to top |
|
 |
Alan Bellingham Guest
|
Posted: Thu Mar 10, 2005 12:53 pm Post subject: Re: operator ++ doubts (2) |
|
|
"Petar Popara" <none> wrote:
| Quote: | int t = b[i++] | b[i++];
Will it *allways* (and on every compiler) evaluate to t=0xf0 and i=2 or
behaviour is undefined?
|
Undefined behaviour. (i is incremented twice, and also read.)
Better written as
int t = b[i] | b[i+1];
i += 2;
| Quote: | t = ((m_inputBuffer[i++] << 4) & 0x30) | (m_inputBuffer[i] >> 4);
Will "i" on the right side *ever* be 1?
|
It could be. But with '|', we don't know which side will be evaluated
first, since both must be.
Better written as
t = ((m_inputBuffer[i] << 4) & 0x30) | (m_inputBuffer[i+1] >> 4);
++i;
(And probably just as efficient.)
This original code makes assumptions which probably do actually work, at
least on the original compiler. But changing compiler may produce
changed results, since as previously mentioned, the rules aren't
guaranteeing what should occur.
Part of the C standard (and for these purposes, the C++ standard) is a
rigorous attempt to specify an abstract computing device in terms of
what it should do under all circumstances. A C compiler is then correct
if it produces code that behaves in exactly that way under the same
circumstances.
Because of the differences in hardware and operating systems, there are
cases where an attempt to specify certain behaviour on certain platforms
would lead to inefficiencies in the code that could be used elsewhere.
In this particular case, it comes down to whether code may be reordered.
Looking at the second example, we have the following primitive
sub-expressions (assuming I have made no errors):
(1) address1 = m_inputBuffer + i
(2) i = i + 1
(3) temp1 = *address1
(4) temp2 = temp1 << 4
(5) temp3 = temp2 & 0x30
(6) address2 = m_inputBuffer + i
(7) temp4 = *address2
( temp5 = temp4 >> 4
(9) t = temp3 | temp 5
Note that there is no 'sequence point' in here.
Now, in the above, the compiler is permitted to rearrange the order of
things, so long as none of the pseudo-temp variables has its order of
setting and reading changed, and so long as (2) occurs after (1).
(Oh, and those temps are almost certainly registers.)
Let's assume our processor has the ability to shift multiple registers
at the same time. This means that it might want to do:
address1 = m_inputBuffer + i
i = i + 1
temp1 = *address1
address2 = m_inputBuffer + i
temp4 = *address2
temp5 = temp4 >> 4 ... and at the same time ... temp2 = temp1 << 4
temp3 = temp2 & 0x30
t = temp3 | temp 5
The current rules means that it can - there has been no sequence point,
everything is internal, so it's free to do so. But this lack of rules on
how it reorders things also means that the "i = i + 1" line is permitted
to float down to the end, which would produce a different result.
Alan Bellingham
--
ACCU Conference 2005 - 20-23 April, Randolph Hotel, Oxford, UK
|
|
| Back to top |
|
 |
Petar Popara Guest
|
Posted: Thu Mar 10, 2005 1:04 pm Post subject: Re: operator ++ doubts (2) |
|
|
| Quote: | Better written as
int t = b[i] | b[i+1];
i += 2;
|
Is this also OK:
int t = b[i++];
t |= b[i++];
t = ((m_inputBuffer[i++] << 4) & 0x30);
t |= (m_inputBuffer[i] >> 4);
|
|
| Back to top |
|
 |
Alan Bellingham Guest
|
Posted: Thu Mar 10, 2005 1:29 pm Post subject: Re: operator ++ doubts (2) |
|
|
"Petar Popara" <none> wrote:
| Quote: | Is this also OK:
int t = b[i++];
t |= b[i++];
|
Yes, i is incremented only once per statement.
| Quote: | t = ((m_inputBuffer[i++] << 4) & 0x30);
t |= (m_inputBuffer[i] >> 4);
|
Yes, no problem here either.
Alan Bellingham
--
ACCU Conference 2005 - 20-23 April, Randolph Hotel, Oxford, UK
|
|
| Back to top |
|
 |
Gillmer J. Derge [TeamB] Guest
|
Posted: Thu Mar 10, 2005 2:54 pm Post subject: Re: operator ++ doubts (2) |
|
|
Wiljo wrote:
| Quote: | Whichever
way you look at it, it is best to avoid these kind of constructs.
|
This single sentence is really the best piece of advice in both of these
threads. If the code is so complicated and strange that you need to
post to a newsgroup asking what it does, then you should change it.
Even if it turns out to be valid code and you eventually figure out what
it does, where does that leave the next guy who needs to take over the
code when you leave?
--
Gillmer J. Derge [TeamB]
|
|
| Back to top |
|
 |
Thomas Maeder [TeamB] Guest
|
Posted: Thu Mar 10, 2005 5:11 pm Post subject: Re: operator ++ doubts (2) |
|
|
Wiljo <invalid (AT) invalid (DOT) address> writes:
| Quote: | After much deliberation and reviewing the other posts of the previous
thread, I must agree that modifying and object twice in a single
expression results in Undefined Behaviour.
|
This is too strong. For example, this:
int i(someValue());
++i || ++i;
someOtherValue() ? ++i : --i;
is correct code because ?: and built-in || and && have a sequence
point after the evaluation of the left(-most) sub-expression.
| Quote: | It is compiler dependant,
and it's result even depends on optimiziations turned on or off.
If "i" were a class, and an operator++ was written for it. it would
look something like this:
class myInt
{
int value;
public: myInt( void ) { value = 0; }
myInt( int Value ) { value = Value; }
int operator++( void ); //* pre increment
int operator++( int ); //* post increment
myInt & operator=( int Value );
};
[snip]
void testFunc( void )
{
myInt i = 0;
int result = ++i + ++i;
}
This piece of code already behaves different then to when "i" were
just an int. It is more likely that this piece of code finishes with
"i" being 2, and "result" being 1.
|
It's not just more likely, but guaranteed. There are sequence points
between the two increment operations.
| Quote: | Whichever way you look at it, it is best to avoid these kind of
constructs.
|
Wholeheartedly agreed.
|
|
| Back to top |
|
 |
Wiljo Guest
|
Posted: Mon Mar 14, 2005 10:49 am Post subject: Re: operator ++ doubts (2) |
|
|
Thomas Maeder [TeamB] wrote:
| Quote: | Wiljo <invalid (AT) invalid (DOT) address> writes:
After much deliberation and reviewing the other posts of the previous
thread, I must agree that modifying and object twice in a single
expression results in Undefined Behaviour.
This is too strong. For example, this:
int i(someValue());
++i || ++i;
someOtherValue() ? ++i : --i;
is correct code because ?: and built-in || and && have a sequence
point after the evaluation of the left(-most) sub-expression.
Thanks, you are absolutely right. Up to this thread I didn't even know about |
sequence points, apart from the ';' that is. But I did know that || and && were
being processed from left to right, and that as soon as one condition fails the
remaining part will not even be evaluated. Thus I realise now that these kinds
of expressions must contain sequence points. I am glad that I know about them
now. This said, my remark must be changed to: Modifying an object twice within
the boundaries of two sequence points results in Undefined Behaviour.
| Quote: |
It is compiler dependant,
and it's result even depends on optimiziations turned on or off.
If "i" were a class, and an operator++ was written for it. it would
look something like this:
class myInt
{
int value;
public: myInt( void ) { value = 0; }
myInt( int Value ) { value = Value; }
int operator++( void ); //* pre increment
int operator++( int ); //* post increment
myInt & operator=( int Value );
};
[snip]
void testFunc( void )
{
myInt i = 0;
int result = ++i + ++i;
}
This piece of code already behaves different then to when "i" were
just an int. It is more likely that this piece of code finishes with
"i" being 2, and "result" being 1.
It's not just more likely, but guaranteed. There are sequence points
between the two increment operations.
Yes, that was what I was trying to explain. Using a class like this introduces |
sequence points. And thank you for making it clear that when "i" is a class with
correctly defined operators, the result is guaranteed. As long as you know what
is expected though, because my example was faulty. Apart from missing some
operators, the value of result is 3, instead of 1 as I mistakenly wrote down.
See my sub-post for this correction. It is the difference between the pre- and
post-increment that confused me at the time.
| Quote: |
Whichever way you look at it, it is best to avoid these kind of
constructs.
Wholeheartedly agreed.
Thanks, I hope everyone understands this. |
Wiljo.
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|