Why doesn't this version of logical AND in C show short circuit behavior?

Yes, this is a matter of homework, but I did my research and a lot of thought on this topic and I can not understand it. This question says that this piece of code does NOT show short circuit behavior and asks why. But it seems to me that he is showing a short circuit, so can someone explain why this is not so?

In C:

int sc_and(int a, int b) { return a ? b : 0; } 

It seems to me that in the case when a is false, the program will not try to evaluate b , but I must be wrong. Why does the program even touch b in this case, when it is not needed?

+79
c short-circuiting
Oct 13 '14 at 15:17
source share
6 answers

This is a trick. b is the input argument to sc_and , so it will always be evaluated. In other words, sc_and(a(), b()) will call a() and call b() (order is not guaranteed), then call sc_and with the results a(), b() , which goes to a?b:0 . This has nothing to do with the ternary operator itself, which would be absolutely short.

UPDATE

As for why I called this “trick”: this is due to the lack of a clearly defined context for where to consider the “short circuit” (at least as the OP reproduced). Many people, when asking only definitions of a function, assume that the context of the question asks a question about the body of the function ; they often do not see function as an expression in itself. This is the trick of the question; Let me remind you that in programming in general, but especially in languages ​​like C-like, which often have many exceptions to the rules, you cannot do this. For example, if a question was asked as such:

Consider the following code. Will sc_i show short circuit behavior when called from main:

 int sc_and(int a, int b){ return a?b:0; } int a(){ cout<<"called a!"<<endl; return 0; } int b(){ cout<<"called b!"<<endl; return 1; } int main(char* argc, char** argv){ int x = sc_and(a(), b()); return 0; } 

It would be immediately clear that you should think of sc_and as your own in your own domain-specific language , and evaluate whether the sc_and call is short-circuited, like normal && . I would not think that this is generally a trick, because it is clear that you should not focus on the ternary operator and instead focus on the mechanics of C / C ++ call functions (and, I think, fit into the next question to write sc_and , which performs a short circuit in which #define will be used, not a function).

Regardless of whether you call the ternary operator to perform a short circuit (or something else, such as a “conditional assessment”), it depends on your definition of a short circuit, and you can read various comments to think about it. In my opinion, this is true, but it is not very important for the real question or why I called it a "trick".

+117
Oct 13 '14 at 15:23
source share

When statement

 bool x = a && b++; // a and b are of int type 

executed, b++ will not be evaluated if the operand a evaluates to false (short circuit behavior). This means that there will be no side effect on b .

Now let's look at the function:

 bool and_fun(int a, int b) { return a && b; } 

and name it

 bool x = and_fun(a, b++); 

In this case, if a is true or false , b++ will always be evaluated during the function call, and a side effect on b will always occur.

The same is true for

 int x = a ? b : 0; // Short circuit behavior 

and

 int sc_and (int a, int b) // No short circuit behavior. { return a ? b : 0; } 
+42
Oct 13 '14 at 15:49
source share

As others have already pointed out, regardless of what is passed into the function as two arguments, it receives an estimate as it is passed. This is the way to operation tenary.

On the other hand, it is

 #define sc_and(a, b) \ ((a) ?(b) :0) 

there will be a "short circuit" since this macro does not imply a function call, nor does it evaluate the argument (s) of the function.

+19
Oct. 13 '14 at 16:54
source share

Edited to fix bugs noted in @cmasters comment.




AT

 int sc_and(int a, int b) { return a ? b : 0; } 

... the return ed return shows the evaluation of the short circuit, but the function call is not executed.

Try to call

 sc_and (0, 1 / 0); 

A function call evaluates 1 / 0 , although it is never used, so it probably causes a division by zero error.

Relevant excerpts from the (draft) ANSI C standard:

2.1.2.3 Program Execution

...

In an abstract machine, all expressions are evaluated according to semantics. An actual implementation should not evaluate a part of an expression if it can infer that its value is not used and that it doesn’t need side effects (including a function call or access to a volatile object caused by a call).

and

3.3.2.2 Functional Calls

....

Semantics

...

In preparation for the function call, the arguments are evaluated, and each parameter is assigned the value of the corresponding argument.

I assume that each argument is evaluated as an expression, but the list of arguments as a whole is not an expression, so behavior other than SCE is mandatory.

As a mudguard on the deep water surface of standard C, I would appreciate a proper informed presentation on two aspects:

  • Does 1/0 rate undefined behavior?
  • Is the argument list an expression? (I think not)



PS

Even you switch to C ++ and define sc_and as an inline function, you will not get SCE. If you define it as a C macro, as @alk does, you will.

+5
Oct. 15 '14 at 8:01
source share

To clearly see the triple short circuit, try changing the code a bit to use function pointers instead of integers:

 int a() { printf("I'm a() returning 0\n"); return 0; } int b() { printf("And I'm b() returning 1 (not that it matters)\n"); return 1; } int sc_and(int (*a)(), int (*b)()) { a() ? b() : 0; } int main() { sc_and(a, b); return 0; } 

And then compile it (even with optimization almost unchanged: -O0 !). You will see that b() not executed if a() returns false.

 % gcc -O0 tershort.c % ./a.out I'm a() returning 0 % 

Here the generated assembly looks like this:

  call *%rdx <-- call a() testl %eax, %eax <-- test result je .L8 <-- skip if 0 (false) movq -16(%rbp), %rdx movl $0, %eax call *%rdx <- calls b() only if not skipped .L8: 

Since others correctly pointed out that the question is to focus on triple operator behavior that has a short circuit (let's call it “conditional evaluation”) instead of evaluating parameters when calling (calling by value), which is NOT a short circuit.

+4
Oct. 15 '14 at 7:39
source share

The terminal operator C can never be short-circuited, since it evaluates only one expression a (condition) to determine the value given by expressions b and c if any value can be returned.

The following code:

 int ret = a ? b : c; // Here, b and c are expressions that return a value. 

This is almost equivalent to the following code:

 int ret; if(a) {ret = b} else {ret = c} 

The expression a can be formed by other operators, such as && or || which may be a short circuit, because they can evaluate two expressions before returning the value, but this will not be considered a triple operator performing a short circuit, but the operators used in the condition, as in a regular if.

Update:

There is some discussion that the triple operator is a short circuit operator. The argument says that any operator that does not evaluate all its operands makes a short circuit according to @aruisdante in the comment below. If this definition is given, then the ternary operator will be shorted, and in this case I agree with this initial definition. The problem is that the term “short circuit” was originally used for a certain type of operator that allowed this behavior, and these are logical / logical operators, and the reason that this is only what I will try to explain.

Following the article "Detection of short circuits," the evaluation of a short circuit applies only to logical operators embedded in the language, such that knowing that the first operand will make the second inappropriate, that is, for &&, the operator is the first operand is false , and for || the operator is the first operand true ; the C11 specification also notes this in 6.5.13 the logical AND operator and 6.5.14 the logical OR operator.

This means that to determine a short circuit, you must define it in an operator that must evaluate all operands in the same way as logical operators, if the first operand does not make the second unnecessary. This corresponds to what is written in another definition of a short circuit in MathWorks in the Logical Short Circuit section, since the short circuit comes from logical operators.

As I tried to explain the C-ternary operator, also called the triple, if it evaluates only two of the operands, it evaluates the first, and then evaluates the second, or one of the two remaining, depending on the value of the first. He always does this, and should not evaluate all three in any situation, so there is no “short circuit” in any case.

As always, if you see that something is wrong, write a comment with an argument against it, and not just with downvote, which just makes SO worse, and I believe that we can be a much better community, just answers with top-down answers do not agree.

0
Oct 14 '14 at 10:51
source share



All Articles