Chapter 2

Exercise 2.1

Trigraphs are used when the input device used, or the host system's native character set, do not support enough distinct characters for the full C language.

Exercise 2.2

Trigraphs would not be used in a system that has enough distinct characters to allocate a separate one to each of the C language symbols. For maximum portability, one might see a trigraph representation of a C program being distributed, on the grounds that most systems which do not use ASCII will be able to read ASCII coded data and translate it into their native codeset. A Standard C compiler could then compile such a program directly.

Exercise 2.3

White space characters are not equivalent to each other inside strings and character constants. Newline is special to the preprocessor.

Exercise 2.4

To continue a long line. Especially in systems that have an upper limit on physical line length.

Exercise 2.5

They become joined.

Exercise 2.6

Because the */ which apparently terminates the inner comment actually terminates the outer comment.

Exercise 2.7

31 characters for internal variables, six for external variables. The six character names must not rely on distinction between upper and lower case, either.

Exercise 2.8

A declaration introduces a name and a type for something. It does not necessarily reserve any storage.

Exercise 2.9

A definition is a declaration that also reserves storage.

Exercise 2.10

It is always the case that the largest range of values can be held in a long double, although it may not actually be any different from one of the smaller floating point types.

Exercise 2.11

The same answer holds true for the type with the greatest precision: long double. C does not permit the language implementor to use the same number of bits for, say, double and long double, then to allocate more bits for precision in one type and more for range in the other.

Exercise 2.12

There can never be problems assigning a shorter floating point type to a longer one.

Exercise 2.13

Assigning a longer floating type to a shorter one can result in overflow and undefined behaviour.

Exercise 2.14

Undefined behaviour is completely unpredictable. Anything may happen. Often, nothing seems to happen except that erroneous arithmetic values are produced.

Exercise 2.15

  1. Signed int (by the integral promotions).
  2. This cannot be predicted without knowing about the implementation. If an int can hold all of the values of an unsigned char the result will be int, again by the integral promotions. Otherwise, it will have to be unsigned int.
  3. Unsigned int.
  4. Long.
  5. Unsigned long.
  6. Long.
  7. Float.
  8. Float.
  9. Long double.

Exercise 2.16

  1. i1 % i2
  2. i1 % (int)f1
  3. If either operand is negative, the sign is implementation defined, otherwise it is positive. This means that, even if both operands are negative, you can't predict the sign.
  4. Two—unary negate, binary subtract.
  5. i1 &= 0xf;
  6. i1 |= 0xf;
  7. i1 &= ~0xf;
  8. i1 = ((i2 >> 4) & 0xf) | ((i2 & 0xf) << 4);
  9. The result is unpredictable. You must never use the same variable more than once in an expression if the expression changes its value.

Exercise 2.17

  1. (c = (( u * f) + 2.6L);
    (int = ((float) + long double);
    (int = (long double));

    Note: the integral promotion of char to int might be to unsigned int, depending on the implementation.

  2. (u += (((--f) / u) % 3));
    (unsigned += ((float / unsigned) % int));
    (unsigned += (float % int));
    (unsigned += float);
  3. (i <<= (u * (- (++f))));
    (int <<= (unsigned * (- float)));
    (int <<= (unsigned * float));
    (int <<= float);

    The rules for the shift operators state the right-hand operand is always converted to int. However, this does not affect the result, whose type is always determined by the type of the left-hand operand. This is doubly so for the current example, since an assignment operator is being used.

  4. (u = (((i + 3) + 4) + 3.1));

    The rules state that the subexpressions involving + can be arbitrarily regrouped, as long as no type changes would be introduced. The types are:

    (unsigned = (((int + int) + int) + double))

    so the leftmost two additions can be regrouped. Working from the left:

    (unsigned = ((int + int) + double));
    (unsigned = (int + double));
    (unsigned = double);
  5. (u = (((3.1 + i) + 3 ) + 4));

    See the comments above on regrouping.

    (unsigned = (((double + int) + int) + int));

    The two rightmost additions can be regrouped.

    (unsigned = ((double + int) + int));
    (unsigned = (double + int));
    (unsigned = double);
  6. (c = ((i << (- (--f))) & 0xf));
    (char = ((int << (- (--float))) & int ));
    (char = ((int << (- float)) & int ));
    (char = ((int << float) & int));
    (char = (int & int));