 Variables and Data
 Expressions
 Statements
 Summary
Expressions
Expressions cover assigning or computing values and use a sequence of operators and operands. The operators and operands are combined in such a way as to obtain the desired results.
There are an unlimited number of ways to build expressions, but there are some basic rules that are fairly easy to follow. Most of these rules in C are similar to those in Visual Basic. C has some "shortcut" operators that make statements shorter, like the ++ operator that can increment a variable.
This section covers the operators, precedence, conversions, and type casts. This gives you the building blocks for expressions.
Operators and Precedence
In this section I discuss operators, their uses, and their precedence. This provides much of the information you need in order to successfully use these operators within expressions.
Unary Operators
The unary operators in C include *, &, sizeof, , ~, !, ++, and . You've seen and used the indirection (*) and addressof operators (&) earlier in this chapter in the section on pointers:

Indirection operator—This is used to assign a value to the variable that a pointer variable is pointing to.

Addressof operator—This operator is used to assign a variable's address to a pointer. These are demonstrated following:
int x=10;
int *px = &x; //addressof operator to assign pointer
*px = 20; //indirection to assign value
Sizeof operator—This operator provides the amount of storage required to store the item in the operand. This is expressed in bytes. Len in Visual Basic roughly equates to this operator.
Negation operator ()—This operator does what you might expect: It returns the negative of the operand. Visual Basic has the same operator.
Bitwise not (~)—This operator produces the bitwise complement of the operand. The logical not (!) produces the logical opposite of the operand. Visual Basic uses the NOT keyword for the logical not.
Increment (++)and decrement ()operators—These operators can be used in a couple ways. When used within an expression, it is significant if these operators are used as prefix operators or suffix operators. Basically they increment or decrement the variable they are affixed to. Used creatively they can save code. The code following shows how to use these operators.
x = 0;
item[++x] = 1; //item[1]
x = 0;
item[x++] = 1; //item[0]
Using the increment operator as a prefix changes the value before the assignment. Using it as a suffix operator increments the value after the assignment.
Binary Operators
The binary operators include *, /, %, +, , <<, >>, <, >, <=, >=, ==, !, =, &, , ^, &&, , and ,; only some of these have counterparts in Visual Basic. And unlike VB, C also allows a combination of the assignment operator and other binary operators.
The multiplicative operators—the multiplication (*), division (/), and remainder (%) —all have counterparts in Visual Basic. Only the remainder operator is different in VB, the Mod operator is used in VB instead of the %. The multiplication and division operators are the same in C and VB. These three operators work pretty much like you'd expect.
The additive operators plus(+) and minus () have direct counterparts in Visual Basic and work just as you'd expect.
The bitwise shift operators shift their operand left(<<) or right (>>) by the number of positions specified. Visual Basic can simulate this by multiplying by a number, but the shift still does things a bit differently than a multiplication does and works across the entire unsigned number. Because of signed numbers, bit shifts in Visual Basic are fairly complex issues.
Logical comparison operators are the same as Visual Basic in many cases, but different enough in a couple of cases to be aggravating. The <, >, <=, and >= operators are all the same as VB and work like you would expect them. The equivalent comparison is different (==) in C than in VB (=). Also the not operator in C (!) is different than VB (NOT). A common way of using the not operator (!) in C has a different counterpart in VB: not equal in C is !=, in VB it is <>.
Bitwise operators AND (&), OR (), and exclusive OR (^) compare to the Visual Basic keywords AND, OR, and XOR. The problem with the Visual Basic operators is that context decides whether they are bit operators or logical operators and this can produce unexpected results in some cases. In addition you have the fact that there are no unsigned types in Visual Basic which hinder the use of bitwise operators.
Logical operators AND (&&) and OR () compare to the Visual Basic operators AND and OR. Again in VB, context determines which operation is performed, logical or bitwise.
The == operator is worth further mention. This is an equal comparison operator. In Visual Basic the equal operator does double duty, both as assignment and comparison. Not so in C. This has both good points and bad points and it can be especially troublesome to a VB programmer.
VB programmers are used to using the equal sign for comparison and so typically make the mistake of trying to use it in C. This is a simple thing, but sometimes habit takes over. Many times misuse of this operator will not produce an error, just an unwanted result.
C operators are summarized in Table 3.2.
Table 3.2 C Operators
Operator 
Basic Use 
Visual Basic 
Unary 


* 
Indirection 
None 
& 
Address of 
Varptr 
sizeof 
Size of 
Len(x)—sort of 
 
Negation 
 
~ 
Bitwise not 
NOT 
! 
Logical not 
NOT 
++ 
Increment 
x = x + 1 
 
Decrement 
x = x  1 
Binary 


* 
Multiplication 
* 
/ 
Division 
/ 
% 
Remainder 
MOD 
+ 
Addition 
+ 
 
Subtraction 
 
<< 
Left bit shift 
None 
>> 
Right bit shift 
None 
< 
Less than 
< 
> 
Greater then 
> 
<= 
Less then or equal 
<= 
>= 
Greater than or equal 
>= 
== 
Logical equal 
= 
! 
Not 
NOT 
= 
Equal assignment 
= 
& 
Bitwise AND 
AND 
 
Bitwise inclusive OR 
OR 
^ 
Bitwise exclusive or 
XOR 
&& 
Logical AND 
AND 
 
Logical OR 
OR 
, 
Sequential evaluation 
None 
? : 
Conditional Evaluator 
IIF 
Assignment 


= 
Simple assignment 
= 
*= 
Multiplication assignment 
x = x * y 
/= 
Division assignment 
x = x / y 
%= 
Remainder assignment 
x = x MOD y 
+= 
Addition assignment 
x = x + y 
<<= 
Left shift assignment 
None 
>>= 
Right shift assignment 
None 
&= 
Bit AND assignment 
x = x AND y 
= 
Bit OR assignment 
x = x OR y 
^= 
Bit Exclusive OR assignment 
x = x XOR y 
Precedence
Operators listed at the same level have the same precedence. As in Visual Basic, grouping statement segments within parentheses can alter precedence.
Table 3.3 summarizes operator precedence.
Table 3.3 C Operator Precedence
Symbols 
Basic Use 
Associativity 
[]().> postfix ++ postfix  
Expression 
Left to Right 
Prefix ++ prefix—sizeof & *  ~ ! 
Unary 
Right to Left 
Type casts 
Unary 
Right to Left 
* / % 
Multiplicative 
Left to Right 
+  
Additive 
Left to Right 
<< >> 
Bitwise shift 
Left to Right 
< > <= >= 
Relational 
Left to Right 
== != 
Logical Equality 
Left to Right 
& 
Bitwise AND 
Left to Right 
^ 
Bitwise Exclusive OR 
Left to Right 
 
Bitwise Inclusive OR 
Left to Right 
&& 
LogicalAND 
Left to Right 
 
LogicalOR 
Left to Right 
?: 
Conditionalexpression 
Right to Left 
= *= /= %= += = <<= >>= &= ^= = 
Assignment 
Right to Left 
, 
Sequential evaluation 
Left to Right 
There are subtle problems that can creep into expressions with multiple operators depending on your compiler and the current compilation flags. For example, logical expressions will often "shortcut" the latter part of the expression if the first part is enough to determine the outcome of the logical expression.
Tip
Precedence is difficult to deal with in complex expressions. My advice is to group your expressions so there will be little ambiguity.
Conversions and Type Casts
Conversions and type casts follow a set of rules. These rules involve a couple of definitions that you need to know to understand the conversions involving numeric data:
Sign extend—This rule dictates how smaller integral types are converted to larger ones. The number must be represented in a different manner because the bit that denotes the negativity is in a different spot.
Preserve low order word or byte—This rule describes how a larger integral type is converted to a smaller one. The high order word or byte will be ignored, effectively it is dropped.
Loss of precision—This rule governs what happens when a number is converted to float and the significant digits are larger than 7. It can also happen with a double after 14 digits.
From Signed Integral Types
When converting from signed integral types, the main point to remember in integral conversions is that there is a loss of the upper bytes when going from a larger integral to a smaller one, like a long to a short. If there is information contained in the upper bytes, it will therefore be lost. Converting from a smaller int to a larger one involves no real problems. Converting a signed integer to an unsigned one doesn't change the data as long as they are the same size. It may change the way the bits are interpreted if the signed number is negative, but the in memory representation will remain unchanged as long as they are the same size.
Converting an integral type to a decimal type (float or double) should leave the number unchanged, unless the number is longer than the decimal type's significant digits, in which case a loss of precision will occur. A summary of signed integral conversions is shown in Table 3.4. Also a floatingpoint representation has gaps in its coverage of all decimal numbers (which is infinite).
Note
Because the long will be converted to a mantissa and an exponent (for example, 1000 becomes 1E+3), you can actually end up with a different number than what you started with. This has nothing to do with loss of precision that results from too few significant digits.
Table 3.4 Signed Integral Conversions
From 
To 
Method 
char 
short 
Signextend 
char 
long 
Signextend 
char 
unsigned char 
Preserve pattern; highorder bit loses function as sign bit 
char 
unsigned short 
Signextend to short; convert short to unsigned short 
char 
unsigned long 
Signextend to long; convert long to unsigned long 
char 
float 
Signextend to long; convert long to float 
char 
double 
Signextend to long; convert long to double 
char 
long double 
Signextend to long; convert long to double 
short 
char 
Preserve loworder byte 
short 
long 
Signextend 
short 
unsigned char 
Preserve loworder byte 
short 
unsigned short 
Preserve bit pattern; highorder bit loses function as sign bit 
short 
unsigned long 
Signextend to long; convert long to unsigned long 
short 
float 
Signextend to long; convert long to float 
short 
double 
Signextend to long; convert long to double 
short 
long double 
Signextend to long; convert long to double 
long 
char 
Preserve loworder byte 
long 
short 
Preserve loworder word 
long 
unsigned char 
Preserve loworder byte 
long 
unsigned short 
Preserve loworder word 
long 
unsigned long 
Preserve bit pattern; highorder bit loses function as sign bit 
long 
float 
Represent as float. If long cannot be represented exactly, some precision is lost. 
long 
double 
Represent as double. If long cannot be represented exactly as a double, some precision is lost. 
long 
long double 
Represent as double. If long cannot be represented exactly as a double, some precision is lost. 
From Unsigned Integral Types
The concerns with unsigned types are the same as with signed integral types. Again, going from unsigned to signed may change the way the bits are interpreted, but in memory it will still be the same if the types are the same size. A summary of unsigned integral conversions is shown in Table 3.5.
Table 3.5 Unsigned Integral Conversions
From 
To 
Method 
unsigned char 
char 
Preserve bit pattern; highorder bit becomes sign bit 
unsigned char 
short 
Zeroextend 
unsigned char 
long 
Zeroextend 
unsigned char 
unsigned short 
Zeroextend 
unsigned char 
unsigned long 
Zeroextend 
unsigned char 
float 
Convert to long; convert long to float 
unsigned char 
double 
Convert to long; convert long to double 
unsigned char 
long double 
Convert to long; convert long to double 
unsigned short 
char 
Preserve loworder byte 
unsigned short 
short 
Preserve bit pattern; highorder bit becomes sign bit 
unsigned short 
long 
Zeroextend 
unsigned short 
unsigned char 
Preserve loworder byte 
unsigned short 
unsigned long 
Zeroextend 
unsigned short 
float 
Convert to long; convert long to float 
unsigned short 
double 
Convert to long; convert long to double 
unsigned short 
long double 
Convert to long; convert long to double 
unsigned long 
char 
Preserve loworder byte 
unsigned long 
short 
Preserve loworder word 
unsigned long 
long 
Preserve bit pattern; highorder bit becomes sign bit 
unsigned long 
unsigned char 
Preserve loworder byte 
unsigned long 
unsigned short 
Preserve loworder word 
unsigned long 
float 
Convert to long; convert long to float 
unsigned long 
double 
Convert directly to double 
unsigned long 
long double 
Convert to long; convert long to double 
From Floating Point Types
Floating point conversions can result in loss of precision or can even be undefined if, for example, the float value is converted to a short and the float value is out of the short's range. A summary of floating point conversions is shown in Table 3.6.
Table 3.6 Floating Point Conversions
From 
To 
Method 
Float 
char 
Convert to long; convert long to char 
Float 
short 
Convert to long; convert long to short 
Float 
Llong 
Truncate at decimal point. If result is too large to be represented as long, result is undefined. 
Float 
unsigned short 
Convert to long; convert long to unsigned short 
Float 
unsigned long 
Convert to long; convert long to unsigned long 
Float 
double 
Change internal representation 
Float 
long double 
Change internal representation 
Double 
char 
Convert to float; convert float to char 
Double 
short 
Convert to float; convert float to short 
Double 
long 
Truncate at decimal point. If result is too large to be represented as long, result is undefined. 
Double 
unsigned short 
Convert to long; convert long to unsigned short 
Double 
unsigned long 
Convert to long; convert long to unsigned long 
double 
float 
Represent as a float. If double value cannot be represented exactly as float, loss of precision occurs. If value is too large to be represented as float, the result is undefined. 
long double 
char 
Convert to float; convert float to char 
long double 
short 
Convert to float; convert float to short 
long double 
long 
Truncate at decimal point. If result is too large to be represented as long, result is undefined. 
long double 
unsigned short 
Convert to long; convert long to unsigned short 
long double 
unsigned long 
Convert to long; convert long to unsigned long 
long double 
float 
Represent as a float. If double value cannot be represented exactly as float, loss of precision occurs. If value is too large to be represented as float, the result is undefined. 
long double 
double 
The long double value is treated as double. 
To and From Pointers
Pointers can be converted to integral types and back to pointers. Here you simply need to be aware of the size of the pointer and the size of the integral, which may be determined by the memoryaddressing model of your operating system (for example, 32 bit for WIN32).
Generally speaking pointers can be converted without loss of data or any consequences. However there can be some alignment issues with some types of pointers, as well as other issues with the data.
Note
The biggest issue in converting a pointer has to do with accessing the data after the pointer is converted. Usually this is why you convert the pointer, so you can access the data. However, there is no protection in C if you want to convert a pointer to a double to a pointer to a long and then try to use the data like it contains long data. This gives erroneous results, but you can do it.
Other Types
Because an enum is an integer, conversions for it work the same as for an integer. For the Microsoft compiler, an integer is the same as a long.
Void types have no value and cannot be converted—only void pointers may be converted to other pointer types with no loss of information.
Type Casts
Type casts are the way you change one variable type to another. Obviously some type casts are allowable and others are not. The conversion rules you saw in the previous sections apply to type casts as well as implicit conversions. The code following shows a few examples of type casts.
//type casts //int to short int i=2; short sval = (short) i; //long to pointer – common in handling lParam of SendMessage char *p; p = (char *)lParam;
Table 3.7 summarizes the allowable type casts.
Table 3.7 Type Cast Conversions
Destination Types 
Potential Sources 
Integral types 
Any integer type or floatingpoint type, or pointer to an object 
Floatingpoint 
Any arithmetic type 
A pointer to an object, or (void *) 
Any integer type, (void *), a pointer to an object, or a function pointer 
Function pointer 
Any integral type, a pointer to an object, or a function pointer 
A structure, union, or array 
None 
Void type 
Any type 