CSC447

Concepts of Programming Languages

C: Strict versus Nonstrict

Instructor: James Riely

Strict and non-strict operators

  • A strict construct evaluates all of its operands before it runs
  • Name some strict constructs in C
    • Arithmetic operators: +, -, *, /, ...
    • Comparison operators: <, <=, ==, !=, >, >=
    • Bitwise operators: |, &, ~, ^
    • Function calls
    • etc.

Strict and non-strict operators

  • A strict construct evaluates all of its operands before it runs
  • Name some non-strict constructs in C
    • e1 && e2 is strict in e1, but not e2
      p && p->m()
    • e1 || e2 is strict in e1, but not e2
      p==NULL || p->m()
    • e1 ? e2 : e3 is strict in e1, but not e2 or e3
    • Macro expansion

C's Ternary Operator

  • (e1 ? e2 : e3)
  • Evaluate e1; if true evaluate e2, otherwise evaluate e3
  • A conditional expression!

$ gcc statements-01.c && ./a.out 
x=1, y=0, z=111
x=0, y=2, z=222
...
          

int main () {
  for (int i=0; i<10; i++) {
    int x = 0;
    int y = 0;
    int z = (rand()%2) ? (x=1,111) : (y=2,222);
    printf ("x=%d, y=%d, z=%d\n", x, y, z);
  }
}
          

Conditionals

  • Conditional statement vs conditional expression

int fact (int n) {
  if (n <= 1) {
    return 1;
  } else {
    return n * fact (n - 1);
  }
}
          

int fact (int n) {
  return (n <= 1) ? 1 : n * fact (n - 1);
}
          

What happens?

  • Function calls are strict

$ gcc statements-02.c && ./a.out 
x=1, y=2, z=111
x=1, y=2, z=222
...
          

int fcond (int b, int t, int f) { return b ? t : f; }
int main () {
  for (int i=0; i<10; i++) {
    int x = 0;
    int y = 0;
    int z = fcond (rand()%2,  (x=1,111), (y=2,222));
    printf ("x=%d, y=%d, z=%d\n", x, y, z);
  }
}
          

What happens?

  • Macro calls are non-strict

$ gcc statements-03.c && ./a.out 
x=1, y=0, z=111
x=0, y=2, z=222
...
          

#define mcond(b, t, f) (b)?(t):(f)
int main () {
  for (int i=0; i<10; i++) {
    int x = 0;
    int y = 0;
    int z = mcond (rand()%2,  (x=1,111), (y=2,222));
    printf ("x=%d, y=%d, z=%d\n", x, y, z);
  }
}
          

What happens?

  • Macro calls are evaluated in the compiler, by textual substitution

$ gcc statements-04.c && ./a.out 
x=11, rx=33, y=13, ry=36
          

int ftriple (int i) { return i + i + i; }
#define mtriple(i) (i)+(i)+(i)
int main () {
  int x = 10;  
  int rx = ftriple(x=x+1);
  int y = 10;
  int ry = mtriple(y=y+1);
  printf ("x=%d, rx=%d, y=%d, ry=%d\n", x, rx, y, ry);
}