ace5's blog

By ace5, 15 months ago, In English

Take a look at this code:

Code

At first glance, it should output $$$3$$$ because that's what $$$f()$$$ returns, which is then assigned to $$$k_0$$$. But when executed, this code outputs $$$1$$$!

This code is very short and everything seems clear, we're simply assinging $$$k_0$$$ to the return value of $$$f()$$$ which is $$$3$$$, and the push_back in $$$f()$$$ clearly doesn't affect the result... or does it?

How an assingment operation really works

It may seem obvious at first, we just copying one value to another, but let's see how compiler actually handles this operation.

Consider this code. (Source)

Code

This code can output any permutation from '$$$abcabc$$$' to '$$$cbacba$$$', because the evaluation order of function arguments and operands in addition is unsequenced in C++. This flexibility allows compilers to optimize performance by evaluationg arguments in any order (or even simultaneously).

And assignment is just another operation, meaning it's left and right operands can be evaluated in any order.

In our original example here's what happens:

1) Compiler evaluates the left operand (obtaining a reference to $$$k_0$$$).

2) Then, it calls $$$f()$$$.

3) Finally, it assigns the return value ($$$3$$$) to the previously obtained reference.

And if you're familiar with how vectors work, you might already see the issue.

How vectors work

A vector is a dynamic container, it's size can change, and elements can be added ('push_back') or removed ('pop_back').

Every vector has a capacity (the allocated memory space, initially empty). And when you call push_back, one of two things happens:

1) Capacity is sufficient. The new element is placed at the end, and the vector's 'end()' pointer is incremented.

2) Capacity is insufficient. A new memory block (typically $$$2x$$$ the current size) is allocated, existing elements are copied/moved to the new location, and then everything is the same as in the first case.

Vectors store elements contiguosly for fast indexing, so if reallocation occurs, the entire vector moves to a different memory location.

So what happens?

1) k.push_back(1) initializes the vector with $$$1$$$.

2) In $$$k_0 = f()$$$, the compiler:

2.1) First evaluates the left side (obtaining a reference to $$$k_0$$$).

2.2) Then calls $$$f()$$$, which performs k.push_back(3), and if reallocation occurs, reference to $$$k_0$$$ changes.

2.3) Finally, it assigns $$$3$$$ to the now invalid reference, leaving the actual $$$k_0$$$ unchanged ($$$1$$$).

Conclusion

You can avoid this bug in many different ways, such as:

1) Storing the value of $$$f()$$$ in a variable and then assigning it to $$$k_0$$$

2) Preallocate memory (do k.reserve(2) in this example) to ensure no reallocation happens during push_back.

3) Push_back to the vector before or after the function if possible.

I faced this mistake while debugging a Cartesian tree problem, and spent 2-3 hours figuring it out. Hopefully, this blog helps you avoid the same mistake or at least know how to fix it.

Have you faced a similar mistake?

Full text and comments »

  • Vote: I like it
  • +254
  • Vote: I do not like it

By ace5, 2 years ago, translation, In English

Today I got this message:

Spoiler

When looking at the solutions of the listed participants on the problem 1951B I saw that all these solutions completely (or with a change of variables) same as mine, so it's not just a coincidence, solutions copied from mine. Since I didn't give my solution to anyone, and didn't keep the code publicly available, this could only happen if one of the participants blocked this task, copied my code, and sent it to the others.

Indeed, it turns out that in my room only one participant blocked task B — _Untrackable_(the nickname speaks for itself). He blocked this task at 1:44, and if you look at the submissions of participants from the list, you can see that many of them earlier sent code for this task that was completely different from mine (some even in other languages!), but all of them at approximately 1:55 (after he blocked this task), sent almost exactly my code.

This method of cheating at contests allows the person who sends solutions to everyone to remain unpunished, and all the punishment goes to another honest participant.

Please look at this problem, MikeMirzayanov.

Full text and comments »

  • Vote: I like it
  • +1054
  • Vote: I do not like it

By ace5, history, 3 years ago, translation, In English

Hello, Codeforces!

I and ooaa are excited to invite you to participate in Codeforces Round 922 (Div. 2), which will take place on Jan/30/2024 17:35 (Moscow time).

This round will be rated for participants, whose rating is below 2100. Participants with higher rating can participate unofficially.

You will be given 7-8 problems and 2 hours to solve them. We recommend you to read all the problems. 1 or more interactive problems may occur in the round, so we suggest to read this post.

We would like to thank:

We wish you good luck and high rating!

UPD. Score distribution: $$$500 - 1000 - 1250 - 2000 - 2500 - 3000 - 3250$$$.

UPD2. Congratulations to the winners!

Div. 2

  1. alice_ssoi

  2. JaredGoff

  3. ppltn

  4. sunc_mgu_govno

  5. 1926_yes

Div. 1+2

  1. arvindf232

  2. SSerxhs

  3. alice_ssoi

  4. natofp

  5. JaredGoff

Congratulations to the first solves as well!

UPD3. Editorial.

Full text and comments »

  • Vote: I like it
  • +575
  • Vote: I do not like it