DEC C
Language Reference Manual


Previous Contents Index

6.7 Assignment Operators

There are several assignment operators. Assignments result in the value of the target variable after the assignment. They can be used as subexpressions in larger expressions. Assignment operators do not produce lvalues.

Assignment expressions have two operands: a modifiable lvalue on the left and an expression on the right. A simple assignment consists of the equal sign (=) between two operands:


E1 = E2; 

The value of expression E2 is assigned to E1. The type is the type of E1, and the result is the value of E1 after completion of the operation.

A compound assignment consists of two operands, one on either side of the equal sign (=), in combination with another binary operator. For example:


E1 += E2; 

This is equivalent to the following simple assignment (except that in the compound assignment E1 is evaluated once, while in the simple assignment E1 is evaluated twice):


E1 = E1 + E2; 

In the following example, the following assignments are equivalent:


a *= b + 1; 
 
a = a * (b + 1);         

In another example, the following expression adds 100 to the contents of number[1]:


number[1] += 100; 

The result of this expression is the result after the addition and has the same type as number[1].

If both assignment operands are arithmetic, the right operand is converted to the type of the left before the assignment (see Section 6.10.1).

The assignment operator (=) can be used to assign values to structures and unions. In the VAX C compatibility mode of DEC C, one structure can be assigned to another as long as the structures are defined to be the same size, in bytes. In ANSI mode, the structure values must also have the same type. With all compound assignment operators, all right operands and all left operands must be either pointers or evaluate to arithmetic values. If the operator is --= or +=, the left operand can be a pointer, and the right operand (which must be integral) is converted in the same manner as the right operand in the binary plus (+) and minus (--) operations.

Do not reverse the characters that comprise a compound assignment operator, as in the following example:


E1 =+ E2; 

This is an obsolete form that is no longer supported, but it will pass through the compiler undetected. (It is interpreted as an assignment operator followed by the unary plus operator).

6.8 Comma Operator

When two or more expressions are separated by the comma operator, they evaluate from left to right. The result has the type and value of the rightmost expression (although side effects of the other expressions, if any, do take place). The result is not an lvalue. In the following example, the value 1 is assigned to R, and the value 2 is assigned to T:


R = T = 1,   T += 2,   T -= 1; 

Side effects for each expression are completed before the next expression is evaluated.

A comma expression must be enclosed with parentheses if it appears where commas have some other meaning, as in argument and initializing lists. Consider the following expression:


f(a, (t=3,t+2), c) 

This example calls the function f with the arguments a, 5, and c. In addition, variable t is assigned the value 3.

6.9 Constant Expressions

A constant expression is an expression that contains only constants. A constant expression can be evaluated during compilation rather than at run time, and can be used in any place that a constant can occur. In the following example, limit+1 is a constant expression, and is evaluated at compile time:


#define limit 500 
char x[limit+1] 

A constant expression cannot contain assignment, increment, decrement, function-call, or comma operators, except when they are within the operand of a sizeof operator. Each constant expression must evaluate to a constant that is in the range of representable values for its type.

There are several contexts in which C requires an expression that must evaluate to a constant:

6.9.1 Integral Constant Expressions

An integral constant expression has an integral type and contains only operands that are integer constants, enumeration constants, character constants, sizeof expressions, or floating constants that are the immediate operands of casts. Cast operands in an integral constant expression only convert arithmetic types to integral types, except as part of an operand to the sizeof operator.

C allows more latitude for constant expressions in initializers. Such a constant expression can evaluate to one of the following:

6.9.2 Arithmetic Constant Expressions

An arithmetic constant expression has an arithmetic type and contains only operands that are integer constants, floating constants, enumeration constants, character constants, or sizeof expressions. Cast operators in an arithmetic constant expression only convert arithmetic types to arithmetic types, except as part of an operand to the sizeof operator.

6.9.3 Address Constants

An address constant is a pointer to an lvalue designating an object of static storage duration (see Section 2.10), or to a function designator. Address constants must be created explicitly by using the unary & operator, or implicitly by using an expression of array or function type. The array subscript [] and member access operators . and -->, the address & and indirection * unary operators, and pointer casts can be used to create an address constant, but the value of an object cannot be accessed by use of these operators.

6.10 Data-Type Conversions

C performs data-type conversions in the following four situations:

The following sections describe how operands and function arguments are converted.

6.10.1 Usual Arithmetic Conversions

The following rules---referred to as the usual arithmetic conversions---govern the conversion of all operands in arithmetic expressions. The effect is to bring operands to a common type, which is also the type of the result. The rules govern in the following order:

  1. If either operand is not of arithmetic type, no conversion is performed.
  2. If either operand has type long double, the other operand is converted to long double.
  3. Otherwise, if either operand has type double, the other operand is converted to double.
  4. Otherwise, if either operand has type float, the other operand is converted to float.
  5. Otherwise, the integral promotions are performed on both operands, and the following rules apply:
    1. If either operand has type unsigned long int, the other operand is converted to unsigned long int.
    2. Otherwise, if one operand has type long int and the other has type unsigned int, and if a long int can represent all values of an unsigned int, the operand of type unsigned int is converted to long int. If a long int cannot represent all the values of an unsigned int, both operands are converted to unsigned long int.
    3. Otherwise, if either operand has type long int, the other operand is converted to long int.
    4. Otherwise, if either operand has type unsigned int, the other operand is converted to unsigned int.
    5. Otherwise, both operands have type int.

The following sections elaborate on the usual arithmetic conversion rules.

6.10.1.1 Characters and Integers

A char, short int, or int bit field, either signed or unsigned, or an object that has enumeration type, can be used in an expression wherever an int or unsigned int is permitted. If an int can represent all values of the original type, the value is converted to an int. Otherwise, it is converted to an unsigned int. These conversion rules are called the integral promotions.

This implementation of integral promotion is called value preserving, as opposed to unsigned preserving in which unsigned char and unsigned short widen to unsigned int. DEC C uses value-preserving promotions, as required by the ANSI C standard, unless the common C mode is specified.

To help locate arithmetic conversions that depend on unsigned preserving rules, DEC C, with the check option enabled, flags any integral promotions of unsigned char and unsigned short to int that could be affected by the value-preserving approach for integral promotions.

All other arithmetic types are unchanged by the integral promotions.

In DEC C, variables of type char are bytes treated as signed integers. When a longer integer is converted to a shorter integer or to char, it is truncated on the left; excess bits are discarded. For example:


int i; 
char c; 
 
i = 0xFFFFFF41; 
c = i; 

This code assigns hex 41 ('A') to c. The compiler converts shorter signed integers to longer ones by sign extension.

6.10.1.2 Signed and Unsigned Integers

Conversions also take place between the various kinds of integers.

When a value with an integral type is converted to another integral type (such as int converted to long int) and the value can be represented by the new type, the value is unchanged.

When a signed integer is converted to an unsigned integer of equal or greater size, and the signed integer value is nonnegative, its value is unchanged. If the signed integer value is negative, then:

When an integer value is demoted to an unsigned integer of smaller size, the result is the nonnegative remainder of the value divided by the number one greater than the largest representable unsigned value for the new integral type.

When an integer value is demoted to a signed integer of smaller size, or an unsigned integer is converted to its corresponding signed integer, the value is unchanged if it is small enough to be represented by the new type. Otherwise, the result is truncated; excess high-order bits are discarded and precision is lost.

Conversion between integral types of the same size, whether signed or unsigned, results in no machine-level representation change.

6.10.1.3 Floating and Integral

When a floating-type operand is converted to an integer, the fractional part is discarded.

When a floating-type value is to be converted at compile time to an integer or another floating type, and the result cannot be represented, the compiler reports a warning in the following instances:

When a value of integral type is converted to floating type, and the value is in the range of values that can be represented, but not exactly, the result of the conversion is either the next higher or next lower value, whichever is the natural result of the conversion on the hardware. See your DEC C documentation for the conversion result on your platform.

6.10.1.4 Floating Types

If an operand of type float appears in an expression, it is treated as a single-precision object unless the expression also involves an object of type double or long double, in which case the usual arithmetic conversion applies.

When a float is promoted to double or long double, or a double is promoted to long double, its value is unchanged.

The behavior is undefined when a double is demoted to float, or a long double to double or float, if the value being converted is outside the range of values that can be represented.

If the value being converted is inside the range of values that can be represented, but not exactly, the result is rounded to either the next higher or next lower representable float value.

6.10.2 Pointer Conversions

Although two types (for example, int and long) can have the same representation, they are still different types. This means that a pointer to int cannot be assigned to a pointer to long without using a cast. Nor can a pointer to a function of one type be assigned to a pointer to a function of a different type without using a cast. In addition, pointers to functions that have different parameter-type information, including the old-style absence of parameter-type information, are different types. In these instances, if a cast is not used, the compiler issues an error. Because there are alignment restrictions on some target processors, access through an unaligned pointer can result in a much slower access time or a machine exception.

A pointer to void can be converted to or from a pointer to any incomplete or object type. If a pointer to any incomplete or object type is converted to a pointer to void and back, the result compares equal to the original pointer.

An integral constant expression equal to 0, or such an expression cast to the void * type, is called a null pointer constant. If a null pointer constant is assigned to or compared for equality with a pointer, the constant is converted to a pointer of that type. Such a pointer is called a null pointer, and is guaranteed to compare unequal to a pointer to any object or function.

An array designator is automatically converted to a pointer to the array type, and the pointer points to the first element of the array.

6.10.3 Function Argument Conversions

The data types of function arguments are assumed to match the types of the formal parameters unless a function prototype declaration is present. In the presence of a function prototype, all arguments in the function invocation are compared for assignment compatibility to all parameters declared in the function prototype declaration. If the type of the argument does not match the type of the parameter but is assignment compatible, C converts the argument to the type of the parameter (see Section 6.10.1). If an argument in the function invocation is not assignment compatible to a parameter declared in the function prototype declaration, an error message is generated.

If a function prototype is not present, all arguments of type float are converted to double, all arguments of type char or short are converted to type int, all arguments of type unsigned char and unsigned short are converted to unsigned int, and an array or function name is converted to the address of the named array or function. The compiler performs no other conversions automatically, and any mismatches after these conversions are programming errors.

A function designator is an expression that has function type. Except when it is the operand of the sizeof operator or the unary & operator, a function designator with type "function returning type" is converted to an expression that has type "pointer to function returning type."


Previous Next Contents Index