Home > Articles > Programming > Visual Basic

  • Print
  • + Share This
This chapter is from the book

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 address-of 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.

  • Address-of operator—This operator is used to assign a variable's address to a pointer. These are demonstrated following:

  • int x=10;
    int *px = &x; //address-of 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

&&

Logical-AND

Left to Right

||

Logical-OR

Left to Right

?:

Conditional-expression

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 floating-point 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

Sign-extend

char

long

Sign-extend

char

unsigned char

Preserve pattern; high-order bit loses function as sign bit

char

unsigned short

Sign-extend to short; convert short to unsigned short

char

unsigned long

Sign-extend to long; convert long to unsigned long

char

float

Sign-extend to long; convert long to float

char

double

Sign-extend to long; convert long to double

char

long double

Sign-extend to long; convert long to double

short

char

Preserve low-order byte

short

long

Sign-extend

short

unsigned char

Preserve low-order byte

short

unsigned short

Preserve bit pattern; high-order bit loses function as sign bit

short

unsigned long

Sign-extend to long; convert long to unsigned long

short

float

Sign-extend to long; convert long to float

short

double

Sign-extend to long; convert long to double

short

long double

Sign-extend to long; convert long to double

long

char

Preserve low-order byte

long

short

Preserve low-order word

long

unsigned char

Preserve low-order byte

long

unsigned short

Preserve low-order word

long

unsigned long

Preserve bit pattern; high-order 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; high-order bit becomes sign bit

unsigned char

short

Zero-extend

unsigned char

long

Zero-extend

unsigned char

unsigned short

Zero-extend

unsigned char

unsigned long

Zero-extend

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 low-order byte

unsigned short

short

Preserve bit pattern; high-order bit becomes sign bit

unsigned short

long

Zero-extend

unsigned short

unsigned char

Preserve low-order byte

unsigned short

unsigned long

Zero-extend

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 low-order byte

unsigned long

short

Preserve low-order word

unsigned long

long

Preserve bit pattern; high-order bit becomes sign bit

unsigned long

unsigned char

Preserve low-order byte

unsigned long

unsigned short

Preserve low-order 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 memory-addressing 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 floating-point type, or pointer to an object

Floating-point

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


  • + Share This
  • 🔖 Save To Your Account