Answers to Washu's quiz #1?

Started by
29 comments, last by MaulingMonkey 15 years, 2 months ago
I just came across Washu's C++ quiz, when I skimmed through this GDNet thread, and I just finished answering the questions. Can someone check my answers? (He didn't provide the answers for the first quiz)
Quote:Given the following three lines of code, answer these questions.
int* p = new int[10];
int* j = p + 11;
int* k = p + 10;
1.A) Is the behavior of the second line well defined? 1.B) If the behavior second line is well defined, where does the pointer point to? 1.C) What are some of the legal operations that can be performed on the third pointer?
1.A) Yes 1.B) It points to the location immediately after the int array (The address of 'p' + sizeof(int)*10). What's contained there, on the other hand, could be utter gibberish. 1.C) You can use any pointer manipulation operators on it, like any other pointer. (add, subtract, multiply, divide, etc...)
Quote:2) What output should the following line of code product?
int a = 10;
std::cout<<a<<a++<<--a;
2) 10 9 9
Quote:3) Assuming the function called in the following block of code has no default parameters, how many parameters does it take? Which objects are passed to it? f((a, b, c), d, e, ((g, h), i));
3) It takes four parameters. (a, d, e, and g)
Quote:4) Assuming the function called in the following block of code takes an A* and a B*, what is bad about the code?
f(new A(), new B());
4) What's bad is that you'll leak memory unless 'f' knows to free both 'A' and 'B', because the scope of A and B will end immediately following the function call, and so the user has no chance to free it themselves. 'f' must do the freeing (or else keep pointers to them to free later, depending on what the function is used for), and 'f' must also do the error checking, in case 'new' fails. I know I made a few mistakes, from reading this, but did I get any of the other questions right?
Advertisement
Quote:1.A) Yes

Actually, I don't think so. You're allowed to point one past the end of the array, but not past that. The last element of the array is (p + 9). So one past is (p + 10). (p + 11) is undefined.

Quote:1.C) You can use any pointer manipulation operators on it, like any other pointer. (add, subtract, multiply, divide, etc...)

You can't multiply/divide pointers.

Quote:2) 10 9 9

Isn't this undefined behaviour? a, a++ and --a could be evaluated in any order.

Quote:4) What's bad is that you'll leak memory unless 'f' knows to free both 'A' and 'B', because the scope of A and B will end immediately following the function call, and so the user has no chance to free it themselves. 'f' must do the freeing (or else keep pointers to them to free later, depending on what the function is used for), and 'f' must also do the error checking, in case 'new' fails.

There's no error checking that can be done. If new fails, an exception is thrown, and memory will leak if either of the allocations succeeded before throwing. That's why this is bad - because you have a 50% chance of leaking memory if either allocation fails.
NextWar: The Quest for Earth available now for Windows Phone 7.
The answers are here and perhaps in the comments here. Favor the first link. Sadly, you got most of them wrong (or at least partially so) :( But most people do, so its okay.
Quote:Original post by Sc4Freak
Quote:2) 10 9 9

Isn't this undefined behaviour? a, a++ and --a could be evaluated in any order.

Correct -- there are no sequence points seperating the evaluation of a, a++, and --a. Since a is int, this violates the rule of not modifying integral types multiple times between sequence points, the penalty is UB.

Were "a", say, a non-pointer iterator type instead, you'd merely get an unspecified order of evaluation.

Operator precedence is (almost) entirely unrelated to evaluation order -- it just specifies that a+b+c is evaluated as operator+( operator+(a,b), c ) rather than operator+( a , operator+(b,c) ). Only a few operators define any extra sequence points: the intrinsic ",", "&&", "||", and "?:" operators. These only apply to the intrinsic version -- overloaded versions need not apply.

To consider a( b(), c() );, the only (relevant) sequence point is that a() will be executed sometime after both b() and c() have.

Quote:From the C++ Standard (ISO/IEC 14882:2003), page 65, with various styling added

5 Expressions [expr]

...

4 Except where noted, the order of evaluation of operands of individual operators and subexpressions of indi-
vidual expressions, and the order in which side effects take place, is unspecified.53) Between the previous
and next sequence point a scalar object shall have its stored value modified at most once by the evaluation
of an expression.
Furthermore, the prior value shall be accessed only to determine the value to be stored.
The requirements of this paragraph shall be met for each allowable ordering of the subexpressions of a full
expression; otherwise the behavior is undefined
. [Example:
    i = v[i++];  // the behavior is unspecified    i = 7, i++, i++;  //  i becomes 9 [MM:  intrinsic operator, adds sequence points]    i = ++i + 1;  // the behavior is unspecified    i = i + 1;  // the value of i is incremented

—end example]


[Edited by - MaulingMonkey on January 23, 2009 7:25:37 PM]
Disclaimer: My answers might be wrong.

Quote:Given the following three lines of code, answer these questions.
int* p = new int[10];int* j = p + 11;int* k = p + 10;

1.A) Is the behavior of the second line well defined?
1.B) If the behavior second line is well defined, where does the pointer point to?
1.C) What are some of the legal operations that can be performed on the third pointer?


1.A) No
1.B) It is not well defined, it is out of the bounds of the deferenceable range + 1.
1.C) All pointer operations sans dereferencing.

Quote:2) What output should the following line of code product?
int a = 10;std::cout<<a<<a++<<--a;


2) Three numbers, values consisting of 10 and 9. Specifics are unspecified since order of evaluation is unspecified.

Edit: Well apparently this is undefined as opposed to unspecified.

Quote:3) Assuming the function called in the following block of code has no default parameters, how many parameters does it take? Which objects are passed to it?
f((a, b, c), d, e, ((g, h), i));

3) It takes four parameters: c, d, e, i

Quote:4) Assuming the function called in the following block of code takes an A* and a B*, what is bad about the code?
f(new A(), new B());

4) If one allocation fails then an exception is thrown, if the the other allocation had already succeeded then memory is leaked.
Quote:Original post by dmatter
Quote:2) What output should the following line of code product?
int a = 10;std::cout<<a<<a++<<--a;


2) Three numbers, values consisting of 10 and 9. Specifics are unspecified since order of evaluation is unspecified.

Were it not UB, it could be 11 as well. Consider the case where a++, then a, then --a are evaluated.

Quote:
Quote:3) Assuming the function called in the following block of code has no default parameters, how many parameters does it take? Which objects are passed to it?
f((a, b, c), d, e, ((g, h), i));

3) It takes four parameters: c, d, e, i

Good catch.
Thanks for linking to the answers.

I couldn't remember whether the comma operator evaluates both objects, and returns the first, or evaluates both, and returns the second. Apparently it's the second. (Question 3) The others I just flat out got wrong.

However, I must question Washu's answer to #2.

Quote:2. This line of code has two problems. The first is that the order in which function parameters are evaluated is unspecified. As such, a, a++, and --a can all be processed in any order. However, there is a bigger problem. We are not allowed to modify the same object more than once between sequence points, yet in this case, we have done so twice. As such the behavior of this code is undefined. It could legally obliterate the universe.

std::cout isn't really a function. It's overloading the << operator, and the << operator has a defined order of which objects are evaluated first.

It'd evaluate to:
(((std::cout << a) << a++) << --a);

With each pair of parapheses returning a reference to std::out. (Like here)

It'd print 10, 10, 10 every time, regardless of compiler. (I said 10 9 9, because like the comma operator, I mistakenly swapped the order of evaluation in my mind)

Isn't that correct?

[Edit:] Others posted before I finished my post, so my response as already been broken before it even became public. [smile]
Isn't C++ fun?
Quote:Original post by MaulingMonkey
Quote:Original post by dmatter
2) Three numbers, values consisting of 10 and 9. Specifics are unspecified since order of evaluation is unspecified.

Were it not UB, it could be 11 as well. Consider the case where a++, then a, then --a are evaluated.
Oops, thanks for the catch, 11 slipped by me.
Quote:Original post by Servant of the Lord
It'd print 10, 10, 10 every time, regardless of compiler. (I said 10 9 9, because like the comma operator, I mistakenly swapped the order of evaluation in my mind)

I'm bored.

MSVC 9, Release: 9910
MSVC 9, Debug: 10910
MSVC 8, Release: 9910
MSVC 8, Debug: 10910
MSVC 7.1, Release: 9910
MSVC 7.1, Debug: 10910
g++ 3.4.4 cygming -O0: 101010
g++ 3.4.4 cygming -O1: 101010
g++ 3.4.4 cygming -O2: 101010
g++ 3.4.4 cygming -O3: 101010
g++ 3.4.4 cygming -Os: 101010
Borland Turbo C++ 2006, Debug: 1099
Borland Turbo C++ 2006, -O1: 1099
Borland Turbo C++ 2006, -O2: 1099


This topic is closed to new replies.

Advertisement