简体   繁体   中英

Evaluation of arithmetic array subscript expression as part of operand to the conditional-operator in C

Hi guys why this program return 2 not 3

When we do this arithmetic 'a' <= (s[++i]) && (s[++i]) <= 'z'?(s[++i]) - 'a' + 'A': (s[++i])

First we test s[++i] = 1 , second going to be tested s[++i] should be 2 and this test fails and last one happen s[++i] == 3 , so why compiler return = 2?

Thank you for answering.

int main()
{
  int i=0; 
  char s[10];

  strcpy(s, "0123");
  putchar(('a' <= (s[++i]) && (s[++i]) <= 'z' ?(s[++i]) - 'a' + 'A': (s[++i])));

  printf("\n");

  return 0;
}

The right hand side of && , which is && (s[++i]) <= 'z' is not evaluated because the left hand side is already false ( 'a' <= (s[++i]) resolves to 'a' <= '1' , which is false).

Thus, only the left hand side of && , as well as the right hand side of : are evaluated.

Initial state:

s = "0123"
i = 0

The execution of the expression in steps:

'a' <= (s[++i]) && (s[++i]) <= 'z' ? (s[++i]) - 'a' + 'A' : (s[++i])
// execute ++i -> i = 1
'a' <= s[i] && (s[++i]) <= 'z' ? (s[++i]) - 'a' + 'A' : (s[++i])
'a' <= s[1] && (s[++i]) <= 'z' ? (s[++i]) - 'a' + 'A' : (s[++i])
'a' <=  '1' && (s[++i]) <= 'z' ? (s[++i]) - 'a' + 'A' : (s[++i])
// we look at http://www.asciitable.com/ Note1
 97 <=   49 && (s[++i]) <= 'z' ? (s[++i]) - 'a' + 'A' : (s[++i])
     0      && (s[++i]) <= 'z' ? (s[++i]) - 'a' + 'A' : (s[++i]))
// because of short-circuiting in && operator we do not evaluate right side of &&
// Note `a && b ? c : d` is equal to `(a && b) ? c : d`
// Note `&&` introduces a sequence point
              0                ? (s[++i]) - 'a' + 'A' : (s[++i])
// Note `?` introduces a sequence point
(s[++i])
s[++i]
// execute ++i -> i = 2
s[2]
'2'

Note 1: C standard doesn't say systems use ascii encoding, but majority of platforms used today use ascii.

First we test s[++i] = 1, second going to be tested s[++i] should be 2

No, because && is short-circuit evaluated . If the left side of && is false then the right side of && will not be executed.

First let's split on ternary operators like this

'a' <= (s[++i]) && (s[++i]) <= 'z' ?
  (s[++i]) - 'a' + 'A':
  (s[++i])

and evaluate the first condition 'a' <= (s[++i]) && (s[++i]) <= 'z' well 'a' <= ([s++i]) returns false because 'a' which is 97 is bigger than s[++i] i was 0 so ++i returns 1 and s[1] is '1' wich is 49 so 97 <= 49 is false and because of that the && stops there and returns 0 (false) and that makes the ternary operators statement executes the else condition which then returns s[++i] remember that i has been incremented once when the first condition was evaluated that means s[++i] is s[2] which is '2' and that's the value that should be returned

Let's analyze this statement:

putchar(('a' <= (s[++i]) && (s[++i]) <= 'z' ?(s[++i]) - 'a' + 'A': (s[++i])));

We can first remove redundant parentheses:

putchar('a' <= s[++i] && s[++i] <= 'z' ? s[++i] - 'a' + 'A' : s[++i]);

The argument expression is a ternary expression. Let's transform this into an equivalent if / else statement:

if ('a' <= s[++i] && s[++i] <= 'z')
    putchar(s[++i] - 'a' + 'A');
else
    putchar(s[++i]);

The left part of the && operator is evaluated: 'a' is compared to s[1] and i is incremented. In the ASCII character set, 'a' is larger than '1' so this comparison evaluates to 0 and i is set to 1 .

The right part of && is not evaluated because the left part is false.

The else branch of the if statement is evaluated: s[2] is passed to putchar() and i is incremented and set to 2 .

Hence the program outputs 2 and i is set to 2 .

Note that the different parts of the expression are separated by sequence points at && , ? and : and there is a single ++ operator incrementing i in each part, so the expression does not have undefined behavior as it might seem at first glance.

The code shows what would happen with macros that evaluate their argument more than once. For example one could define islower and toupper as macros:

// These macros are incorrect and have multiple evaluation of their argument
// do not use these, they are only used to illustrate the problems
#define islower(c)  'a' <= (c) && (c) <= 'z'
#define toupper(c)  (islower(c) ?(c) - 'a' + 'A': (c))

With these definitions, putchar(toupper(s[++i])); would expand as:

putchar(('a' <= (s[++i]) && (s[++i]) <= 'z' ?(s[++i]) - 'a' + 'A': (s[++i])));

which has multiple side effects and does not correctly test s[1] nor output s[1] .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM