Introduction
•
Expressions are the fundamental means of specifying computations in a programming language
•
To understand expression evaluation, need to be familiar with the orders of operator and operand evaluation
•
Essence of imperative languages is dominant role of assignment statements
•
The purpose of an assignment statement is to change the value of a variable.
Arithmetic Expressions
•
Arithmetic evaluation was one of the motivations for the development of the first programming languages
•
Arithmetic expressions consist of operators, operands, parentheses, and function calls
Arithmetic Expressions: Design Issues
•
Design issues for arithmetic expressions
– operator precedence rules
– operator associativity rules
– order of operand evaluation
– operand evaluation side effects
– operator overloading
– mode mixing expressions
Arithmetic Expressions: Operators
•
A unary operator has one operand
•
A Unary addition is called the identity operator, has no affect of its operand. In Java, unary plus or minus actually does have an effect when its operand is short or byte , it causes an implicit conversion of that operand to int type.
•
Unary minus operator can appear at the beginning or anywhere, as long as it is parenthesized.
Ex: A+(-B) *C
•
A binary operator has two operands. (Infix in most languages, prefix in some operators of Perl).
•
A ternary operator has three operands ? : , included in the C-based languages.
Arithmetic Expressions: Operator Precedence Rules
•
The operator precedence rules for expression evaluation define the order in which “adjacent” operators of different precedence levels are evaluated
•
Typical precedence levels
– parentheses
–
unary operators
–
** (if the language supports it)
–
*, /
–
+, -
•
The precedence of the arithmetic operators of a few common programming languages are as follows: (sorted from the highest to the lowest)
Fortran
**
C-based
Postfix ++, --
Ada
**, abs
*, /
All +, -
Prefix ++, --, unary +, - *, /, mod, rem
*, /, % Unary +, -
Binary +, -
Binary +, - Binary +, -
•
The (**) operator is exponentiation. The (%) operator of C is exactly like the (rem) of Ada, it takes two integer operands and yield the reminder of 1 st
divided by 2 nd
.
•
The Ada (mod) operator is identical to (rem) when both operands are positive, but can be different when one or both are negative.
•
The (abs) od Ada is a unary operator that yields the absolute value of its operand.
1
Arithmetic Expressions: Operator Associativity Rule
•
The operator associativity rules for expression evaluation define the order in which adjacent operators with the same precedence level are evaluated
•
Typical associativity rules
–
Left to right, except **, which is right to left
–
Sometimes unary operators associate right to left (e.g., in FORTRAN) A**B**C. The right operator is evaluated 1 st .
–
In Ada, exponentiation is non associative, which means that the expression A**B**C is illegal.
The expression must be parenthesized to show the desired order. (A**B)**C or A**(B**C)
–
In VB the exponentiation (^) is left associative.
–
Fortran unary and binary minus operators have the same precedence, but in Ada and most other common languages, unary minus have precedence over binary minus.
-A-B
(-A) –B
Consider the following –A/B , -A*B The relative precedence of the unary minus and binary operators are irrelevant. i.e. the order of evalustion of the two operators has no effect on the value of the expression.
But, -A**B, has an effect. Fortran, VB and Ada are the oly languages exponentiation operator.In these languages the exponentiation has higher precedence over unary minus. So, -A**B
-
(A**B)
If the unary operator appears at positions other than at the left of the expression, it must be parenthesized, in order to give it highest precedence.
There is one situation where the precedence of a unary operator can be confusing. N Ada, the precedence of unary minus is lower than that of (mod),so expression -17 mod 5 is equivalent to
–(17 mod 5) which evaluated to -2, rather than 3, which would be the result if unary minus had higher precedence than (mod), as it does in C-based languages. (A mod B= (A+k*B) mod B)
–
Associativity rules for some imperative languages
Language Associativity rules
Fortran Left: *, /, +, -
Right: **
C-based
Ada
Left: *, /, %, binary +, binary –
Right: ++, --, unary -, unary +
Left: all except **
Nonassociative **
•
APL is different; all operators have equal precedence and all operators associate right to left
A*B+C if A=3, B=4, C=5, then value =27
•
Precedence and associativity rules can be overriden with parentheses. Ex: (A+B)*C
Arithmetic Expressions: Conditional Expressions
•
Conditional Expressions (?:) ternary operator
–
C-based languages (e.g., C, C++) expression_1? expression_2: expression_3
–
An example: average = (count == 0)? 0 : sum / count
–
Evaluates as if written like if (count == 0) average = 0 else average = sum /count
Arithmetic Expressions: Operand Evaluation Order
•
Operand evaluation order
1.
Variables: fetch the value from memory
2.
Constants: sometimes a fetch from memory; sometimes the constant is in the machine language instruction and not require a memory fetch.
2
3.
If an operand is parenthesized expression, then all operators it contains must be evaluated before its value can be used as an operand.
Arithmetic Expressions: Potentials for Side Effects
•
Functional side effects: occurs when a function changes a two-way parameter or a non-local variable (non-local is a global variable declared outside the function but is accessible in the function).
•
Consider the expression a+fun(a) if fun does not have the side effect of changing a , then the order of evaluation of the two operands, a and fun(a) , has no effect on the value of the expression.
•
If fun changes a , there I an effect.
•
Ex: suppose fun returns the value of its argument divided by 2, and changes the value of its parameter to 20.
Suppose we have the following:
a=10;
b=a+fun(a); if the value of a is fetched first (in the expression evaluation process), its value is 10 and the value of the expression is 15. But if the 2 nd
operand is evaluated 1 st
, then the value of the first operand is
20 (because it is changed by the function), and the value of the expression is 25.
Consider the following C program which illustrates the same problem when a function changes a global variable that appears in expression. int a
5; //global int fun1( )
{ a
17; return 3; } // end of fun1 void fun2( )
{ a
a
fun1( ); } // end of fun2 void main( )
The value computed for the value of a a in fun2 depends on the order of evaluation of the operands in the expression a+fun1( )
will be either 8 or 20.
.
{ fun2( ); } // end of main
Functional Side Effects
•
Two possible solutions to the problem of operand evaluation order.
1.
Write the language definition to disallow functional side effects
•
No two-way parameters in functions
•
No non-local references in functions
•
Advantage: it works!
•
Disadvantage: inflexibility of two-way parameters and non-local references
•
Consider the case of C and C++, which have only functions.
•
To eliminate the side effects of two-way parameters and still provide subprograms that return more than one value, a new subprogram type that is similar to the procedures of the other imperative languages would be required.
•
When efficiency is important, using access to global variables to avoid parameter passing is an important method of increasing execution speed. Ex: In compilers, access to data such as the symbol table is commonplace.
2.
Write the language definition to demand that operand evaluation order be fixed
•
Disadvantage : limits some compiler optimizations
•
In Java language definition guarantees that operands appear to be evaluated in leftto-right order, eliminating the problem of side effects.
Overloaded Operators
•
Use of an operator for more than one purpose is called operator overloading
•
Some are common (e.g., + for int and float Java use it for string catenation. It is accessible, as long as readability and reliability are not suffer)).
3
•
Some are potential trouble (e.g., * in C and C++, & if it is binary operator, specifies a bitwise logical AND operation. As a unary operator with a variable as its operand, means address of that variable (address-of-operator)).
–
Loss of compiler error detection (omission of an operand should be a detectable error).
Ex: simple keying error of leaving out 1 st
operand for a bitwise AND operation can go undetected by the compiler, because it is interpreted as an address-of operator.
–
Some loss of readability (same symbol for two completely unrelated operations).
– Can be avoided by introduction of new symbols (e.g., Pascal’s div for integer division)
•
C++, Ada, Fortran95, and C# allow user-defined overloaded operators
•
Potential problems:
–
Users can define nonsense operations
–
Readability may suffer, even when the operators make sense
Type Conversions
•
A narrowing conversion converts a value to a type that cannot store even approximation of all of the values of the original type.
Ex: In Java convert a double to float : the range of double is much larger than that of float
Float to int
•
A widening conversion converts a value to a type that can include at least approximation of all f the values of the original type.
Ex: converting an int to a float in Java.
•
Widening conversions are nearly always safe, whereas narrowing conversions are not.
•
Type conversions can be either explicit or implicit.
Type Conversions: Mixed Mode
•
A mixed-mode expression is one that has operands of different types.
•
Languages that allow such expressions, much define conventions for implicit operand type conversions because computers usually do not have binary operations that take operands of different types.
•
A coercion is an implicit type conversion initiated by the compiler.
•
Disadvantage of coercions:
–
They decrease in the type error detection ability of the compiler
Consider the following Java code:
i nt a;
float b, c, d;
…
d=b*a;
Suppose that the second operand of (*) meant to be (c), but wrongly typed (a). because
Java allows mixed-mode, compiler would not detect as an error. Simply insert code to coerce int operand (a) to float . If the previous case was in Ada, it would be error.
•
In most languages, all numeric types are coerced in expressions, using widening conversions
Consider the following Java code byte a, b, c;
…
A=b+c;
The values of (b) and (c) are coerced to int and an int addition is performed, then the sum is converted to byte and put in (a).
•
In Ada, there are virtually no coercions in expressions
Explicit Type Conversions
•
Explicit Type Conversions could be widening or narrowing warning message could be issued when explicit narrowing conversion results in significant change to the value of the object being converted.
•
Called casting in C-based language
•
Examples
4
– C: (int) angle
– Ada: Float (sum)
Note that Ada’s syntax is similar to function calls
Type Conversions: Errors in Expressions
•
Causes due to
–
Inherent limitations of arithmetic e.g., division by zero is disallowed
–
Limitations of computer arithmetic e.g. overflow or underflow, where the result of an operation cannot be represented in the memory cell where it must be stored (either too large or too small).
•
Often ignored by the run-time system because it consider exceptions and programs can detect and deal with it.
Relational and Boolean Expressions
•
Relational Expressions
–
Use relational operators and operands of various types (simple like integer, or complex like character string ) ( numeric, string, ordinal types)
–
Evaluate to some Boolean representation
–
Operator symbols used vary somewhat among languages (!=, /=, .NE., <>, #)
–
The relational operators always have lower precedence than arithmetic operators.
Ex: a+1>2*b the arithmetic expressions are evaluated 1 st
.
Relational and Boolean Expressions
•
Boolean Expressions consist of Boolean variables, Boolean constants, relational expressions, and Boolean operators.
–
Operands are Boolean and the result is Boolean
–
Example operators: logical operators
FORTRAN 77 FORTRAN 90 C Ada
.AND. and && and
.OR. or || or
.NOT. not ! not
xor (exclusive OR)
Ada: AND and OR operators have equal precedence.
C-based: AND has higher precedence than OR.
Relational and Boolean Expressions: No Boolean Type in C
•
C has no Boolean type--it uses int type with 0 for false and nonzero for true
• One odd characteristic of C’s expressions: a < b < c is a legal expression, but the result is not what you might expect:
–
Left most operator is evaluated, producing 0 or 1
–
The evaluation result is then compared with the third operand (i.e., c). there is never a comparison between b and c in this expression.
Relational and Boolean Expressions: Operator Precedence
•
Precedence of C-based operators (arithmetic, relational, Boolean).
Highest prefix ++, --
unary +, -, prefix ++, --, !
*,/,%
binary +, -
<, >, <=, >=
=, !=
&&
Lowest ||
Short Circuit Evaluation
5
•
An expression in which the result is determined without evaluating all of the operands and/or operators
•
Example: (13*a) * (b/13–1)
If a is zero, there is no need to evaluate (b/13-1). It is difficult to detected during execution
The value of the Boolean expression
(a>=0) && (b<10) if the 1 st
relational expression is false, no need to evaluate the 2 nd
.easily discovered during execution.
•
Problem with non-short-circuit evaluation index = 0; while (index < length) && (LIST[index] != value)
index++;
Both relational expressions are evaluated.
–
When index=length, LIST [index] will cause an indexing problem (assuming LIST has length -1 elements)
•
C, C++, and Java: use short-circuit evaluation for the usual Boolean operators ( && and || ), but also provide bitwise Boolean operators that are not short circuit ( & and | )
•
Ada: programmer can specify either (short-circuit is specified with and then and or else)
•
Ex: assume List is declared 1..Listlen, the Ada code
Index :
1; while (Index
Listlen) and then (List(Inde x)/
Key) loop
Index :
Index
1; end loop;
Will not cause an error when Key is not in List and Index becomes larger than Listlen .
•
Short-circuit evaluation exposes the potential problem of side effects in expressions.
•
Suppose short-circuit evaluation is used on an expression and part of the expression that contains a side effect is not evaluated; then the side effect only occur in complete evaluation of the whole expression. If program correctness depends on the side effect, short-circuit evaluation can result in a serious error. e.g. (a > b) || ((b++) / 3)
•
In this expression, b is changed only when a<=b . if the programmer assumed b would be changed every time this expression is evaluated during execution, and the program’s correctness depends on it, the program will fail.
Assignment Statements
•
Assignment statements provides the mechanism by which the user can dynamically change the bindings of values to variables.
•
The general syntax
<target_var> <assign_operator> <expression>
The assignment operator
= FORTRAN, BASIC, PL/I, C, C++, Java
:= ALGOLs, Pascal, Ada
= can be bad when it is overloaded for the relational operator for equality
Instead of typing if(x==y)….we typed if (x=y)…
This si a loss of error detection in the C design of the assignment operation that leads to program errors.
Example of safety deficiencies of C and C++ programs.
Assignment Statements: Conditional Targets
•
Conditional targets (C, C++, and Java)
(flag)? total : subtotal = 0
Which is equivalent to if (flag) total = 0
6
else subtotal = 0
Assignment Statements: Compound Operators
Compound Assignment operator
•
A shorthand method of specifying a commonly needed form of assignment
•
Introduced in ALGOL; adopted by C
•
Example a = a + b is written as a += b
Assignment Statements: Unary Assignment Operators
•
Unary assignment operators in C-based languages combine increment and decrement operations with assignment
•
Examples sum = ++count (count incremented, then assigned to sum) count=count+1;
sum=count; sum = count++ ( Assignment of the value of count to some occurs 1 st
, then count is incremented, added to sum) sum=count;
count=count+1;
Using of unary increment operator to form a complete assignment statement. count++ (count incremented )
When two Unary operators apply to the same operands, the association is right
-count++ (count incremented then negated ) equivalent to –(count++)
Assignment as an Expression
•
In C, C++, and Java, the assignment statement produces a result and can be used as operands.
•
This design treats the assignment operator much like any other binary operators, except that it has te side effect of changing its left operand.
•
An example: in C
while ((ch = getchar())!= EOF){…}
= has lower precedence of relational operator, this why we put ch=getchar() in parenthesis.
ch = getchar() is carried out; the result (assigned to ch ) is used as a conditional value for the while statement.
Disadvantages of allowing assignment statement to be operands in expressions, it provides another kind of expression side effect. Makes expressions difficult to read and understand.
Mixed-Mode Assignment
•
Assignment statements can also be mixed-mode, for example int a, b; float c; c = a / b;
•
The coercion takes place only after the right side expression has been evaluated. One alternative will be to coerce all operands in the right side to the type of the target before evaluation as in the above example.
•
In Pascal, integer variables can be assigned to real variables, but real variables cannot be assigned to integers
•
In Java and C#, only widening assignment coercions are done. i.e. int value can be assigned to float variable, but not vice versa. This increases the reliability.
•
In Ada, there is no assignment coercion
7