Блог пользователя ashwanikumarsharma

Автор ashwanikumarsharma, история, 2 месяца назад, По-английски

Anyone who know it properly, Please explain!

Cpp Code

#include<iostream>
using namespace std;

int main(){
    int p = 5;
    int q = ++p * ++p ;
    cout<<p<<endl;
    cout<<q<<endl;

    p = 5;
    q = ++p * p++;
    cout<<p<<endl;
    cout<<q<<endl;

    p = 5;
    q = p++ * ++p;
    cout<<p<<endl;
    cout<<q<<endl;

    p = 5;
    q = p++ * p++;
    cout<<p<<endl;
    cout<<q<<endl;

return 0;
}

Java Code

class incre{
    public static void main(String args[]){
        int p = 5;
        int q = ++p * ++p;
        
        System.out.println(p);
        System.out.println(q);

        p = 5;
        q = ++p * p++;

        System.out.println(p);
        System.out.println(q);

        p = 5;
        q = p++ * ++p;
        System.out.println(p);
        System.out.println(q);

        p = 5;
        q = p++ * p++;
        System.out.println(p);
        System.out.println(q);
    }
}

Cpp Output

7
49
7
42
7
35
7
30

Java Output

7
42
7
36
7
35
7
30

Although java output is obvious and it is what I expect but lots of confusion in Cpp

If possible attach some reliable document to read this

  • Проголосовать: нравится
  • -5
  • Проголосовать: не нравится

»
2 месяца назад, # |
Rev. 2   Проголосовать: нравится -16 Проголосовать: не нравится

Here's an article on pre-increment and post-increment in C++. I think it could be helpful to you.

»
2 месяца назад, # |
  Проголосовать: нравится +29 Проголосовать: не нравится

Your C++ program is undefined behavior.

There are plenty of C++ compilers. While their developers had some freedom (after all, the resulting compilers are not identical, and the executables produced by compiling same code might differ vastly), there is a set of rules any C++ compiler should follow. This set of rules is called C++ standard.

One great thing C++ standard defines is what a well-formed program is, and what should be a behavior of a well-formed program once it is compiled. I am oversimplifying here, but a well-formed program is one which is written absolutely correctly with full compliance to the standard. (I am fully omitting the implementation-defined stuff here, please read about it in the Internet if you want to know more.)

However, programmers are prone to errors, so definitely not all programs on earth that will ever be compiled are well-formed. Another great thing that C++ standard does is some classification of ways a code can be erroneous, and what a compiler should do in this case. For example, a program can be ill-formed — in this case the standard says that the compiler should detect this, abort the compilation and throw a reasonable error message. In particular, such program always gets a Compilation Error in Codeforces, and this is indeed what this is.

C++ is an obscure language in the sense that there are ways to write a program which is not compliant with the standard but which is extremely difficult to detect. For example, you can write a program that is well-formed if and only if Riemann hypothesis is false. Probably the property of the program being well-formed is merely undecidable. The standard did a sneaky thing and did not shoulder this burden on the C++ compiler developers; instead the entirety of the responsibility is on the C++ code writer. There is a thing called IFNDR (ill-formed, no diagnostic required) — a type of programs which are incorrect in a way which is not required to trigger a compiler error. If you allow such a mistake in your program, the compiler has a complete freedom of action: it might throw a compilation error, or it might compile succesfully and result in some program. Any resulting executable file would be a correct result of the compiler, even one which cannot execute or one which formats your C drive right away. Theoretically, if you don't want your C drive be cleansed, you should never run a program which was produced by compiling an ill-formed program; but in practice in is usually considered no big deal (luckily for us, C++ compiler developers don't aim to format as many hard drives around the world as possible).

The phenomenon of a program being so wrong wrong so that even if it is compiled, the resulting executable can do literally anything, is called undefined behavior (UB). C++ standard does its best describing all sorts of mistakes that lead to UB, some examples can be found here: https://en.cppreference.com/w/cpp/language/ub. Among them, there is this phrase: "Some examples of undefined behavior are [...] more than one modifications of the same scalar in an expression that is unsequenced". Since you have a line int q = ++p * ++p ; with two modifications of p which, according to the standard, can be applied in any order, your whole program is UB and, if it is compiled successfully, it can do practically anything. We can translate this conversation into the "implementation-defined" plane and discuss the supposition that actually the program most probably tries to perform these two increments in some order, speculate on this order, but I believe this is a wrong thing to do. Avoid undefined behaviors in your code and do not expect anything nice if you failed.

»
2 месяца назад, # |
Rev. 2   Проголосовать: нравится +15 Проголосовать: не нравится

In C++, using multiple pre-increment/post-increment operations in one expression changing the same variable is undefined behaviour, so the program could, in theory, do basically whatever it wants. I'm assuming that a different compiler would end up giving different results.

The way I understand why this is the case, (which may be wrong, I'm not an expert when it comes to nuances of C++), the root of the issue here is that C++ standard doesn't specify the order of evaluation in situations similar to this (probably because it allows better optimization). They may even be computed at the same time. If the evaluation has side effects (meaning, doesn't just return an answer, but changes something else), and if two operations unsequenced with respect to each other (meaning, they may happen in any order) have side effects affecting the same place in memory, you have a problem, so the behavior is undefined.

I understand if this is confusing, so unless you're sure you know what you're doing, I would advice you to not use i++ or ++i in an expression if you also want to use its return value, and definitely don't use i++ or ++i in an expression if there is another part of the expression affecting i (such as another ++i) or reading i (such as a[i]).

For more reading, here's a related stack overflow answer and a wikipedia link.

  • »
    »
    2 месяца назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    Multiple pre-increment/post-increment operations in one expression are ok, if they're applied to different scalars (so these modifications don't affect same value). So a = ++b + c++ should be fine.

    • »
      »
      »
      2 месяца назад, # ^ |
        Проголосовать: нравится +18 Проголосовать: не нравится

      I somehow managed to forget to say that, even though it obviously applies. Thanks for pointing this out.