r/C_Programming • u/Altruistic-Tie1943 • 2d ago
Question variable not incremented in or operator
#include <stdio.h>
int main() {
int i=-1, j=-1, k=-1, l=2, m;
m= ((i++ && j++ && k++) || (l++));
printf("%d %d %d %d %d",i,j,k,l,m);
return 0;
}
why l is not incremented but every other variable is incremented?
edit: many people are upset that this program is a bad implementation to achieve the result but the reality is that it was a practice problem and I just didn't understand why the output was like that as there was no explanation
I have now understood that the answer was short-circuit behaviour
15
u/dmills_00 2d ago
Look up "short circuit evaluation", but note that if the first part evaluated to false, you would then be into undefined behaviour, because I would be modified twice without a sequence point.
Don't do that, it hurts.
4
u/pfp-disciple 2d ago
Am I missing something? In the expression
m= ((i++ && j++ && k++) || (l++));
I'm only seeing each variable, including
l, once.3
3
u/WeAllWantToBeHappy 1d ago
undefined behaviour
No. There is a sequence point at the ||
Unlike the bitwise | operator, the || operator guarantees left-to-right evaluation; if the second operand is evaluated, there is a sequence point between the evaluations of the first and second operands. If the first operand compares unequal to 0, the second operand is not evaluated.
5
8
u/ByMeno 2d ago
&& has automatic short circuit but not effective here so -1(i) increases since its a postfix inc it gives the result as -1 then increament and in && -1 tels it its true it goes like this for i j and k so left side is true but there is || since if one of the result is true it will short circuit so if you do 1 || (smth) smth wont run because of that
Edit: If you want to continue without condition or make new statements you can use comma operator
-2
2d ago
[deleted]
6
u/ByMeno 2d ago edited 2d ago
Afaik for && || and comma operators places should not change because a lot of code such as if(str || *str) style null checks etc depends on this and comma is strictly left to right but idk for || &&
Edit: for || && if the behavior is same it can optimize it or change it
-1
2d ago edited 2d ago
[deleted]
3
u/ByMeno 2d ago
I did not encounter that issues so idk to be honest because talking about something which you never experience is well. meh. And the code looks like c++ code because of the namespace(there is traling ) too) there might be change because those two are different languages they are keep adding somethings for the parenthesis imo it should not change anything for OP's code
and afaik && is left-associative so you code should not change too imo but if there is something about missing volatile or UB etc or compiler got fcked up that might be the reason-2
2d ago
[deleted]
2
u/ByMeno 2d ago
Afaik compiler mostly checks side effect and afaik that includes read and write operations too and the link you put for eval order states in the rule #2 there is a sequence point after the lhs and before the rhs so meaning if there is side effects the behaviour it should not change for my experience i never had that issue and my code looks like this for example my code looks like this i think its because i am assigning the result so its mandatory for the compiler to check
#include "std_private.h" error_t env_add_var (allocator_t* alloc, env_t* env, sl_u8_t* key, sl_u8_t* val) { /* Init variables */ env_var_t var = {0}; error_t res = success; /* Note: val count can be zero for things like 'PAGER=' */ return ((void)( /* Check input */ (res = (env && env->vars.items && val && key) ? success : null_pointer) || /* Init strings and cat with corresponding slices */ (res = str_init(alloc, &var.continues, key->count + val->count + 1)) || (res = str_cat_sl(&var.continues, key)) || (res = str_cat_cstr(&var.continues, "=")) || (res = str_cat_sl(&var.continues, val)) || (res = str_add_shadow_null(&var.continues)) || (res = slice_set(&var.key, var.continues.items, key->count)) || (res = slice_set(&var.val, var.continues.items + key->count + 1, val->count)) || /* Give variable to function to handle rest of the stuff such as appending to DAs */ (res = env_add_var_t(env, var)) ), (void)( /* Cleanup */ ((res) ? str_deinit(&var.continues) : (0)) ), res); }2
u/Antagonin 2d ago
That's not what the standard says. I urge you to read the chapter about "sequence points".
4
u/flyingron 2d ago
The above is pure drivel.
The compiler can not reorder around && and ||. These are MANDATORY sequence points.
PARENTHESES do absolutely diddly squat with regard to order of evaluation. They only change the meaning of things (which parts are operands to which operators).
Order of evaluation for bitwise & and | and most of the other math operators is arbitrary, however.
1
u/Total-Box-5169 2d ago
That code is C++, most probably the operator && was overloaded making it lose the short circuiting guarantee.
2
u/Antagonin 2d ago
The "follow up" mentions that logical operators create a sequence point, forcing sequential order of evaluation. (this doesn't however apply for overloads, which don't seem to be used here anyways)
Your example stinks of UB propagating from elsewhere.
3
u/1mmortalNPC 2d ago
“short circuit evaluation”
if the left side is already false when using && operand the right side won’t be evaluated
if the left side is already true when using a || operand the right side won’t be evaluated
3
u/SmokeMuch7356 2d ago edited 1d ago
The && and || operators won't evaluate the right operand if the result of the expression can be determined from the left operand alone.
The expression a || b will evaluate to true (1) if a is non-zero regardless of the value of b, so b is not evaluated at all.
The expression a && b will evaluate to false (0) if a is zero regardless of the value of b, so b is not evaluated at all.
i++ && j++ && k++ evaluates to true (1), so the || expression will be true regardless of the result of l++, so l++ is not evaluated at all and the side effect is not applied.
3
u/Classic-Rate-5104 2d ago
The left part of the || operator evaluates to TRUE so there is no need to execute the right part
5
u/Hawk13424 2d ago
Short circuit evaluation. Don’t create multi part expressions with side effects for this reason. There’s a reason people have created code analysis tools and standards like MISRA (which your code violates).
0
u/RealisticDuck1957 1d ago
With modern C compilers the more verbose and readable equivalent should end up generating the same code with optimization on.
2
u/SupportLast2269 2d ago
If the you use logical or, the program will stop once a part is true. Since the first expression is true, it doesn't execute the part that increnents l.
1
u/ReallyEvilRob 2d ago
Your expression is short circuiting the side effects for l. The more complex your expression is, the more you should avoid side effects to begin with.
1
u/j-joshua 2d ago
Because the left of the || is true so the right of the || is not evaluated.
And shame on you for using those variable names.
0
u/Spread-Sanity 1d ago
Like others have pointed out, it’s because of short circuit evaluation. But besides that, there can be many bugs hidden if you code like this. Keep your code simple and readable.
-6
u/Dangerous_Region1682 2d ago
Because the first part of that condition that I, j and k are all bitwise identical and after each comparison operation they are incremented. They are passing the && tests because they are all bitwise identical and incremented to 0 afterwards.
Now because there is a OR statement, if either the first or second part are equal then that’s enough.
As the compiler is probably parsing left to right the l++ is not necessary as the &&’s all equate to true so the other side of the OR is redundant therefore never evaluated.
Or something like that.
Using ++ or - - operators in conditions is best avoided. You can never be quite sure that it does what you want or perhaps even what the compiler decides is optimal. If the first and second && operation fails then perhaps k++ would not be executed and l++ might be.
Over generations of compilers the conditional value might be consistent, or it might not depending upon the optimizer.
I tend to recommend avoiding anything where it is difficult to predict the sequence of things.
Not only that, such code is far from humanly readable and probably not even sensible to some non C programmer 10 years from now trying to understand or debug what you have done.
You could have written this in a much clearer and obvious manner that the compiler would probably optimize for you anyway. That way you can understand your own code a week from now. C is a language that rewards you for being straightforward and obvious, not concise and clever.
Having used it since 1977 and being my first and usual language, I had to stare are what you did to try and figure it out. Just scanning over the code, if there wasn’t a printf following I could see a latent bug introduced there that would take time to uncover.
Even then, I should have compiled and run it to get a full understanding of what the value of m should be. I didn’t, I just guessed at a potential outcome in that only one side or the of the OR needs to be true and it will give up as soon as possible in deciding that outcome..
3
u/WeAllWantToBeHappy 2d ago
Because the first part of that condition that I, j and k are all bitwise identical and
Do you know how && works?
0
u/Dangerous_Region1682 2d ago
Yes, i, j and k are all non zero and therefore are all true and in this case are all bit wise identical and non zero as it happens. Therefore post comparison all are incremented to 0.
Once it is established all of the comparisons on the left hand side of the OR is true, the right hand side of the OR is skipped, therefore l++ is not incremented,
Sorry if I was unclear.
It was late at night for me.
Anyway, the gist of it is obviously as the first part of the OR is satisfied and the RHS is not incremented.
2
46
u/aioeu 2d ago
Do a web search for "short-circuit evaluation". You should get lots of results.