C Programming for Visual Basic Lovers
In this chapter
Variables and Data
Expressions
Statements
Summary
This chapter continues to explore the basic elements of C programming. The idea of this chapter is to build on the information presented in the previous chapter and complete the coverage of the C language. Expressions and statements apply equally well to C++ and C#.
The sections in this chapter cover the following topics:
"Variables and Data"This section covers variable storage and data. This includes type specifiers and qualifiers, variables and declarators, and initialization.
"Expressions"This section deals with expressions, which are usually assignment or logical operators and operands. This section also covers operators, precedence, conversions, and type casts.
"Statements"This section describes statements, which control the flow of the program and use expressions, tokens, and other statements. This section discusses assignment, control, flow, and looping.
Variables and Data
This section introduces variables, as well as how to store access data in C. This section discusses type specifiers and qualifiers, variables, declarators, and initialization.
Type Specifiers and Qualifiers
Type specifiers define the type of a variable or function. The type specifiers include void, char, short, int, long, float, double, signed, and unsigned.
The signed char, signed int, signed short int, signed long int, along with their unsigned counterparts, are termed integral types. When used alone any integral type is assumed to be signed.
Note
I would not suggest using the unsigned or signed specifiers by themselves, because this isn't common practice. On the other hand, you will often see the signed modifier left off of type specifiers.
The float, double, and long double are floating point types. Floating-point types are always signed, so you won't see unsigned qualifiers used with them.
The optional keywords, signed and unsigned can precede or follow any of the integral types (except enum) and can also be used alone, in which case int is implicitly combined with them.
Type void has three distinct uses:
To specify a function with no return value.
To specify an argument type list for a function that has no arguments.
To specify a pointer to an unspecified type.
The const modifier is used to modify a variable declaration so that the value of the variable cannot be changed. You can use this modifier on a variable and assign the variable value at the time of declaration. Another common way to use this modifier is with a parameter passed to a routine. This keeps the routine from modifying the variable.
Table 3.1 Type Specifiers and Equivalents
Type Specifiers |
Equivalents |
Visual Basic Equivalents |
signed char |
char |
Byte |
signed int |
signed, int |
Long |
signed short int |
short, signed short |
Integer |
signed long int |
long, signed long |
Long |
unsigned char |
- |
- |
unsigned int |
unsigned |
- |
unsigned short int |
unsigned short |
- |
unsigned long int |
unsigned long |
- |
Float |
- |
Single |
double |
- |
Double |
long double |
double |
Double |
void |
- |
- |
const |
- |
- |
Table 3.1 is a cross-reference of the simple types. Obviously, there are other variable types that are worth discussing, but these are the intrinsic types available in C programs.
Variables and Declarators
In this section I'll introduce and discuss variables, as well as discuss how to declare variables. Declarator simply refers to the way you declare a variable. You'll use the type specifiers introduced in the previous section.
In Visual Basic, you can declare a variable without declaring its type. The type is a variant if you declare the variable in this manner. C is much stricter about the way variable declarations are done, and you cannot leave off the type completely.
In this section, I'll cover the following variable and declarator subjects:
"Simple Variables"This section covers declaring intrinsic non-pointer types such as int, long, float, char, and double.
"typedef"This section covers how you create new names for variable types.
"Arrays"Arrays are quite different in C. This section covers declaring and using them.
"Pointers"Pointers are usually a dreaded subject for Visual Basic programmers. This section tries to ease your fears.
"Enumeration Variables"Enumeration Variables are a type of integer with values restricted to the listed values.
"Structures"Structures are user-defined types. This is a large section. In C, there are many ways to declare a structure and you should be able to recognize them.
"Unions"Unions are new to you if you haven't been exposed to C. However they are a simple concept and seldom used.
Simple Variables
The simplest form of a declarator specifies the variables name and type. Storage classes (static or extern) or types (int, long, and others) are required on variable declarations. This type of declaration can be used on numeric variables, structures, unions, enumerations, void types, and for types "created" by typedef names. Pointer, array, and function types require more complicated declarators.
Following are some sample declarations and Visual Basic equivalents:
int x; //C declaration Dim x As Long 'VB declaration
short y = 1; //C declaration Dim y As Integer 'VB declaration y = 1
typedef
typedef allows you to declare synonyms for existing types. It is used extensively in Windows to define variable types. While the definition is not really a new type, it is used like one. (There is more discussion of typedef in the section on structures, where it is used extensively to define structure types.)
The following code shows a simple variable typedef, which just allows you to use a new name for a variable type. In this case ULONG instead of unsigned long.
typedef unsigned int ULONG; ULONG var;
Using typedef with functions is not as common as simple variables, but it is possible to do this.
typedef int MyFunc(int, int); //using the above typedef, this declaration MyFunc Doit; //is the same as this declaration int Doit(int, int);
Arrays
An array declaration names the array, and specifies the type and can also specify the number of elements. A variable of array type is a pointer to the memory location of the first element in the array.
If the declaration does not contain the number of elements, this declaration must declare a variable that has been defined elsewhere. This most commonly would refer to an extern array declaration. In these particular cases it's just as easy to declare this as a pointer to whatever type the array contains.
The following code shows some C samples of array declarations followed with the Visual Basic equivalents (the equivalents assume that option base 0 is used).
The following code shows simple arrays, an array of structures, and how to access the array data:
short a[10]; //c style Dim a(9) As Integer 'VB style
char c[2][3]; //c style Dim c(1, 2) As Byte 'VB style
//c style struct { short a, b; } intarray[50]; 'VB style Type twoint Dim a As Integer Dim b As Integer End Type Dim intarray(49) As twoint
a[0] = 1; //C style a(0) = 1 'VB style
c[0][1] = 2; //cstyle c(0,1) = 2 'VB style
Note
The storage on the VB array is different than the C array. So if you are going to pass the array back and forth, you need to declare multidimensional arrays carefully.
When you use a C array within code, you refer to the array elements using a subscript within a set of square brackets. You can review the above code for a sample of both single and multidimensional arrays.
Pointers
Pointers have traditionally been a dreaded subject in C. There are no real intrinsic equivalents in Visual Basic for pointers, although there are ways to load a pointer into a Visual Basic variable. That variable is still a long, however, and just contains a value that is equal to a pointer. You wouldn't expect Visual Basic to be able to manipulate that pointer or use it in the way that C can.
The declaration of pointers is usually fairly simple, but understanding what they do and how to use them can be difficult.
A pointer is a variable (or cast) that holds the memory address of another variable. It "points" to that variable, hence the name.
Note
Remember that a pointer must always have a variable or valid location in memory to point to. I've seen code where pointers were used without having anything to point to. It's easy to get lulled into trying to use a pointer like this. Of course the compiler or operating system will let you know, usually in ways you would rather not see.
When you look at a variable that contains the address of another variable, it seems to be simple to understand what is happening. But this simplicity quickly gets lost in the syntax and details of dealing with pointers. Let's get some of the syntax details out of the way and then I'll talk about some of the theory and usage.
The pointer syntax uses an asterisk. By convention, you should use the variable name px to indicate a pointer to x.
Note
It isn't mandatory to precede pointer variables with p, but it helps to have a rule and follow it. C programmers usually follow a naming convention known as the Hungarian naming convention.
int *px;
This is a variable defined as a pointer to an int. Right now the variable has a "bogus" value in it. Unlike Visual Basic variables, C variables do not automatically get initialized. This means that the variable will contain whatever happened to be in the memory of the computer at the point that the memory is allocated for the variable. Variables aren't initialized at creation, mostly for speed purposes. Also, even if you did automatically initialize it, it's a pointer. You don't know what it points to. You could only initialize it to NULL, so that you could understand that it doesn't contain a valid value.
Suppose you wanted this pointer to contain the address of a variable.
int x = 10; int *px = &x;
You would introduce a new operator in this code. The & operator, called the address of operator, provides the address of the variable it precedes. So px now contains the address of x. Pretty easy right? How do you refer to the value of x by using x's address, which is stored in px? Although this is a little confusing, you use the asterisk again. This is called the indirection operator.
y = *px;
This operation says that y equals the value stored at px. This would be equal to 10, if you assume the following code sample, which I introduced earlier:
int x = 10; int *px = &x;
Still pretty simple, right?
Let's look at arrays again. Pointers are a pretty common way to look at arrays and actually the array name is a pointer. You can learn several lessons about pointers by looking at arrays and in particular char arrays.
Consider the following declarations:
char cArray[20]; char *pArray = cArray;
You can refer to elements of the array with either pointer variable and you can use the array syntax on either pointer. So saying cArray[5] is the same as saying pArray[5]. This is another way to refer to the value of a pointer. They both would refer to the same value.
Remember above that you used the asterisk to refer to the value of a pointer and you can use that syntax on these pointers to get the value of the first element in the array. So *cArray provides the same value as saying cArray[0].
There is one big difference between cArray (the array) and pArray (the pointer). pArray can be an lvalue, whereas cArray cannot. You saw earlier that you can set the value of pArray and you do not have to set it to the start of the array. The following code demonstrates this:
char cArray[20]; cArray[4] = 7; char *pArray = cArray + 4;
This points pArray at the fifth element in the array. So now *pArray and cArray[4] would be equal, and given the above code, that value would be 7.
You've used a character array for an example, but any simple variable type or array can be manipulated by a pointer variable.
Pointers are used extensively in function call parameters. Using a pointer in a parameter is the only way to allow changes to the variable value. This is because C always passes function arguments by value, so the only way to change the function argument is to pass a pointer to the parameter. In this case, the pointer variable itself is passed by value, but that is okay, because you are only interested in what the pointer points to.
Enumeration Variables
An enumeration consists of a set of named integer constants. The enum type in VB and C work very similarly. The storage in C for an enumeration is the same as an int. You could use an enumeration in place of an int when it serves your purpose to have a defined set of values.
The following rules apply to enumerations:
The constant values in an enumeration start at 0 and increment by 1 unless otherwise set.
An enumeration can contain duplicate constant values, but not duplicate identifiers.
Identifiers within an enumeration must be unique from other identifiers within the same scope. The enum does not "hide" the identifiers from the rest of the program. This is different than structures and unions. See Listing 3.1.
Listing 3.1 Enumeration
enum WEEKDAY { en_Sunday = 1, en_Monday = 2, en_Tuesday = 3, en_Wednesday = 4, en_Thursday = 5, en_Friday = 6, en_Saturday = 7 };
Any portion of the program that can see the enumeration in Listing 3.1 can use the values inside the enumeration just like it is a constant.
Structures
While structures, or User Defined Types (UDTs) are very similar in nature in both VB and C, their definition and use can be very different in some notable ways. For example, while there is basically one way to define a UDT in Visual Basic, it turns out there are a number of variations for structure definition in C.
Attributes of Structures
The structure consists of named members of any type and these named members are not visible beyond the scope of the structure. So to refer to a structure member you must qualify the named member with the structure variable.
In C you know that you can refer to values with actual by value variables or with pointer variables. You use different syntax to refer to the structure members depending upon whether you are using a by value structure variable or a pointer to a structure. When the variable is a structure variable (by value) the dot syntax is used (VarName.MemberName). When the variable is a pointer the pointer syntax is used (VarName->MemberName).
Note
Syntax variation with pointers can trip you up when you first work with structures. Once you understand, it's easy to deal with. You will end up using the pointer syntax quite a bit, because Windows' messages like to send you pointers to structures in the lp parameter.
Listing 3.2 shows both forms of structure variables. Both variables access the same structure, but because one is a pointer variable it uses the pointer syntax.
Listing 3.2 Structure Variables and Pointers
Struct DOG { char name[25]; int legs; int brains; }; struct DOG tDog; struct DOG *pDog = &tDog; //pDog points to tDog tDog.legs = 4; //dot syntax pDog->brains = 0; //pointer syntax
Notice in Listing 3.2 that the tDog and pDog variables access the same values. Since pDog points to tDog, it can change the values in tDog.
Declaration of Structures
There are several ways to declare structures. Structure definition can be like law: If there is no apparent rule, apply the reasonable person theory.
For example, you may declare an anonymous structure and name a variable. You may name variables at the time that you declare the structure, or declare them later. You may embed structures within structures, either through declaration or direct struct statements.
Listings 3.3 through 3.9 show some variations of structure definition.
Listing 3.3 shows an anonymous definition. This is a structure without a tag. You should include a variable name, this variable can be used within the scope of the structure definition.
Listing 3.3 Anonymous Structure Definition
//anonymous structure declares variable x struct { int i; int y; }x;
Listing 3.4 shows another anonymous definition. The only difference here is that this code also includes a pointer variable definition. Both these variables can be used within the scope of the structure definition.
Listing 3.4 Anonymous Structure Definition
//anonymous structure declares variable x and pointer p struct { int i; int y; }x, *p;
Listing 3.5 shows a more common form of a structure definition. This one includes a tag to use for definition of other variables. The last line demonstrates how to use the tag to define a structure variable based on that struct.
Listing 3.5 Named Structure Definition
//named structure declares variable x and pointer p struct b { int i; int y; }x,*p; //declaring a variable tVar from a named structure b struct b tVar;
Listing 3.6 shows an embedded structure. The embedded structure is named and defined within the main structure. The structure declared in this manner also exists at file scope level. You can declare a variable based on the embedded structure, as long as it is named.
Listing 3.6 Embedded Structure Definition
//embedded structure struct b { int i; int y; struct c { int z; }h; }x,*p; //embedded structure c exists at file scope level struct c tVar1; //accessing embedded structure members //x is the named variable in struct b above //h is the named variable of the embedded structure and a member of x //z is the member in h x.h.z = 1; //p is a pointer to structure b p=&x; p->h.z = 1;
Listing 3.7 shows how to do an embedded structure by using a named structure defined outside the main structure. The results here are as exactly the same as if the structure were defined within the main structure.
Listing 3.7 Embedded Named Structure Definition
//we can also embed named structures //the following code is the same as the above c struct struct c { int z; }; struct b { int i; int y; struct c h; }x, *p;
Listing 3.8 demonstrates that the embedded structure can be a pointer to a structure as well as the actual structure variable. Remember that the pointer must point to something real. You can't just put the pointer in the structure and start setting values into a variable that isn't there.
Listing 3.8 Embedded Pointer to a Named Structure
struct c { int z; }; //we could use a pointer to the c struct as well struct b { int i; int y; struct c *h; }x,*p; struct c tVar; x.h = &tVar; //remember the pointer must point to something p=&x; x.h=&x.c; //access the value this way x.c.z=1; //or p->h->z = 1; //or x.h->z = 1;
A structure cannot contain a member that is the type of structure being defined. It can however, contain a member declared as a pointer to the structure in which it appears. This allows construction of linked lists. You'll find a demonstration in Listing 3.9.
Listing 3.9 Embedded Pointer to the Main Structure
//we could use a pointer to the b struct struct b { int i; int y; struct b *next; };
Note
As you can see, the structure definitions can be complicated and deceptive. Understanding structure definitions ranks with understanding pointers in terms of importance. This is why I'm going to introduce typedef specifically as it's used with the struct key word.
typedef with Structures
Using typedef with the structure definition is a popular thing to do, but it does introduce more complexities into an already complex subject. If you use the typedef in conjunction with the struct keyword, the uses of the struct parts change a little. This will make the definition simpler and more "VB-like" than any other way of doing it.
Specifically, the spot where you would define variables becomes an area to define variable types. The sample in Listing 3.10 might make this clearer.
Listing 3.10 Using typedef with struct
//typedef with struct typedef struct tagDOG { int legs; int brains; }DOG, *PDOG; //you can define the variable with the tag struct tagDOG var1; //you can define the variable with the typedef DOG var2; PDOG var3 = &var2; var1.legs = 4; var1.breains = 0; var2.legs = 4; var3->brains = 0;
The typedef keyword in this situation allows you to get rid of the struct keyword in your declarations. This makes the declarations cleaner and, in my opinion, easier to read. You can still include a tag and use the tag, along with the struct keyword to declare a variable.
Using struct essentially allows you to define a new variable type. In Visual Basic, you cannot declare variables along with the UDT declaration, instead you define a variable type for the UDT. This is what typedef allows you to do. You define a new variable type that is used to declare your variables.
Unions
Unions have no Visual Basic counterpart. Similar to a structure, a union's primary differentiating feature is that it saves memory because you can use only one element of it at a time. In other words, the union members are all stored in the same spot in memory, so when using the union, it is only possible to use one member at a time. This is a useful feature, but not one that you will use often.
The union will take up the amount of space required by its largest member. All members will access the same spot in memory, so you have to be sure what is in the union before you access the member. Many times you will find a union included in a structure along with a structure member that defines the type of variable contained in the union.
Initialization
Initialization is an important factor when programming in C. Visual Basic variables are always initialized to default values; scalar variables are initialized to 0, string variables are initialized to empty strings. In C, variables are not initialized.
Variables can and will contain "garbage" unless the programmer initializes them.
Caution
If you don't initialize variables in C bad things can happen. Some C programmers advocate initializing every variable. I don't see that as necessary, but you do need to know what you are going to use a variable for and it must be initialized before it is used as an r-value.
A pointer in particular is critical to be initialized. It is simply useless if it is not initialized. Using an uninitialized pointer is a bad error and can crash your program and the system. It is however, a common error.
Scalar Variables
Scalar variables are single value variables, as opposed to aggregate variables and arrays. There are several rules about initializing variables, depending on their scope and storage class:
File-scope level variables These can be initialized where they are declared, but they will take on a 0 value if not initialized.
Global static variables These variables follow the same rules as for file-scope.
Automatic variablesThese variables are reinitialized each time they come within scope. They do not get a value automatically as global static variables do. They must be initialized if you want them to contain non-garbage values.
Static variablesSuch variables must be initialized with constant expressions. Automatic variables can be initialized with constants or any type of expression, including functions.
Listing 3.11 shows several ways to initialize scalar variables.
Listing 3.11 Initialize Scalar Variables
//initialize i to 10 int i = 10; //initialize pointer px to a null value int *px = 0; //initialize pointer pi to address of i int *pi = &i; //initialize constant int to 12 * 25, value of x cannot be changed const int x = (12 * 25);
Aggregate Variables
In C aggregate variables (structures and arrays) may be initialized with constants in an initializer-list. Visual Basic has no initializer list, you simply use code to initialize aggregate variables. You can also write code that will initialize aggregate variables in C. Using code to initialize variables is straightforward, you just write the code to put the values into the variable.
In the case of initializer lists, for each list, the constant expressions are assigned, in order, to each item in the list. The list should match the size of the variable that the list is initializing. Remember that static variables initialize themselves if a list is too short, but automatic variables that have members not covered by the initializer list will be undefined. If the list is too long, an error occurs.
The list for a structure has a value for each member in the structure. The initializer for a union is a constant expression, which is assigned to the first member of the union.
If an array has an unknown size, the list defines the array size. There is no way to set values in the middle of an array without setting the items at the first of the array. You can initialize the array with code if this is required.
Listing 3.12 demonstrates some different ways to initialize aggregate variables.
Listing 3.12 Initialize Aggregate Variables
//int array with three items in the array int i[] = {1,2,3}; //double dimensions int x[2][3] = { {1,1,1}, {2,2,2} }; //structure struct y { int a,b,c; float d; }var = {1,2,3,4.0};
Strings
You may initialize strings with a string literal, or, since a string in C is really an array, you can also initialize it with a list of constants. For a string (which is enclosed in double quotes), the character \0 is assigned to the end of the list for you.
Listing 3.13 demonstrates this.
Listing 3.13 Initializing Strings
//use a string literal null terminator automatically added char c[] = "This is one way"; //use constant list null terminator automatically added char d[] = {'A','n','o','t','h','e','r',' ','w','a','y'}; //bad thing results in string that is not terminated char e[3] = "abcd"; //wide string w_char w[] = L"This is a wide string.";