4. Data Types and Expressions

In this chapter, we will take a look at the basic data types and describe some fundamental rules for forming arithmetic expressions in Objective-C.

Data Types and Constants

You have already been exposed to the Objective-C basic data type int. As you will recall, a variable declared to be of type int can be used to contain integral values only—that is, values that do not contain decimal places.

The Objective-C programming language provides three other basic data types: float, double, and char. A variable declared to be of type float can be used for storing floating-point numbers (values containing decimal places). The double type is the same as type float, only with roughly twice the accuracy. Finally, the char data type can be used to store a single character, such as the letter a, the digit character 6, or a semicolon (more on this later).

In Objective-C, any number, single character, or character string is known as a constant. For example, the number 58 represents a constant integer value. The character string "Programming in Objective-C is fun. " is an example of a constant character string. Expressions consisting entirely of constant values are called constant expressions. So, the expression

128 + 7 - 17

is a constant expression because each of the terms of the expression is a constant value. But if i were declared to be an integer variable, the expression

128 + 7 – i

would not represent a constant expression.

Type int

In Objective-C, an integer constant consists of a sequence of one or more digits. A minus sign preceding the sequence indicates that the value is negative. The values 158, –10, and 0 are all valid examples of integer constants. No embedded spaces are permitted between the digits, and values larger than 999 cannot be expressed using commas. (So, the value 12,000 is not a valid integer constant and must be written as 12000.)

Two special formats in Objective-C enable integer constants to be expressed in a base other than decimal (base 10). If the first digit of the integer value is a 0, the integer is taken as expressed in octal notation—that is in base 8. In that case, the remaining digits of the value must be valid base-8 digits and therefore must be 0–7. So, to express the value 50 in base-8 in Objective-C, which is equivalent to the value 40 in decimal, the notation 050 is used. Similarly, the octal constant 0177 represents the decimal value 127 (1 × 64 + 7 × 8 + 7). An integer value can be displayed at the terminal in octal notation by using the format characters %o in the format string of a printf statement. In such a case, the value is displayed in octal without a leading zero. The format character %#o does cause a leading zero to be displayed before an octal value.

If an integer constant is preceded by a zero and the letter x (either lowercase or uppercase), the value is taken as being expressed in hexadecimal (base-16) notation. Immediately following the letter x are the digits of the hexadecimal value, which can be composed of the digits 0–9 and the letters a–f (or A–F). The letters represent the values 10–15, respectively. So, to assign the hexadecimal value FFEF0D to an integer variable called rgbColor, the statement

rgbColor = 0xFFEF0D;

can be used. The format characters %x display a value in hexadecimal format without the leading 0x and with using lowercase letters a–f for hexidecimal digits. To display the value with the leading 0x, you use the format characters %#x, as in the following:

printf ("Color is %#x ", rgbColor);

An uppercase X, as in %X or %#X, can be used to display the leading x and hexidecimal digits that follow using uppercase letters.

Storage Sizes and Ranges

Every value, whether it's a character, an integer, or a floating-point number, has a range of values associated with it. This range has to do with the amount of storage allocated to store a particular type of data. In general, that amount is not defined in the language; it typically depends on the computer you're running on and is therefore called implementation or machine dependent. For example, an integer can take up 32 bits on your computer, or perhaps it might be stored in 64. You should never write programs that make any assumptions about the size of your data types. You are, however, guaranteed that a minimum amount of storage will be set aside for each basic data type. For example, it's guaranteed that an integer value will be stored in a minimum of 32 bits of storage, which is the size of a word on many computers. However, once again, it's not guaranteed. See Table B.2 in Appendix B, “Objective-C Language Summary,” for more information about data type sizes.

Type float

A variable declared to be of type float can be used for storing values containing decimal places. A floating-point constant is distinguished by the presence of a decimal point. You can omit digits before the decimal point or digits after the decimal point, but obviously you can't omit both. The values 3., 125.8, and -.0001 are all valid examples of floating-point constants. To display a floating-point value at the terminal, the printf conversion characters %f are used.

Floating-point constants can also be expressed in so-called scientific notation. The value 1.7e4 is a floating-point value expressed in this notation and represents the value 1.7 × 10-4. The value before the letter e is known as the mantissa, whereas the value that follows is called the exponent. This exponent, which can be preceded by an optional plus or minus sign, represents the power of 10 by which the mantissa is to be multiplied. So, in the constant 2.25e-3, the 2.25 is the value of the mantissa and -3 is the value of the exponent. This constant represents the value 2.25 × 10-3, or 0.00225. Incidentally, the letter e, which separates the mantissa from the exponent, can be written in either lowercase or uppercase.

To display a value in scientific notation, the format characters %e should be specified in the printf format string. The printf format characters %g can be used to let printf decide whether to display the floating-point value in normal floating-point notation or in scientific notation. This decision will be based on the value of the exponent: If it's less than –4 or greater than 5, %e (scientific notation) format is used; otherwise, %f format is used.

A hexadecimal floating constant consists of a leading 0x or 0X, followed by one or more decimal or hexadecimal digits, followed by a p or P, followed by an optionally signed binary exponent. For example, 0x0.3p10 represents the value 3/16 × 210 = 0.5.

Type double

The type double is very similar to type float, but it is used whenever the range provided by a float variable is not sufficient. Variables declared to be of type double can store roughly twice as many significant digits as can a variable of type float. Most computers represent double values using 64 bits.

Unless told otherwise, all floating-point constants are taken as double values by the Objective-C compiler. To explicitly express a float constant, append either an f or F to the end of the number, like so:

12.5f

To display a double value, the format characters %f, %e, or %g, which are the same format characters used to display a float value, can be used.

Type char

A char variable can be used to store a single character1. A character constant is formed by enclosing the character within a pair of single quotation marks. So 'a', ';', and '0' are all valid examples of character constants. The first constant represents the letter a, the second is a semicolon, and the third is the character zero—which is not the same as the number zero. Do not confuse a character constant, which is a single character enclosed in single quotes, with a character string, which is any number of characters enclosed in double quotes.

The character constant ' '—the newline character—is a valid character constant even though it seems to contradict the rule cited previously. The reason for this is that the backslash character is a special character in the Objective-C system and does not actually count as a character. In other words, the Objective-C compiler treats the character ' ' as a single character, even though it is actually formed by two characters. Other special characters are initiated with the backslash character. See Appendix B for a complete list. The format characters %c can be used in a printf call to display the value of a char variable at the terminal.

In Program 4.1, the basic Objective-C data types are used.

Program 4.1.


#import <stdio.h>

int main (int argc, char *argv[])
{
   int   integerVar = 100;
   float  floatingVar = 331.79;
   double doubleVar = 8.44e+11;
   char  charVar = 'W';

   printf ("integerVar = %i ", integerVar);
   printf ("floatingVar = %f ", floatingVar);
   printf ("doubleVar = %e ", doubleVar);
   printf ("doubleVar = %g ", doubleVar);
   printf ("charVar = %c ", charVar);

   return 0;
}


Program 4.1. Output


integerVar = 100
floatingVar = 331.790009
doubleVar = 8.440000e+11
doubleVar = 8.44e+11
charVar = W


In the second line of the program's output, you will notice that the value of 331.79, which is assigned to floatingVar, is actually displayed as 331.790009. In fact, the actual value displayed is dependent on the particular computer system you are using. The reason for this inaccuracy is the particular way in which numbers are internally represented inside the computer. You have probably come across the same type of inaccuracy when dealing with numbers on your pocket calculator. If you divide 1 by 3 on your calculator, you get the result .33333333, with perhaps some additional 3s tacked on at the end. The string of 3s is the calculator's approximation to one third. Theoretically, there should be an infinite number of 3s. But the calculator can hold only so many digits, thus the inherent inaccuracy of the machine. The same type of inaccuracy applies here: Certain floating-point values cannot be exactly represented inside the computer's memory.

Qualifiers: long, long long, short, unsigned, and signed

If the qualifier long is placed directly before the int declaration, the declared integer variable is of extended range on some computer systems. An example of a long int declaration might be

long int factorial;

This declares the variable factorial to be a long integer variable. As with floats and doubles, the particular accuracy of a long variable depends on your particular computer system. On many systems, an int and a long int both have the same range and either can be used to store integer values up to 32-bits wide (231 – 1, or 2,147,483,647).

A constant value of type long int is formed by optionally appending the letter L (upper- or lowercase) onto the end of an integer constant. No spaces are permitted between the number and the L. So, the declaration

long int numberOfPoints = 131071100L;

declares the variable numberOfPoints to be of type long int with an initial value of 131,071,100.

To display the value of a long int using printf, the letter l is used as a modifier before the integer format characters i, o, and x. This means that the format characters %li can be used to display the value of a long int in decimal format, the characters %lo can display the value in octal format, and the characters %lx can display the value in hexadecimal format.

A long long integer data type can be used like so:

long long int maxAllowedStorage;

This declares the indicated variable to be of the specified extended accuracy, which is guaranteed to be at least 64 bits wide. Instead of a single letter l, two ls are used in the printf string to display long long integers, as in "%lli".

The long qualifier is also allowed in front of a double declaration, like so:

long double US_deficit_2004;

A long double constant is written as a floating constant with the letter l or L immediately following, like so:

1.234e+7L

To display a long double, the L modifier is used. So, %Lf would display a long double value in floating-point notation, %Le would display the same value in scientific notation, and %Lg would tell printf to choose between %Lf and %Le.

The qualifier short, when placed in front of the int declaration, tells the Objective-C compiler that the particular variable being declared is used to store fairly small integer values. The motivation for using short variables is primarily one of conserving memory space, which can be an issue in cases where the program needs a lot of memory and the amount of available memory is limited.

On some machines, a short int takes up half the amount of storage as a regular int variable does. In any case, you are guaranteed that the amount of space allocated for a short int will not be less than 16 bits.

There is no way to explicitly write a constant of type short int in Objective-C. To display a short int variable, place the letter h in front of any of the normal integer conversion characters: %hi, %ho, or %hx. Alternatively, you can use any of the integer conversion characters to display short ints because they can be converted into integers when they are passed as arguments to the printf routine.

The final qualifier that can be placed in front of an int variable is used when an integer variable will be used to store only positive numbers. The following

unsigned int counter;

declares to the compiler that the variable counter is used to contain only positive values. By restricting the use of an integer variable to the exclusive storage of positive integers, the accuracy of the integer variable is extended.

An unsigned int constant is formed by placing the letter u (or U) after the constant, like so:

0x00ffU

You can combine the letters u (or U) and l (or L) when writing an integer constant, so

20000UL

tells the compiler to treat the constant 20000 as unsigned long.

An integer constant that's not followed by any of the letters u, U, l, or L and that is too large to fit into a normal-sized int is treated as an unsigned int by the compiler. If it's too small to fit into an unsigned int, the compiler treats it as a long int. If it still can't fit inside a long int, the compiler makes it an unsigned long int.

When declaring variables to be of type long int, short int, or unsigned int, you can omit the keyword int. Therefore, the unsigned variable counter could have been equivalently declared as follows:

unsigned counter;

You can also declare char variables to be unsigned.

The signed qualifier can be used to explicitly tell the compiler that a particular variable is a signed quantity. Its use is primarily in front of the char declaration, and further discussion is beyond the scope of this book.

Type id

The id data type is used to store an object of any type. It is in a sense a generic object type. For example, the line

id   number;

declares number to be a variable of type id. Methods can be declared to return values of type id, like so:

-(id) newObject: (int) type;

This declares an instance method called newObject that takes a single integer argument called type and returns a value of type id. You should note that id is the default type for return and argument type declarations. So, the following

+allocInit;

declares a class method that returns a value of type id.

The id data type is an important data type used often in this book. It is mentioned in passing here for the sake of completeness. The id type is the basis for very important features in Objective-C know as polymorphism and dynamic binding, which are extensively discussed in Chapter 9, “Polymorphism, Dynamic Typing, and Dynamic Binding.”

Table 4.1 summarizes the basic data types and qualifiers.

Table 4.1. Basic Data Types

image

Arithmetic Expressions

In Objective-C, just as in virtually all programming languages, the plus sign (+) is used to add two values, the minus sign (-) is used to subtract two values, the asterisk (*) is used to multiply two values, and the slash (/) is used to divide two values. These operators are known as binary arithmetic operators because they operate on two values or terms.

You have seen how a simple operation such as addition can be performed in Objective-C. The following program further illustrates the operations of subtraction, multiplication, and division. The last two operations performed in the program introduce the notion that one operator can have a higher priority, or precedence, over another operator. In fact, each operator in Objective-C has a precedence associated with it. This precedence is used to determine how an expression that has more than one operator is evaluated: The operator with the higher precedence is evaluated first. Expressions containing operators of the same precedence are evaluated either from left to right or from right to left, depending on the operator. This is known as the associative property of an operator. Appendix B provides a complete list of operator precedences and their rules of association.

Program 4.2.


// Illustrate the use of various arithmetic operators

#import <stdio.h>

int main (int argc, char *argv[])
{
  int a = 100;
  int b = 2;
  int c = 25;
  int d = 4;
  int result;

  result = a - b; // subtraction
  printf ("a - b = %i ", result);

  result = b * c; // multiplication
  printf ("b * c = %i ", result);

  result = a / c; // division
  printf ("a / c = %i ", result);

  result = a + b * c; // precedence
  printf ("a + b * c = %i ", result);

  printf ("a * b + c * d = %i ", a * b + c * d);
  return 0;
}


Program 4.2. Output


a - b = 98
b * c = 50
a / c = 4
a + b * c = 150
a * b + c * d = 300


After declaring the integer variables a, b, c, d, and result, the program assigns the result of subtracting b from a to result and then displays its value with an appropriate printf call.

The next statement

result = b * c;

has the effect of multiplying the value of b by the value of c and storing the product in result. The result of the multiplication is then displayed using a printf call that should be familiar to you by now.

The next program statement introduces the division operator—the slash. The result of 4, as obtained by dividing 100 by 25, is displayed by the printf statement immediately following the division of a by c.

On some computer systems, attempting to divide a number by zero results in abnormal termination of the program. Even if the program does not terminate abnormally, the results obtained by such a division will be meaningless. In Chapter 6, “Making Decisions,” you will see how you can check for division by zero before the division operation is performed. If it is determined that the divisor is zero, an appropriate action can be taken and the division operation averted.

The expression

a + b * c

does not produce the result of 2550 (102 × 25); rather, the result as displayed by the corresponding printf statement is shown as 150. This is because Objective-C, like most other programming languages, has rules for the order of evaluating multiple operations or terms in an expression. Evaluation of an expression generally proceeds from left to right. However, the operations of multiplication and division are given precedence over the operations of addition and subtraction. Therefore, the expression

a + b * c

is evaluated as

a + (b * c)

by the Objective-C system. (This is the same way this expression would be evaluated if you were to apply the basic rules of algebra.)

If you want to alter the order of evaluation of terms inside an expression, you can use parentheses. In fact, the expression listed previously is a perfectly valid Objective-C expression. Thus, the statement

result = a + (b * c);

could have been substituted in Program 4.2 to achieve identical results. However, if the expression

result = (a + b) * c;

were used instead, the value assigned to result would be 2550 because the value of a (100) would be added to the value of b (2) before multiplication by the value of Objective-C (25) would take place. Parentheses can also be nested, in which case evaluation of the expression proceeds outward from the innermost set of parentheses. Just be sure to have as many closed parentheses as you have open ones.

You will notice from the last statement in Program 4.2 that it is perfectly valid to give an expression as an argument to printf without having to first assign the result of the expression evaluation to a variable. The expression

a * b + c * d

is evaluated according to the rules stated previously as

(a * b) + (c * d)

or

(100 * 2) + (25 * 4)

The result of 300 is handed to the printf routine.

Integer Arithmetic and the Unary Minus Operator

Program 4.3 reinforces what we have just discussed and introduces the concept of integer arithmetic.

Program 4.3.


// More arithmetic expressions
#import <stdio.h>
int main (int argc, char *argv[])
{
  int   a = 25;
  int   b = 2;
  int   result;
  float c = 25.0;
  float d = 2.0;

  printf ("6 + a / 5 * b = %i ", 6 + a / 5 * b);
  printf ("a / b * b = %i ", a / b * b);
  printf ("c / d * d = %f ", c / d * d);
  printf ("-a = %i ", -a);

  return 0;
}


Program 4.3. Output


6 + a / 5 * b = 16
a / b * b = 24
c / d * d = 25.000000
-a = -25


We inserted extra blank spaces between int and the declaration of a, b, and result in the first three statements to align the declaration of each variable. This helps make the program more readable. You also might have noticed in each program presented thus far that a blank space was placed around each operator. This, too, is not required and is done solely for aesthetic reasons. In general, you can add extra blank spaces just about anywhere that a single blank space is allowed. A few extra presses of the spacebar will prove worthwhile if the resulting program is easier to read.

The expression in the first printf call of Program 4.3 reinforces the notion of operator precedence. Evaluation of this expression proceeds as follows:

  1. Because division has higher precedence than addition, the value of a (25) is divided by 5 first. This gives the intermediate result of 4.
  2. Because multiplication also has higher precedence than addition, the intermediate result of 5 is next multiplied by 2, the value of b, giving a new intermediate result of 10.
  3. Finally, the addition of 6 and 10 is performed, giving a final result of 16.

The second printf statement introduces a new twist. You would expect that dividing a by b and then multiplying by b would return the value of a, which has been set to 25. But this does not seem to be the case, as shown by the output display of 24. Did the computer lose a bit somewhere along the way? Very unlikely. The fact of the matter is that this expression was evaluated using integer arithmetic.

If you glance back at the declarations for the variables a and b, you will recall that they were both declared to be of type int. Whenever a term to be evaluated in an expression consists of two integers, the Objective-C system performs the operation using integer arithmetic. In such a case, all decimal portions of numbers are lost. Therefore, when the value of a is divided by the value of b, or 25 is divided by 2, you get an intermediate result of 12 and not 12.5 as you might expect. Multiplying this intermediate result by 2 gives the final result of 24, thus explaining the “lost” digit.

As can be seen from the next-to-last printf statement in Program 4.3, if you perform the same operation using floating-point values instead of integers, you obtain the expected result.

The decision of whether to use a float variable or an int variable should be made based on the variable's intended use. If you don't need any decimal places, use an integer variable. The resulting program will be more efficient—that is, it will execute more quickly on many computers. On the other hand, if you need the decimal place accuracy, the choice is clear. The only question you then must answer is whether to use a float or double. The answer to this question will depend on the desired accuracy of the numbers you are dealing with, as well as their magnitude.

In the last printf statement, the value of the variable a is negated by use of the unary minus operator. A unary operator is one that operates on a single value, as opposed to a binary operator, which operates on two values. The minus sign actually has a dual role: As a binary operator, it is used for subtracting two values; as a unary operator, it is used to negate a value.

The unary minus operator has higher precedence than all other arithmetic operators, except for the unary plus operator (+), which has the same precedence. So the expression

c = -a * b;

results in the multiplication of -a by b. Once again, in Appendix B you will find a table summarizing the various operators and their precedences.

The Modulus Operator

The last operator to be presented in this chapter is the modulus operator, which is symbolized by the percent sign (%). Try to determine how this operator works by analyzing the output from Program 4.4.

Program 4.4.


// The modulus operator

#import <stdio.h>

int main (int argc, char *argv[])
{
  int a = 25, b = 5, c = 10, d = 7;

  printf ("a %% b = %i ", a % b);
  printf ("a %% c = %i ", a % c);
  printf ("a %% d = %i ", a % d);
  printf ("a / d * d + a %% d = %i ", a / d * d + a % d);

  return 0;
}


Program 4.4. Output


a % b = 0
a % c = 5
a % d = 4
a / d * d + a % d = 25


The first statement inside main defines and initializes the variables a, b, c, and d in a single statement.

As you know, printf uses the character that immediately follows the percent sign to determine how to print the next argument. However, if it is another percent sign that follows, the printf routine takes this as an indication that you really intend to display a percent sign and inserts one at the appropriate place in the program's output.

You are correct if you concluded that the function of the modulus operator % is to give the remainder of the first value divided by the second value. In the first example, the remainder, after 25 is divided by 5, is displayed as 0. If you divide 25 by 10, you get a remainder of 5, as verified by the second line of output. Dividing 25 by 7 gives a remainder of 4, as shown in the third output line.

Let's now turn our attention to the expression evaluated in the last statement. You will recall that any operations between two integer values in Objective-C are performed with integer arithmetic. Therefore, any remainder resulting from the division of two integer values is simply discarded. Dividing 25 by 7, as indicated by the expression a / d, gives an intermediate result of 3. Multiplying this value by the value of d, which is 7, produces the intermediate result of 21. Finally, adding the remainder of dividing a by d, as indicated by the expression a % d, leads to the final result of 25. It is no coincidence that this value is the same as the value of the variable a. In general, the expression

a / b * b + a % b

will always equal the value of a, assuming of course that a and b are both integer values. In fact, the modulus operator % is defined to work only with integer values.

As far as precedence is concerned, the modulus operator has equal precedence to the multiplication and division operators. This implies, of course, that an expression such as

table + value % TABLE_SIZE

will be evaluated as

table + (value % TABLE_SIZE)

Integer and Floating-Point Conversions

To effectively develop Objective-C programs, you must understand the rules used for the implicit conversion of floating-point and integer values in Objective-C. Program 4.5 demonstrates some of the simple conversions between numeric data types.

Program 4.5.


// Basic conversions in Objective-C

#import <stdio.h>

int main (int argc, char *argv[])
{
  float  f1 = 123.125, f2;
  int    i1, i2 = -150;
  char   c = 'a';

  i1 = f1;   // floating to integer conversion
  printf ("%f assigned to an int produces %i ", f1, i1);

  f1 = i2;   // integer to floating conversion
  printf ("%i assigned to a float produces %f ", i2, f1);

  f1 = i2 / 100;   // integer divided by integer
  printf ("%i divided by 100 produces %f ", i2, f1);

  f2 = i2 / 100.0;   // integer divided by a float
  printf ("%i divided by 100.0 produces %f ", i2, f2);

  f2 = (float) i2 / 100;   // type cast operator
  printf ("(float) %i divided by 100 produces %f ", i2, f2);

  return 0;
}


Program 4.5. Output


123.125000 assigned to an int produces 123
-150 assigned to a float produces -150.000000
-150 divided by 100 produces -1.000000
-150 divided by 100.0 produces -1.500000
(float) -150 divided by 100 produces -1.500000


Whenever a floating-point value is assigned to an integer variable in Objective-C, the decimal portion of the number gets truncated. So, when the value of f1 is assigned to i1 in the previous program, the number 123.125 is truncated, which means that only its integer portion, or 123, is stored in i1. The first line of the program's output verifies that this is the case.

Assigning an integer variable to a floating variable does not cause any change in the value of the number; the value is simply converted by the system and stored in the floating variable. The second line of the program's output verifies that the value of i2 (–150) was correctly converted and stored in the float variable f1.

The next two lines of the program's output illustrate two points that must be remembered when forming arithmetic expressions. The first has to do with integer arithmetic, which we have already discussed in this chapter. Whenever two operands in an expression are integers (and this applies to short, unsigned, and long integers as well), the operation is carried out under the rules of integer arithmetic. Therefore, any decimal portion resulting from a division operation is discarded, even if the result is assigned to a floating variable (as we did in the program). When the integer variable i2 is divided by the integer constant 100, the system performs the division as an integer division. The result of dividing –150 by 100, which is –1, is therefore the value that is stored in the float variable f1.

The next division performed in the previous program involves an integer variable and a floating-point constant. Any operation between two values in Objective-C is performed as a floating-point operation if either value is a floating-point variable or constant. Therefore, when the value of i2 is divided by 100.0, the system treats the division as a floating-point division and produces the result of –1.5, which is assigned to the float variable f1.

The Type Cast Operator

You've already seen how enclosing a type inside a set of parentheses is used to declare the return and argument types when declaring and defining methods. It serves a different purpose when used inside expressions.

The last division operation from Program 4.5 that reads

f2 = (float) i2 / 100;   // type cast operator

introduces the type cast operator. The type cast operator has the effect of converting the value of the variable i2 to type float for purposes of evaluation of the expression. In no way does this operator permanently affect the value of the variable i2; it is a unary operator that behaves like other unary operators. Because the expression -a has no permanent effect on the value of a, neither does the expression (float) a.

The type cast operator has a higher precedence than all the arithmetic operators except the unary minus and unary plus. Of course, if necessary, you can always use parentheses in an expression to force the terms to be evaluated in any desired order.

As another example of the use of the type cast operator, the expression

(int) 29.55 + (int) 21.99

is evaluated in Objective-C as

29 + 21

because the effect of casting a floating value to an integer is one of truncating the floating-point value. The expression

(float) 6 / (float) 4

produces a result of 1.5, as does the following expression:

(float) 6 / 4

The type cast operator is often used to coerce an object that is a generic id type into an object of a particular class. For example,

id   myNumber;
Fraction *myFraction;
  ...
myFraction = (Fraction *) myNumber;

takes the id variable myNumber and converts it into a Fraction object. The result of the conversion is assigned to the Fraction variable myFraction. You'll learn more about this in a later chapter.

Assignment Operators

The Objective-C language permits you to combine the arithmetic operators with the assignment operator using the following general format:

op=

In this format, op is any of the arithmetic operators, including +, -, *, /, and %. In addition, op can be any of the bit operators for shifting and masking, which is discussed later.

Consider this statement:

count += 10;

The effect of the so-called “plus equals” operator += is to add the expression on the right side of the operator to the expression on the left side of the operator and to store the result back into the variable on the left-hand side of the operator. So, the previous statement is equivalent to this statement:

count = count + 10;

The expression

counter -= 5

uses the “minus equals” assignment operator to subtract 5 from the value of counter and is equivalent to this expression:

counter = counter - 5

A slightly more involved expression is

a /= b + c

which divides a by whatever appears to the right of the equals sign—or by the sum of b and c—and stores the result in a. The addition is performed first because the addition operator has higher precedence than the assignment operator. In fact, all operators but the comma operator have higher precedence than the assignment operators, which all have the same precedence.

In this case, this expression is identical to the following:

a = a / (b + c)

The motivation for using assignment operators is threefold. First, the program statement becomes easier to write because what appears on the left side of the operator does not have to be repeated on the right side. Second, the resulting expression is usually easier to read. Third, the use of these operators can result in programs that execute more quickly because the compiler can sometimes generate less code to evaluate an expression.

A Calculator Class

It's time now to define a new class. We're going to make a Calculator class, which will be a simple four-function calculator you can use to add, multiply, subtract, and divide numbers. Similar to a regular calculator, this one must keep track of the running total, or what's usually called the accumulator. So, methods must let you set the accumulator to a specific value, clear it (or set it to zero), and retrieve its value when you're done. Program 4.6 includes the new class definition and a test program to try your calculator.

Program 4.6.


// Implement a Calculator class

#import <objc/Object.h>
#import <stdio.h>

@interface Calculator: Object
{
    double accumulator;
}

// accumulator methods
-(void)  setAccumulator: (double) value;
-(void)  clear;
-(double) accumulator;

// arithmetic methods
-(void) add: (double) value;
-(void) subtract: (double) value;
-(void) multiply: (double) value;
-(void) divide: (double) value;
@end

@implementation Calculator;
-(void) setAccumulator: (double) value
{
        accumulator = value;
}

-(void) clear
{
        accumulator = 0;
}

-(double) accumulator
{
        return accumulator;
}

-(void) add: (double) value
{
        accumulator += value;
}

-(void) subtract: (double) value
{
        accumulator -= value;
}

-(void) multiply: (double) value
{
        accumulator *= value;
}

-(void) divide: (double) value
{
        accumulator /= value;
}
@end

int main (int argc, char *argv[])
{
    Calculator *deskCalc;

    deskCalc = [[Calculator alloc] init];

    [deskCalc clear];
    [deskCalc setAccumulator: 100.0];
    [deskCalc add: 200.];
    [deskCalc divide: 15.0];
    [deskCalc subtract: 10.0];
    [deskCalc multiply: 5];
    printf ("The result is %g ", [deskCalc accumulator]);
    [deskCalc free];

    return 0;
}


Program 4.6. Output


The result is 50


The Calculator class only has one instance variable, a double value that holds the value of the accumulator. The method definitions themselves are quite straightforward.

Notice the message that invokes the multiply method:

[deskCalc multiply: 5];

The argument to the method is an integer, yet the method expects a double. There is no problem here because numeric arguments to methods are automatically converted to match the type expected. A double is expected by multiply:, so the integer value 5 automatically is converted to a double precision floating value when the function is called. Even though this automatic conversion takes place, it's still better programming practice to supply the correct argument types when invoking methods.

Realize that unlike the Fraction class, where you might work with many different fractions, you might want to work with only a single Calculator object in your program. Yet it still makes sense to define a new class to make working with this object easy. At some point, you might want to add a graphical front end to your calculator so the user can actually click buttons on the screen like one of the calculator applications you probably have installed on your system.

In several of the exercises that follow, you'll see that one additional benefit of defining a Calculator class has to do with the ease of extending it.

Bit Operators

Various operators in the Objective-C language work with the particular bits inside a number. These operators are presented in Table 4.2.

Table 4.2. Bit Operators

image

All the operators listed in Table 4.2, with the exception of the ones complement operator (~), are binary operators and as such take two operands. Bit operations can be performed on any type of integer value but cannot be performed on floating-point values.

The Bitwise AND Operator

Bitwise ANDing is frequently used for masking operations. That is, this operator can be used to easily set specific bits of a data item to 0. For example, the statement

w3 = w1 & 3;

assigns to w3 the value of w1 bitwise ANDed with the constant 3. This has the effect of setting all the bits in w3, other than the rightmost two bits, to 0 and preserving the rightmost two bits from w1.

As with all binary arithmetic operators in C, the binary bit operators can also be used as assignment operators by adding an equal sign. The statement

word &= 15;

therefore performs the same function as the following:

word = word & 15;

Additionally, it has the effect of setting all but the rightmost four bits of word to 0.

When using constants in performing bitwise operations, it is usually more convenient to express the constants in either octal or hexadecimal notation.

The Bitwise Inclusive-OR Operator

When two values are bitwise inclusive-ORed in C, the binary representation of the two values is again compared bit by bit. This time, each bit that is a 1 in the first value or a 1 in the second value produces a 1 in the corresponding bit of the result.

The bitwise inclusive-OR operator is often used to combine several values together, where each value typically has only a single bit turned on in the value. For example, assuming that NSCaseInsensitiveSearch has the predefined value 1 and NSAnchoredSearch has the predefined value 8, the statement

searchOptions = NSCaseInsensitiveSearch | NSAnchoredSearch;

could be used to set the value for searchOptions to be the combination of the NSCaseInsensitiveSearch and NSAnchoredSearch options.

The Bitwise Exclusive-OR Operator

The bitwise exclusive-OR operator, which is often called the XOR operator, works as follows: For corresponding bits of the two operands, if either bit is a 1—but not both—the corresponding bit of the result is a 1; otherwise, it is a 0.

One interesting property of the exclusive-OR operator is that any value exclusive-ORed with itself produces 0. Another interesting trick with the exclusive-OR operator is to use it to exchange two values without using another memory location. Normally, you would interchange two integers, called i1 and i2, with a sequence of statements such as follows:

temp = i1;
i1 = i2;
i2 = temp;

(Assume in the previous sequence of statements that temp has been appropriately declared.) Using the exclusive-OR operator, you can exchange values without the need of the temporary storage location, like so:

i1 ^= i2;
i2 ^= i1;
i1 ^= i2;

It is left as an exercise for you to verify that the previous statements do, in fact, succeed in interchanging the values of i1 and i2.

The Ones Complement Operator

The ones complement operator is a unary operator, and its effect is to simply “flip” the bits of its operand. Each bit of the operand that is a 1 is changed to a 0, and each bit that is a 0 is changed to a 1.

The Left Shift Operator

When a left shift operation is performed on a value, the bits contained in the value are literally shifted to the left. Associated with this operation is the number of places (bits) that the value is to be shifted. Bits that are shifted out through the high-order bit of the data item are lost, and 0s are always shifted in through the low-order bit of the value. So, if w1 is equal to 3, the expression

w1 = w1 << 1;

which can also be expressed as

w1 <<= 1;

results in 3 being shifted one place to the left, which results in 6 being assigned to w1.

The operand on the left of the << operator is the value to be shifted, whereas the operand on the right is the number of bit positions the value is to be shifted by.

The Right Shift Operator

As implied from its name, the right shift operator (>>) shifts the bits of a value to the right. Bits shifted out of the low-order bit of the value are lost. Right shifting an unsigned value always results in 0s being shifted in on the left—that is, through the high-order bits. What is shifted in on the left for signed values depends on the sign of the value being shifted and also on how this operation is implemented on your computer. If the sign bit is 0 (meaning the value is positive), 0s are shifted in no matter what machine you have. However, if the sign bit is 1, on some machines 1s are shifted in and on others 0s are shifted in. This former type of operation is known as an arithmetic right shift, whereas the latter is known as a logical right shift.

It should be noted that the language does not produce a defined result if an attempt is made to shift a value to the left or right by an amount that is greater than or equal to the number of bits in the size of the data item. So, on a machine that represents integers in 32 bits, for example, shifting an integer to the left or right by 32 or more bits is not guaranteed to produce a defined result in your program. You should also note that if you shift a value by a negative amount, the result is also undefined.

Now it is time to show an actual program example that illustrates the use of the various bit operators (see Program 4.7).

Program 4.7.


// Bitwise operators illustrated
#import <stdio.h>

int main (int argc, char *argv[])
{
    unsigned int w1 = 0xA0A0A0A0, w2 = 0xFFFF0000,
                 w3 = 0x00007777;

printf ("%x %x %x ", w1 & w2, w1 | w2, w1 ^ w2);
printf ("%x %x %x ", ~w1, ~w2, ~w3);
printf ("%x %x %x ", w1 ^ w1, w1 & ~w2, w1 | w2 | w3);
printf ("%x %x ", w1 | w2 & w3, w1 | w2 & ~w3);
printf ("%x %x ", ~(~w1 & ~w2), ~(~w1 | ~w2));

w1 ^= w2;
w2 ^= w1;
w1 ^= w2;
printf ("w1 = %x, w2 = %x ", w1, w2);
return 0;
}


Program 4.7. Output


a0a00000 ffffa0a0 5f5fa0a0
5f5f5f5f ffff ffff8888
0 a0a0 fffff7f7
a0a0a0a0 ffffa0a0
ffffa0a0 a0a00000
w1 = ffff0000, w2 = a0a0a0a0
fffe0000 a0a0a000 77770000
7fff8000 a0a0a0 0


You should work out each of the operations from Program 4.7 with a paper and pencil to verify that you understand how the results were obtained.

I should mention the precedence of the various operators here. The AND, OR, and exclusive-OR operators each have lower precedence than any of the arithmetic or relational operators but have higher precedence than the logical AND and logical OR operators. The bitwise AND is higher in precedence than the bitwise exclusive OR, which in turn is higher in precedence than the bitwise OR. The unary ones complement operator has higher precedence than any binary operator. For a summary of operator precedence, see Appendix B.

In the fourth printf call, it is important to remember that the bitwise AND operator has higher precedence than the bitwise OR because this fact influences the resulting value of the expression.

The fifth printf call illustrates DeMorgan's rule—namely that ~(~a & ~b) is equal to a | b, and that ~(~a | ~b) is equal to a & b. The sequence of statements that follows next in the program verifies that the exchange operation works as discussed in the section on the exclusive-OR operator.

Types: _Bool, _Complex, and _Imaginary

Before leaving this chapter, I should mention that there are three other types in the language called _Bool, for working with Boolean (that is, 0 or 1) values, and _Complex and _Imaginary for working with complex and imaginary numbers, respectively.

Support for _Complex and _Imaginary types is optional for a compiler2. For more information, look at the summary of data types in Appendix B.

Objective-C programmers tend to use the BOOL data type instead of _Bool for working with Boolean values in their programs. This “data type” is actually not a data type unto itself but is actually another name for the char data type. This is done with the language's special typedef keyword, which is described in Chapter 10, “More on Variables and Data Types.”

Exercises

  1. Which of the following are invalid constants. Why?

    123.456    0x10.5    0X0G1
    0001       0xFFFF    123L
    0Xab05     0L        -597.25
    123.5e2    .0001     +12
    98.6F      98.7U     17777s
    0996       -12E-12   07777
    1234uL     1.2Fe-7   15,000
    1.234L     197u      100U
    0XABCDEFL  0xabcu    +123

  2. Write a program that converts 27° from degrees Fahrenheit (F) to degrees Celsius (C) using the following formula:

    C = (F - 32) / 1.8

  3. What output would you expect from the following program?

    #import <stdio.h>

    int main (int argc, char *argv[])
    {
       char c, d;

       c = 'd';
       d = c;
       printf ("d = %c ", d);

    return 0;
    }

  4. Write a program to evaluate the polynomial shown here:

    3x3 - 5x2 + 6

    for x = 2.55

  5. Write a program that evaluates the following expression and displays the results (remember to use exponential format to display the result):

    (3.31 x 10-8 x + 2.01 x 10-7) / (7.16 x 10-6 + 2.01 x 10-8)

  6. Complex numbers are numbers that contain two components: a real and an imaginary part. If a is the real component, and b is the imaginary component, the notation

    a + b i

    is used to represent the number.

    Write an Objective-C program that defines a new class called Complex. Following the paradigm established for the Fraction class, define the following methods for your new class:

    -(void) setReal: (double) a;
    -(void) setImaginary: (double) b;
    -(void) print; // display as a + bi
    -(double) real;
    -(double) imaginary;

    Write a test program to test your new class and methods.

  7. Suppose you are developing a library of routines to manipulate graphical objects. Start by defining a new class called Rectangle. For now, just keep track of the rectangle's width and height. Develop methods to set the rectangle's width and height, retrieve these values, and calculate the rectangle's area and perimeter. Assume that these rectangle objects describe rectangles on an integral grid, such as a computer screen. In that case, assume the width and height of the rectangle are integer values.

    Here is the @interface section for the Rectangle class:

    @interface Rectangle: Object
    {
        int  width;
        int  height;
    }

    -(void) setWidth: (int) w;
    -(void) setHeight: (int) h;
    -(int) width;
    -(int) height;
    -(int) area;
    -(int) perimeter;
    @end

    Write the @implementation section and a test program to test your new class and methods.

  8. Modify the add:, subtract:, multiply:, and divide: methods from Program 4.6 to return the resulting value of the accumulator. Test the new methods.
  9. After completing exercise 8, add the following methods to the Calculator class and test them:

    -(double) changeSign;  // change sign of accumulator
    -(double) reciprocal;  // 1/accumulator
    -(double) xSquared;    // accumulator squared

  10. Add a memory capability to the Calculator class from Program 4.6. Implement the following method declarations and test them:

    -(double) memoryClear;     // clear memory
    -(double) memoryStore;     // set memory to accumulator
    -(double) memoryRecall;    // set accumulator to memory
    -(double) memoryAdd;       // add accumulator to memory
    -(double) memorySubtract;  // subtract accumulator from memory

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.214.21