# Chapter 3 Types, Operators, and Expressions; While and For Reference: Brooks, Chapter 3 2 3)

 Page 1/3 Date 07.08.2017 Size 283.95 Kb.
1   2   3

CHAPTER 3

Types, Operators, and Expressions; While and For
Reference: Brooks, Chapter 3 (3.2 - 3.3)

• It is possible to mix data types in arithmetic expressions in C

• The following example is perfectly legal in C

 /* * File : main.c * Author: Robert C. Carden IV */ #include void main(void) { int hours; double speed, distance; printf("Enter the number of hours: "); scanf("%d", &hours); printf("Enter the speed in MPH: ", &speed); scanf("%lf", &speed); distance = hours * speed; printf("You travelled %.2f miles " "at %.2f MPH in %d hours\n", distance, speed, hours); }

• In this example, we are multiplying an integer with a double

• C mandates that all expressions be computed using operand of the same data type

• In this example, C has rules for converting one or both numerical operands to a common type

• Since double is considered to be a higher type than int, the integer operand is converted (promoted) to double and double multiplication is performed

Implicit type conversions
· Performed when an operator has operands of different types

· The operands are converted to a common type

General rules
· In general, the only automatic conversions are those that convert a narrower operand into a wider one without losing information.

· Assignments of a wider type to a narrower type loses information but is allowed.

· A char is treated as a small integer.

· They may be used freely within arithmetic expressions.

· The following example shows how characters and integers are used freely within expressions.

 /* * ctoi: if ch is a numerical character, return * the numerical equivalent * if ch is not, return -1 */ int ctoi(char ch) { int digit = -1; if (ch >= '0' && ch <= '9') { /*compute integer value*/ digit = ch - '0'; } return(digit); }

Implicit type conversions -- example

 /** ** lower: convert c to lower case (ASCII only) ** ** For portability reasons, we pass in ** characters as integers. See later ** discussion on this point. **/ int lower(int c) { if (c >= 'A' && c <= 'Z') /* * to lower case */ return c + 'a' - 'A'; else /* not an uppercase letter */ return c; }

· This function works for ASCII but fails for EBCDIC.

· In ASCII, corresponding upper and lower case letters are a fixed distance apart as numeric values

· Letters are also contiguous

Portable character functions and macros

 example functions semantics of function or macro isdigit(int c) returns 1 <=> c is a digit isupper(int c) returns 1 <=> c is uppercase toupper(int c) converts c to uppercase tolower(int c) converts c to lowercase

· To overcome this common problem, the header is available (and required under ANSI).

· This header defines a family of functions and macros for doing character tests and conversions that are independent of the character set

· Each installation will provide its own to do whatever is necessary

· Now we can rewrite our lower function using these macros:

 #include int lower(int c) { if ( isupper(c) ) return tolower(c) else return c; }

· The C language does not specify whether variables of type char are signed or unsigned quantities.

· The C definition guarantees that any character in a machine's standard character set will never be negative.

· For portability, if you use characters as integers to store non-character data, specify whether it is signed or unsigned.

· When passing or receiving characters to and from functions, people often use int to store characters.

· Problems sometimes arise when users must be able to store an arbitrary character and EOF (-1) in a character variable. In the EBCDIC character set, it already needs all 256 possible values but adding EOF requires 257.

Type conversions -- A.6, p.198, K&R
· The following conversions are performed in an arithmetic expression of mixed types

· If either operand is long double, the other is converted to long double

· Otherwise, if either is double, convert the other to double

· Otherwise, if either is float, convert the other to float

[Integral promotions]

· Otherwise if either is unsigned long int, convert the other to unsigned long int

· Otherwise, if one operand is long int and the other is unsigned int, the effect depends on whether long int can represent all values of an unsigned int; if so, the unsigned int operand is converted to long int; if not, both are converted to unsigned long int

· Otherwise, if one operand is long int, convert the other to long int

· Otherwise, if either operand is unsigned int, convert the other to unsigned int

· Otherwise both operands have type int, i.e. smaller operands are promoted to int.

· Under ANSI, unsigned-ness of operands is not propagated.

Type conversion - the distance program

 void main(void) { int hours; double speed, distance; printf("Enter the number of hours: "); scanf("%d", &hours); printf("Enter the speed in MPH: ", &speed); scanf("%lf", &speed); distance = hours * speed; printf("You travelled %.2f miles " "at %.2f MPH in %d hours\n", distance, speed, hours); }

• The mixed mode expression in question is hours * speed

• Variable hours is of type int

• Variable speed is of type double

• Because they are of different types, an arithmetic type conversion must be applied

1. Neither variable is of type long double so that rule does not apply

2. Variable speed is of type double and since hours is not, hours shall be promoted to double and double (floating point) multiplication shall be performed

• No information is lost here because of the nature of double

• Note from earlier discussions that double has a 53 bit mantissa (on our Intel systems)

• By the same token, int is 32 bits and can easily fit into the integer part of the double

• The result of this expression is a double and the variable it is being assigned to is double

• These are the same so no conversion for the assignment is required

Type conversion - the distance program using floats

 void main(void) { int hours; float speed, distance; printf("Enter the number of hours: "); scanf("%d", &hours); printf("Enter the speed in MPH: ", &speed); scanf("%lf", &speed); distance = hours * speed; printf("You travelled %.2f miles " "at %.2f MPH in %d hours\n", distance, speed, hours); }

• The mixed mode expression in question is hours * speed

• Variable hours is of type int

• Variable speed is of type float

• Because they are of different types, an arithmetic type conversion must be applied

1. Neither variable is of type long double so that rule does not apply

2. Neither variable is of type double so that rule does not apply

3. Variable speed is of type float and since hours is not, hours shall be promoted to float and float (floating point) multiplication shall be performed

• In this example, information might be lost if hours is a fairly large integer

• Note from earlier discussions that float has a 24 bit mantissa (on our Intel systems)

• By the same token, int is 32 bits and may not be able to fit into the integer part of the float

• The excess, low order digits in the floating point number will be truncated

• The result of this expression is a float and the variable it is being assigned to is float

• These are the same so no conversion for the assignment is required

Integer constants revisited - some subtle points
· The type of integer constant is the first of the corresponding list in which its value can be represented (ANSI draft, section 3.1.3.2)

 unsuffixed decimal: int, long, unsigned long unsuffixed octal or hexadecimal: int, long, unsigned long suffixed by letter u or U: unsigned int, unsigned long suffixed by letter l or L: long, unsigned long suffixed by both u or U and l or L: unsigned long

• In LIMITS.H, we see:

#define LONG_MIN (-2147483647L - 1)

#define LONG_MAX 2147483647L

#define ULONG_MAX 0xffffffffUL

• Consider the rather arcane definition of LONG_MIN

• Suppose we had rewritten it as

#define LONG_MIN -2147483648L

• This would not work at all

• First, the compiler would determine the type of the constant 2147483648L

• Because it is suffixed with an L, the compiler may choose the first of long and unsigned long which can represent this integer

• However, this integer requires a full 32 bits and cannot be represented as a long

• Therefore, it must be an unsigned long

• Then, we negate the unsigned long 2147483648

• That will produce another unsigned long and not the minimum signed long number

Mixed mode expressions - evaluating from the bottom up

 #include void main(void) { double result1, result2; result1 = 2.56 + (3 / 2); result2 = 2.56 + (3.0 / 2); printf("result1 = 2.56+(3 /2) = %.2f\n", result1); printf("result2 = 2.56+(3.0/2) = %.2f\n", result2); }

• The example above illustrates a very common pitfall in C

• Many people are aware that integer division truncates and would expect 3/2 to produce a result of 1

• Even more people, though, get confused when they cleverly observe that the result is being assigned to a double and therefore conclude that the entire expression is a double expression. Wrong.

• C evaluates expressions from the bottom up and determines the type and value of each subexpression as it goes, each step of the way

• In the case of computing result1, C first considers 3/2

• Both operands are integers, therefore we do integer division and note that the result is an int

• Notice at this point there is no concern or care about the larger expression

• Notice that C does not care about the context

• Then C considers adding 2.56 to the integer result of computing 3/2

• Now we are adding a double to an int, the int result is promoted to double, and the result is double

• Finally, we have a double which we are assigning to a double

• No conversion is necessary and the C compiler is happy with life

Mixed mode expressions - evaluating from the bottom up

 #include void main(void) { double result1, result2; result1 = 2.56 + (3 / 2); result2 = 2.56 + (3.0 / 2); printf("result1 = 2.56+(3 /2) = %.2f\n", result1); printf("result2 = 2.56+(3.0/2) = %.2f\n", result2); }

Here is the actual result of running this program:

Explicit type conversion -- assignments
· The expression x = y forces y to be converted into the type on the left

· Consider the following C fragment:

 float f; int i; /* converts int to float */ f = i; * truncating fractional part */ i = f;

· Converting float to int causes the truncation of the fractional part

· Longer integers are converted to shorter ones or to chars by dropping the excess high-order bits.

 int i; char c; i = c; /* widen c to fit in i */ c = i; /* value originally in c is unchanged */

[Instructor: give examples on the board of these scenarios]

Explicit type conversions -- coerced

• The cast operator is of the form

( )

• It is a unary operator with the effect that it converts its operand to the desired type.

• The cast operator is an operator equal in precedence to unary minus (-)

 float f; int i, j; f = i / j; /* integer division */ f = (float) i / j; /* real division */ i = f % j; /* syntax error */ i = (int) f % j; /* f is converted to int */

• A cast is an expression: it involves a cast operator and an operand to be cast

• It produces a value

• it does not modify the operand

• In the first example, it converts the int i to float

• The result of this is divided by j using floating point division

• In the second example, it converts the float back to int, truncating the fractional part

• That integer result is then modulo with j

Using casts to get the correct type of expression

 #include void main(void) { result1 = 2.56 + (double) 3 / 2; result2 = 2.56 + (double)(3 / 2); printf("result1 = 2.56+(double)3 /2 = %.2f\n", result1); printf("result2 = 2.56+(double)(3/2) = %.2f\n", result2); }

• It is important to understand why the computation of result1 uses floating point division while the computation of result2 uses integer division

• Once again, all expressions are evaluated from the simplest, innermost expression outward

Increment and decrement operators
· Unary operators ++ and -- increment and decrement their respective operands

· highest precedence (same as unary -)

· right to left associativity

 Operand Equivalent C code y = ++ x; x = x + 1; /*increment*/ y = x; /*use result*/ y = x ++; y = x; /*use value*/ x = x + 1; /*increment*/ y = -- x; x = x - 1; y = x; y = x --; y = x; x = x - 1; z++; z = z + 1;

· When ++ or -- are placed before the operand, they are called pre-increment and pre-decrement respectively.

· the return value of the expression is the new value of the operand

· When ++ or -- are placed after the operand, they are called post-increment and post-decrement respectively

· the return value of the expression is the original value of the operand
· Cannot apply ++ or -- to something cannot be an lvalue

· An lvalue is anything that can be updated, for instance on the left hand side of an assignment statement

· The expression

 (a+b)++

is illegal.

· However, the expression

 x[i]++

is legal and equivalent to
 x[i] = x[i] + 1

ANSI Note (p. 39)
"Between the previous and next sequence point, an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored."
· Thus, if a variable is updated more than once within an expression between two consecutive sequence points, the behavior is undefined.
example

 i = 1; i = ++i + 1;

· What gets assigned to i?

· Two assignments are being requested, but ANSI specifies that i shall have its stored value modified at most once.

· The resulting value of ++i will propagate through the expression, but the store may or may not occur.

· Consequently, the compiler may choose to allow the ++i to actually store into i or let the assignment statement store into i.

· Thus, only one update to i will win and thus i may be either 2 or 3.

Example expressions
· In the table below, assume that the following declarations are in scope.

 int a = 1, b = 2, c = 3, d = 4;

· The following table gives an expression, the equivalent fully parenthesized expression, the value of the expression, and then the subsequent values of a, b, c, and d after its evaluation.

· Assume that these expressions are evaluated independent of each other.

 Expression Equivalent Expr Value a b c d a * b / c (a * b) / c 0 1 2 3 4 a * b % c + a ((a * b) % c) + a 3 1 2 3 4 ++ a * b - c -- ((++a) * b) - (c--) 1 2 2 2 4 7 - - b * ++ d 7 - ((-b) * (++d)) 17 1 2 3 5

Illustration of third row

Assignment operators and expressions
· In C, any expression can be a statement.

· In Pascal, one uses an assignment statement.

· In C, assignments are simply expressions.

· As a result, it is perfectly legal to write the following meaningless code in C.

 3; /* valid */ 4+5; /* valid */

Assignments are expressions
· The return value of an assignment expression is the value that just got assigned

· The object getting updated by the assignment is called an lvalue

· The object (expression) being assigned to it is called an rvalue
example

 x = 5 + 7;

· In this example, x is the lvalue, 5+7 is the rvalue, and the return value of the entire expression is 12.

Example of assignments being expressions
· The following three statements

 b = 2; c = 3; a = b + c;

may be written as one statement

 a = (b = 2) + (c = 3);

example
· Assignment operators associate right to left

· Thus, the following expression

 a = b = c = 0;

is equivalent to writing
 a = (b = (c = 0));

which in turn is equivalent to writing
 c = 0; b = 0; a = 0;

· Note that because assignment statements associate right to left, variables a, b, and c are all lvalues.

· C also provides operators such as += and -= to write expressions such as
 a += 2

 a = a + 2

· In Algol, the equivalent expression would be
 a :=*+ 2

· The following list contains all the possible assignment operators:

 = += -= *= /= %= >>= <<= &= ^= |=

Assignment operators
· The semantics of a general assignment operator is specified by

variable op= expression

being equivalent to

variable = variable op (expression)
· In the table below, assume that the following declarations are in scope.

 int i = 1, j = 2, k = 3, m = 4;

· The following table gives an expression, the equivalent fully parenthesized expression, and the value of the expression.

· Assume that these expressions are evaluated independent of each other.

 Expression Equivalent Expr Equivalent Expr Value j *= k + 3 j *= (k + 3) j = j * (k + 3) 12 i += j + k i += (j + k) i = i + (j + k) 6 j *= k = m + 5 j *= (k=(m+5)) j = j * (k = (m + 5)) 18

example
· The expression

 x *= 5;

is equivalent to writing
 x = x * 5;

· Similarly, the expression

 y = (x1 += 1) * (x2 -= 2);

is roughly equivalent to writing
 x1 = x1 + 1; x2 = x2 - 2; y = x1 * x2;

· Note, however, that because C does not enforce the order of evaluation of the multiplicative operands, the first and second statements may be interchanged
example -- computing the powers of two

 /* some powers of two are printed */ #include int main (void) { int power = 1; printf ("%6d", power *= 2); printf ("%6d", power *= 2); printf ("%6d", power *= 2); printf ("%6d", power *= 2); printf ("%6d", power *= 2); printf ("%6d", power *= 2); printf ("%6d\n", power *= 2); }

The output of this program is
 2 4 8 16 32 64 128

Conditional expressions