Morrison_FM 8/24/00 11:43 AM Page ii C++ for VB Programmers Copyright © 2000 by Jonathan D. Morrison All rights reserved. No part of this work may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording, or by any information storage or retrieval system, without the prior written permission of the copyright owner and the publisher. ISBN (pbk): 1-893115-76-3 Printed and bound in the United States of America 12345678910 Trademarked names may appear in this book. Rather than use a trademark symbol with every occurrence of a trademarked name, we use the names only in an editorial fashion and to the benefit of the trademark owner, with no intention of infringement of the trademark. Editorial Directors: Dan Appleman, Gary Cornell, Karen Watterson Technical Reviewer: Dan Appleman Editor: Katharine Dvorak Projects Manager: Grace Wong Supervising Production Editor: MaryAnn Brickner Production Services and Page Composition: Impressions Book and Journal Services, Inc. Artist: Frank Giangreco Cover: Karl Miyajima Distributed to the book trade in the United States by Springer-Verlag New York, Inc.,175 Fifth Avenue, New York, NY, 10010 and outside the United States by Springer-Verlag GmbH & Co. KG, Tiergartenstr. 17, 69112 Heidelberg, Germany In the United States, phone 1-800-SPRINGER; orders@springer-ny.com; http://www.springer-ny.com Outside the United States, contact orders@springer.de; http://www.springer.de; fax +49 6221 345229 For information on translations, please contact Apress directly at 901 Grayson Street, Suite 204, Berkeley, CA, 94710 Phone: 510-549-5931; Fax: 510-549-5939; info@apress.com; http://www.apress.com The information in this book is distributed on an “as is” basis, without warranty. Although every precaution has been taken in the preparation of this work, neither the author nor Apress shall have any liability to any person or entity with respect to any loss or damage caused or alleged to be caused directly or indirectly by the information contained in this work. Morrison_001_224 8/22/00 11:53 AM Page 33 C H A P TER 4 C++ 101 WELL, IT ’S INEVITABLE . IF WE ARE GOING TO DO anything useful with C++ we are going to have to learn the syntax of the language. Because you already know Visual Basic, it seems logical to use this knowledge as a crutch for learning C++, which is what we will do throughout this chapter. As you start to learn C++ it is important to remember that you already know how to program—you just don’t know the syntax of C++. Think of the process as learning how to drive a new type of car for the first time. You already know how to drive; you just have to learn where the trunk release is and how to set the clock in the new car and you’re as good as you were in your old car. Data and Variables Regardless of the language in which it was written, every computer program contains some amount of data. Even if the program is only to write the words “Hello World,” the fact that a program runs on a computer is proof that the program contains data. Thus, it is a good idea to start our learning of the C++ language by examining the available C++ data types, their Visual Basic equivalents (where applicable), and their storage capacity, which is summarized in Table 4-1. You can see that there are many different data types available in C++. Also notice that in most cases they map directly to Visual Basic types. NOTE: The void data type in C++ is special in that it has no value. It is used to show that there is an absence of value. The void type is usually used as the return type of a function to denote that the function does not return a value, or as the only item in a parameter list for a function that takes no arguments. Declaring Variables Knowing the data types available in C++ is one thing, but we also need to know how to declare variables in order to make use of these data types. The syntax for variable declaration is in the form: DataType VariableName [= InitialValue]; 33 Morrison_001_224 8/22/00 11:53 AM Page 34 Chapter 4 Table 4-1. C++ Data Types VISUAL BASIC NAME SIZE (BYTES) EQUIVALENT RANGE short unsigned short bool int 2 2 1 *4 Integer N/A Boolean Long unsigned int long *4 4 Long Long unsigned long char unsigned char enum float double long double void 4 1 1 *4 4 8 10 0 Long N/A Byte Long Single Double N/A N/A –32,768 to 32,767 0 to 65,535 **true or false –2,147,483,648 to 2,147,483,647 0 to 4,294,967,295 –2,147,483,648 to 2,147,483,647 0 to 4,294,967,295 –128 to 127 0 to 255 Same as int 3.4E +/- 38 (7 digits) 1.7E +/- 308 (15 digits) 1.2E +/- 4932 (19 digits) Used to show an absence of value * Assumes Win32 platform. ** In C++ the value true equates to any non-zero value, while in Visual Basic the value True equates to 1. In both cases false equates to 0. An example declaration is as follows: int x = 0; This statement assigns the value 0 to the variable x. In fact, any of the numeric data types in C++ (which include short, int, long, float and double) can be initialized in this way. Now you may have noticed that the keyword unsigned appears before some of the types in our data type chart. This is because C++ supports the idea of unsigned numbers. An unsigned number is simply a number whose value cannot be less than zero. Unsigned numbers are useful for several applications, such as representing binary data. (In contrast, Visual Basic has only one unsigned data type, Byte, which covers the range 0 to 255.) For example, if I wanted to assign the number 14 to x in my code, I would write the following assignment statement: unsigned int x = 14; 34 Morrison_001_224 8/22/00 11:53 AM Page 35 C++ 101 NOTE: unsigned can prefix most C++ numeric data types. The numeric data types are straight forward, but there are several data types that require a little more explanation as to how they are used. We’ll look at those now. The char Data Type The char data type is quite flexible. It can be used for its namesake and hold a single character with an assignment such as the following: char myChar = ‘a’; Notice the use of single quotes surrounding the initializing value of a. When you want to assign a single character to a variable of type char, you must always surround it with single quotes. You may assume that because we told our program to store the character a in memory that it did. Well, actually it didn’t. Don’t assume that we have a renegade variable here; it is just that a computer is not able to store characters—only numbers. So what does get stored as the value of myChar? The number 61, which just happens to be the hexadecimal equivalent of the number 97, which just happens to be the ASCII (American Standard Code for Information Interchange) value for the character a. Make sense? Good. Now don’t let this confuse you. The key thing to remember is that all of the data in a computer is stored as numbers. It is the context in which we use those numbers that generates meaning. Think of the saying, “One man’s trash is another man’s treasure.” To someone just looking at the memory stored in our program, the number 61 doesn’t mean anything. But, because we know that 61 is the ASCII representation of a character, to us it means a lot. Just remember that all data interpretation is relative to its intended use. (This concept will start to make more sense when we cover the printf() function later in this chapter.) The char[] Data Type The char[] data type is not a unique data type; it is just an array of char, or basically a string of characters. We could use this data type to store any character 35 Morrison_001_224 8/22/00 11:53 AM Page 36 Chapter 4 string that is too large for the type char. (In other words, a string that is more than one character long.) Following is an example usage of type char[]: char myChars[255]; This declaration is pretty much identical to the following declaration in Visual Basic: Dim myChars As String * 255; Both of these declarations allocate space for 255 characters. They cannot grow in size later in the program in which they are used, which is limiting in most cases. Where this type of declaration is useful is in a situation where you know the maximum length of a string. However, there is another way to declare a char[ ], shown here: char myChars[] = "Hello"; This declaration is identical to the following: char myChars[6] = "Hello"; In other words, when a char[] is set to a string with no size specified, the compiler counts the number of characters in the string and automatically sets the char[ ] to that size plus one. Why plus one? The extra space is needed to hold the NULL terminator (which I cover in the next chapter). What is interesting about char[ ] is that you can access any of the members of the array directly by using standard array notation. For example, the following statement references the ‘e’ in “Hello”: myChars[1] Why [1]? Because unlike the schizophrenic Visual Basic (which has arrays starting at one and zero), arrays in C++ are always 0-based. The first element of an array is at index 0, such that myChars[0],from our previous example, is equal to the letter ‘H’ in “Hello.” The enum Data Type The enum data type is the identical cousin of the enum construct in Visual Basic. To declare an enumeration we use the following syntax: 36 Morrison_001_224 8/22/00 11:53 AM Page 37 C++ 101 enum RETURN_VALUES { RET_OK, RET_FAILED, RET_UNDEFINED }; Now, just as in the Visual Basic version of enum, if no specific value is set for an element, all of the elements are incremented in order of appearance starting at zero. For example, in our previous declaration, RET_OK equals 0, RET_FAILED equals 1 and RET_UNDEFINED equals 2. If any member is set with a value then the next member that does not have a value gets incremented based on that number. Consider the following alteration to our enum: enum RETURN_VALUES { RET_OK, RET_FAILED = 100, RET_UNDEFINED }; In this version of our enum, RET_OK equals 0, RET_FAILED equals 100, and RET_UNDEFINED equals 101. This works exactly as in Visual Basic. NOTE: While enums allow for assigning some values and not others, for the sake of readability it is generally better to assign all of the values in an enum or assign none at all. User Defined Types Although the pre-defined types in C++ are useful, sometimes you just need to create your own custom type. In Visual Basic we do this with a User Defined Type (UDT) as shown here: Private Type MyType lngNum1 As Long lngNum2 As Long End Type 37 Morrison_001_224 8/22/00 11:53 AM Page 38 Chapter 4 We can then use it in code like this: Private Sub Form_Load() Dim t As MyType t.lngNum1 = 1 t.lngNum2 = 2 End Sub This creates a flexible way of representing custom data types and data relationships. It should be no surprise that C++ also has the ability to express UDTs. In C++, a struct is used to do this. The syntax for creating a struct is as follows: struct myStruct { int num1; int num2; }; We can then use it like so: struct myStruct x; x.num1 = 1; x.num2 = 2; Notice that it is similar to the Visual Basic version except that we have to prefix our variable declaration with the keyword struct in C++. Standard Library Functions Visual Basic makes it easy to become spoiled by the number of things that are done for us by the development environment and the language itself. The fact that all of Visual Basic’s language facilities are at our fingertips as soon as we open a new project is not only useful but also subversively amazing. We don’t have to worry about where Visual Basic keeps the MsgBox() function or where the InStr() function lives. We just call them up as we need to and they magically appear. In C++ however, this is not the case. We get nothing by default; we have to ask for anything we want. The way to ask for things in C++ is to use the #include directive. Remember that #include is used to bring another file (usually a header file) into our program. If we want to print to the screen from our program, we 38 Morrison_001_224 8/22/00 11:53 AM Page 39 C++ 101 have to either write a function to do so, or we have to #include a header file that links to or contains a function that does screen output. There is no function in the C++ language itself that prints to the screen. Now, anyone with any previous exposure to C++ will probably disagree with me and say that there are in fact functions in C++ that print to the screen. And, they’d be right—sort of. Let me explain. In C++ there is an entity known as the Standard C++ Library, which is a set of header files that link to the C++ runtime library. Every implementation of the C++ compiler ships with these header files and the runtime library. These header files are the C++ programmers interface into the C++ runtime library. The Standard C++ Library is a set of language support files that make using the language easier and more efficient. They are a part of the American National Standards Institute (ANSI) C++ standard but are not an official part of the language. Whether or not the Standard C++ Runtime Library is part of the C++ language is only a matter of semantics, because the fact of the matter is that this library contains the functions that all C++ programmers use to write C++ programs. You may be wondering why I brought up this whole discussion if it is just about semantics. Here’s why: Let’s suppose that the C++ runtime has a function named printToScreen() and I write C++ source code that uses that function. I would expect that when I compiled my program that the compiler would find that function in the standard C++ library, but it doesn’t. Remember from Chapter 3 that the linker only knows about things that are on its “to do” list, which comes in the form of function prototypes and declarations. In my source code I never told the linker anything about the printToScreen() function. I just used it in my code. This is no good because the linker has to take my call to that function and convert to a function pointer that points to the real implementation of printToScreen(), but in this case there is no declaration for the linker to resolve with. Therefore, if I want to use the printToScreen() function, I have to #include the header file that contains the declaration of that function. There are roughly 50 header files in the Standard C++ Library, which correlate directly to the C++ runtime library. These headers are divided into logical opera- NOTE: Due to the vastness of the Standard C++ Library, I will not attempt to detail each one here. I will instead explain the use of each function that we use in our example programs. Microsoft has done a good job of documenting and providing example uses of each function in the C++ runtime library. I suggest that you poke around through each header file and look up the usage of the various functions. You can also search by functionality in the help file. It is also worth noting that in Windows programming, many of the C++ Runtime Library functions have more efficient counterparts available through the Win32API. However, it is useful to know the common C++ Runtime Library functions since you will be writing C++ code. 39 Morrison_001_224 8/22/00 11:53 AM Page 40 Chapter 4 tional units. For example, the Standard C++ Library file, <stdio.h>, defines the standard input/output functions available from the C++ runtime library. Ultimately, if you want to use one of the functions in the runtime, you have to include the proper header file. Program Structure Okay, now that we have some understanding of the basic elements of C++, let’s try to put them to use in an application. C++ programs are structured pretty much like Visual Basic programs. They consist of code modules that contain functions, variables, and constants (which should all be familiar terms). There are, however, a few elements of C++ that will be completely foreign to most Visual Basic programmers. Not to worry—by the end of this book they will be as familiar as “Option Explicit” is to you today. I am a firm believer in learning by example, so, let’s write a simple C++ program and then examine each part of it in detail in order to learn the different components that make up a C++ program. To start, open Visual Studio and select File ➔ New. Then select “Win32 console application” as our project type. In the Project Name field of the New Project dialog box, enter ex01_ch4. When prompted for the type of project, select “A simple application.” as shown in Figure 4-1. After clicking OK, your development environment should look similar to the environment shown in Figure 4-2. Figure 4-1. The “New” project dialog box 40 Morrison_001_224 8/22/00 11:53 AM Page 41 C++ 101 Figure 4-2. The Visual Studio development environment with ex01_ch04 loaded Notice that there are two views available for the project: Class View and File View. These choices are available by selecting one of the aforementioned tabs at the bottom of the Workspace window on the left side of the development environment (assuming you haven’t moved it), as shown in Figure 4-3. We want to select File View as our view. This enables us to see all of the files in our project grouped by type. Let’s open the Source Files node on the treeview control in the File View window. You should see two files in the node: ex01_ch4.cpp and stdafx.cpp. The contents of the file should display in your development environment’s code window. Now, add the following code to your open file so that it looks like the code listed here: // ex01_ch4.cpp : Defines the entry point for the console application. // #include "stdafx.h" //variable declarations int iPublicVar = 0; int main(int argc, char* argv[]) { changeVar(); 41 Morrison_001_224 8/22/00 11:53 AM Page 42 Chapter 4 printf("iPublicVar == %d.\n",iPublicVar); printf("The sum of 5 and 4 is %d.\n",addNums(5,4)); changeVar(); printf("iPublicVar == %d.\n",iPublicVar); changeVar(); printf("iPublicVar == %d.\n",iPublicVar); return 0; } //function to add numbers int addNums(int iNum1, int iNum2) { int iSum = 0; iSum = iNum1 + iNum2; return iSum; } //sub to change a variable. void changeVar() { iPublicVar++; } Figure 4-3. The Class View and File View tabs 42 Morrison_001_224 8/22/00 11:53 AM Page 43 C++ 101 Figure 4-4. Visual Studio in File View with the project ex01_ch04 loaded Your development environment should now look like the environment shown in Figure 4-4. Remember from Chapter 3 that the #include preprocessor directive copies the contents of one file into another at compile time. Well, guess what the first line of code in our program does. Yep, #includes stdafx.h. The reason we do this is because we want to include the contents of stdafx.h in our source code file before it gets compiled. (Makes sense, right?) Generally, header files contain variable declarations, constant definitions, or preprocessor directives (including #include; yes you can have nested #includes). One of the most common elements found in a header file is a function prototype. Function prototyping is the first one of those “foreign concepts” I mentioned earlier in this chapter, so let me explain. In Visual Basic we always use Option Explicit, which forces us to declare variables in our program before we use them. C++ takes this idea one step further by requiring function prototypes in our program before we use them. But, before we can go too far I must explain how a C++ function is organized. The form of a C++ function is as follows: 43 Morrison_001_224 8/22/00 11:53 AM Page 44 Chapter 4 returnValueType functionName([arguments]){functionBody} This is in contrast to a Visual Basic function, which takes the following form: [Public or Private] Function functionName([arguments]) ReturnValueType Let’s look at two examples of a function prototype: int addNums(int iNum1,int iNum2); //first function prototype void changeVar(); //second function prototype These are both examples of function prototypes. You may notice that the prototypes don’t contain any executable code. So then, what is the point of having a function prototype? If you remember from Chapter 3, we said that when the compiler runs, it generates an error if it hits an unrecognized symbol (such as an undefined variable or function). The way we define a symbol is by either defining a variable or writing a function prototype. What a prototype does is tell the compiler that there is a function matching its description somewhere in the program. Once this is done, the compiler is satisfied. The next step is for the linker to go and actually find the executable code that implements the function prototype and put it into the program. Think of this process as similar to having a credit card. You may have a credit card (function declaration) in your possession, which suggests that you will be able to pay for a purchase. However, when you go to the counter the cashier (the linker) will check with the credit card company to verify that you have available credit for your purchases (function’s implementation). If she finds that you do (the implementation is found), you can take your purchases and go (your program gets linked into an .exe). On the other hand, if your credit card is rejected (no function implementation is found), you will not be able to purchase your items (your program will not be linked into an .exe). Now, let’s look at our header file, stdafx.h, after you’ve added the function prototypes for the functions added earlier to the file ex01-ch4.cpp: // stdafx.h : include file for standard system include files, // or project specific include files that are used frequently, but // are changed infrequently // #if !defined(AFX_STDAFX_H__7AC25963_B4D4_11D3_BC89_901151C10000__INCLUDED_) #define AFX_STDAFX_H__7AC25963_B4D4_11D3_BC89_901151C10000__INCLUDED_ #if_MSC_VER > 1000 #pragma once 44 Morrison_001_224 8/22/00 11:53 AM Page 45 C++ 101 #endif // _MSC_VER > 1000 #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers #include <stdio.h> //function prototypes int addNums(int iNum1,int iNum2); void changeVar(); // TODO: reference additional headers your program requires here //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations immediately before the previous line. #endif // !defined(AFX_STDAFX_H__7AC25963_B4D4_11D3_BC89_901151C10000__INCLUDED_) You can see the function prototypes for our two functions, addNums() and changeVar(). We also defined one public variable, iPublicVar, whose initial value is set to 0. Because we #included stdafx.h in our source code file, ex01_ch4.cpp, our function prototypes are recognized before the compiler runs into our function calls in our program. NOTE: The order of the function prototypes and function calls is important. The function prototype must appear before any call to the function that it represents. It is for this reason that function prototypes usually live in header files that get #included into a source file where the actual function is called. Continuing on with our program, we see the line after our #include is the actual entry point of our application. We know this because of the fact that it is the definition of the function main(). Let’s look at it: int main(int argc, char* argv[]) We know from our previous definition of a C++ function that the left most symbol, int, indicates that our function will have a return value type of int. And that the next symbol, reading from right to left, is our function’s name; main. The next two sets of declarations, int argc and char* argv[], are our programs arguments. The first argument tells us the number of arguments that were passed to our program, while the second argument is an array of char* that actually con- 45 Morrison_001_224 8/22/00 11:53 AM Page 46 Chapter 4 tains the arguments. We will leave this discussion until after we have discussed pointers, but suffice it to say that with these two parameters we can accept any number of arguments from the command line. Curly Braces { } In case you’re not familiar with the term, scope identifies a certain section of code as being a cohesive unit. In other words, anything defined in that particular scope is visible to all other elements of that scope. If a variable is declared inside of a function, it is said to have Function Scope. If a variable is declared in a public code module not contained within a function, it is said to have Global Scope. When a scope is exited (such as a function returning), all items declared in that scope (such as variables) disappear as well. In C++ you can create scope by using curly braces ({ }). If you declare a variable after a curly brace, it will only be in scope until the closing curly brace is encountered. In some cases a curly brace is optional, as in an if statement, but I find it much more clear to always enclose a related group of statements together in a set of curly braces. (See “The if Statement” later in this chapter for more information on if statements.) Semi-Colon ; The next line in our program is a call to changeVar() followed by a semi-colon. A semi-colon in C++ is used to terminate a statement. This is exactly the same as in Visual Basic, except that Visual Basic uses an end of line character to terminate a statement. So does that mean that every line of code in C++ will be terminated with a semi-colon? No. Consider the following: #define CALL_CHANGE_VAR changeVar() This line of code sets CALL_CHANGE_VAR as an alias for the text “changeVar().” So why doesn’t this line need to be terminated with a semi-colon? This line is not an executable statement. Remember from Chapter 3 that #define is a preprocessor directive, not a compiler directive. Before our program compiles, the preprocessor will go through our source code and replace all occurrences of CALL_CHANGE_VAR with the text “changeVar().” I would use this in code as follows: { //Other program statements here CALL_CHANGE_VAR; //Other program statements here } 46 Morrison_001_224 8/22/00 11:53 AM Page 47 C++ 101 Now after the preprocessor runs, this code fragment would look like this: { //Other program statements here changeVar(); //Other program statements here } Just as in Visual Basic, in C++ there is no requirement that a statement be on a single line. Consider the following statement: int x = 2 + 2; I could have just as easily (and correctly) written it as: int x = 2 + 2; An equivalent statement in Visual Basic would look like this: Dim x As Integer x _ = _ 2 _ + _ 2 As you can see, C++ does not require a “line continuation character” (like Visual Basic does) to continue a program statement to the next line. Use this technique judiciously, however, as it can make the source code more difficult to read. printf() Our next program statement: printf("iPublicVar == %d.\n",iPublicVar); makes use of the Standard C++ Library function, printf(). This function is used to print to the standard output device (which is usually a computer monitor’s 47 Morrison_001_224 8/22/00 11:53 AM Page 48 Chapter 4 screen). The function prototype for this function is in the stdio.h header file, which we #included in our program inside of our header file, stdafx.h. The printf() prototype looks like this: int printf(const char *, ...); Now, if we dissect this prototype, we can see that printf() returns a value of type int (which indicates the number of characters written to the screen, or a negative number if there was an error). You may notice that the arguments section of our function prototype look a bit odd. This is because we have just encountered two new C++ language elements, one of which is the ellipsis (the three periods next to each other in the place of the functions second parameter). An ellipsis is simply a matter of notation that tells the compiler that this function will accept any number and type of arguments. The compiler then disables typechecking for the additional parameters (because there is nothing to check against). This is considered bad programming practice in most cases. However, there are some functions that necessitate using the ellipsis. I will not go into the specifics of implementing a function that uses an ellipsis, but you will probably see them from time to time. For example, printf() is one of those functions that makes legitimate use of the ellipsis. To see why, let’s continue examining our call to printf(): printf("iPublicVar == %d.\n",iPublicVar); The first parameter we pass to printf() in the previous code is an ordinary string literal. There is, however, something that looks a little odd contained within that ordinary string literal. First, you may have noticed a %d in the place where we want our variable’s (iPublicVar) value to be displayed. Then at the end of our string literal you may also have seen a \n. This brings to the surface two more new C++ language features: format specifiers and escape sequences. First let’s examine the area of format specifiers. Format Specifiers The f in printf() stands for format, which means printf(), prints a formatted string. In fact, printf() is somewhat similar to the Format function in Visual Basic. However, to achieve the effect of our call to printf() with Visual Basic, we have to use concatenation. Let’s see how this looks: 48 Morrison_001_224 8/22/00 11:53 AM Page 49 C++ 101 Dim strMessage As String Dim lngPublicVar As Long Public Sub Main lngPublicVar = 5 Print "lngPublicVar = " & lngPublicVar & "." & vbCrLf End Sub NOTE: Concatenation is the process of joining various string literals and variables together to form one string. With these few lines of code we concatenate the variable lngPublicVar to the string literal "lngPublicVar = ". We also add a vbCrLf to the end of the line to ensure that we send a carriage return/line feed to the screen. printf() produces the same result, but using format specifiers instead. printf() actually takes the format specifiers from our string literal and replaces them with the values of the variables passed in to the ellipsis parameter. Remember, because we are not restricted by the number of arguments we can pass to printf(), we could actually use printf() as follows: printf("iPublicVar == %d.%d%d\n",iPublicVar,iPublicVar,iPublicVar); This would produce the following output (assuming that iPublicVar equals 5): "iPublicVar == 5.55" You may be wondering why I put a %d as the format specifier. Well, %d tells printf() to interpret the variable passed to it as a decimal number (hence, d for decimal). NOTE: There are many other format specifiers available in C++. All of the C++ format specifiers and their respective meanings and uses are available in the online documentation provided with Visual Studio 6.0. We will be seeing some of the additional format specifiers used throughout the rest of the book though. Escape Sequences Much like the format specifiers we just looked at, escape sequences tell C++ functions how to format output. In the above example of printf: 49 Morrison_001_224 8/22/00 11:53 AM Page 50 Chapter 4 printf("iPublicVar == %d.%d%d\n",iPublicVar,iPublicVar,iPublicVar); The \n after the three %d format specifiers is the “newline” escape sequence. It tells the printf function to force the output device to place a newline (or carriage return) in the place the \n escape sequence occupies. There are many escape sequences available in C++. They are all listed with their meanings and uses in the Visual Studio Help File. The next line in our program: printf("The sum of 5 and 4 is %d.\n",addNums(5,4)); makes another call to printf(), but instead of passing a variable as the second parameter, we pass the results of a function call, addNums(5,4). This may seem strange, but the statements in the second parameter position of a call to printf() are always fully evaluated before being passed to the actual call, printf(). For example, our function, addNums(), evaluates to the sum of the parameters passed to it. For all intents and purposes, printf() doesn’t know the difference between a function in its parameter list and an actual hard coded value. As far as printf() is concerned, the following two calls are identical: printf("The sum of 5 and 4 is %d.\n",addNums(5,4)); printf("The sum of 5 and 4 is %d.\n",9); Most of the rest of the code in our program is just a repeat of earlier calls with one exception: return 0; } The keyword return is used to exit a function. In this case we tell the program to exit the function main() and return a value of 0. To exit a function that does not return a value, you just call return with no value as such: return; Figure 4-5 shows the output of our program. Granted, this example is fairly useless as an application, but it explains the structure of a C++ program pretty well. Operators In order to benefit from C++ we are going to perform various operations on our data, so it’s important that you learn about some of the operators available in C++. I listed the most relevant C++ operators, their Visual Basic equivalents (where applicable), and a description of the functionality they provide in Table 4-2. A complete list of C++ operators is available in the C+ help file. 50 Morrison_001_224 8/22/00 11:53 AM Page 51 C++ 101 Figure 4-5. The output of ex01_ch04.exe Table 4-2. C++ Operators C++ OPERATOR VB EQUIVALENT DESCRIPTION + * / % ++ -= == != > < >= <= & && | || ~ ! += -= *= /= + * / % N/A N/A = = <> > < >= <= And N/A Or N/A Not N/A N/A N/A N/A N/A Addition Subtraction Multiplication Division Remainder from a division (modulus) Increment operator Decrement operator Assignment Equality Inequality Greater than Less than Greater than or equal to Less than or equal to Bitwise AND Logical AND Bitwise Or Logical Or Bitwise Not (complement) Logical Not Addition and assignment Subtraction and assignment Multiplication and assignment Division and assignment 51 Morrison_001_224 8/22/00 11:53 AM Page 52 Chapter 4 In order to get a better feel for the operators in C++ let’s build a small program that will use a variety of the operators as well as some of the logic control structures such as for-loops and select-case. Don’t expect to learn too much about good programming practice from this application, just use it to learn about the use of the C++ operators. Again, I am only reviewing a few of the most commonly used operators. Open a new console application project in Visual Studio and copy the contents of the source file ex02_ch4 and the header file stdafx.h, which are listed here. (This project is also located on the Web site that accompanies this book at www.apress.com.) The contents of the file ex2_ch4.cpp are // ex02_ch4.cpp : Defines the entry point for the console application. // #include "stdafx.h" int i1 = 0; int main(int argc, char* argv[]) { //increment i1 by 5 printf("Begin for-loop with (i1 == %d)...\n",i1); for (int a = 0; a < 5; a++) { printf("i1 == %d.\n",i1++); } printf("End for-loop with (i1 == %d)...\n\n",i1); //decrement it back down to 0 printf("Begin do-while-loop with (i1 == %d)...\n",i1); do { printf("i1 == %d.\n",i1--); }while (i1 > 0); printf("End do-while-loop with (i1 == %d)...\n\n",i1); //increment i1 by 5 printf("Begin for-loop with (i1 == %d)...\n",i1); for (int b = 0; b < 5; b++) { printf("i1 == %d.\n",++i1); 52 Morrison_001_224 8/22/00 11:53 AM Page 53 C++ 101 } printf("End for-loop with (i1 == %d)...\n\n",i1); //increment i1 by multiples of 5 printf("Begin for-loop with (i1 == %d)...\n",i1); for (int c = 0; c < 5; c++) { printf("(i1 += 5) == %d.\n",(i1 += 5)); } printf("End for-loop with (i1 == %d)...\n\n",i1); //bitwise and logical operations printf("Begin logical and bitwise operations with (i1 == %d)...\n",i1); printf("(i1 & 2) == %d\n",i1 & 2); //bitwise printf("(i1 && 2) == %d\n",i1 && 2); //logical printf("(i1 | 2) == %d\n",i1 | 2); //bitwise printf("(i1 || 2) == %d\n",i1 || 2); //logical printf("(!i1) == %d\n",!i1); //logical printf("(~i1) == %d\n",~i1); //bitwise printf("End logical and bitwise operations with (i1 == %d)...\n",i1); return 0; } The contents of the file stdafx.h are // stdafx.h : include file for standard system include files, // or project specific include files that are used frequently, but // are changed infrequently // #if!defined(AFX_STDAFX_H__ED10E577_1CBD_4B53_8CB3_FC5D51278B80__INCLUDED_) #define AFX_STDAFX_H__ED10E577_1CBD_4B53_8CB3_FC5D51278B80__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from #include <stdio.h> //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations immediately before the previous line. #endif // !defined(AFX_STDAFX_H__ED10E577_1CBD_4B53_8CB3_FC5D51278B80__INCLUDED_) 53 Morrison_001_224 8/22/00 11:53 AM Page 54 Chapter 4 Figure 4-6. ex02_ch4.exe Figure 4-6 displays the output from the program when it is run. There certainly is a lot going on in this little program. First and foremost there are a lot of mathematical operations. Now I won’t spend time going over the obvious operations such as addition and subtraction, but I will briefly explain the operators that do not have a Visual Basic equivalent. The ++ and -- Operators The origin of the name for the C++ programming language comes from the ++ operator. The ++ operator is a unary operator that increments the variable that it precedes or follows. The C programming language was an improvement to the proprietary Bell Labs programming language “B”. (B presumably stands for Bell.) And C++ was an improvement on the C programming language. Therefore, C++ should theoretically be called “D.” However, the inventors of C++, being the jokesters they are, decided to increment the C programming language by calling it C++. 54 Morrison_001_224 8/22/00 11:53 AM Page 55 C++ 101 There is an important point to be made about the placement of the ++ operator. If the ++ operator comes before the operand, such as: x = ++i; the value i will be incremented before its current value is assigned to x. This is called a prefix operator. The postfix version of the ++ operator (or one that comes after the operand) is used as in the following: x = i++; In this case, x will be assigned the current value of i before it is incremented. This is an important difference to note, as you can see from the example. Our first for-loop (we will discuss the for-loop statement later in this chapter) starts at 0 and ends with 4 by using the postfix version of the ++ operator. However, the second for-loop using the same exact logic and data, prints the values 1 through 5 because the ++ operator is a prefix. Following is the C++ operation followed by the equivalent Visual Basic code necessary to accomplish the same output. The C++ version (postfix) is x = i++; The Visual Basic version (postfix) is x = i i = i + 1 The C++ version (prefix) is x = ++i; The Visual Basic version (prefix) is i = i + 1 x = i The - and -- operators follow the same exact principles as the + and ++ operators, but for subtraction. 55 Morrison_001_224 8/22/00 11:53 AM Page 56 Chapter 4 Binary Operators You may not realize that Visual Basic does not have any boolean operators. Daft you say? Well it’s true (no pun intended). To prove my case let’s do a test. In Boolean logic, “Not 1” should equal 0 (or false). However, if I run this statement in Visual Basic, I get the result of –2. The reason is that Visual Basic does only bitwise operations. In most cases this doesn’t make a difference, but you do need to be careful. Consider the following Visual Basic code: Option Explicit Private Sub Form_Load() 'Check the return value of the function If Not GetVal Then MsgBox "Function failed." Else MsgBox "Function succeded." End If End Sub Public Function GetVal() 'Return what most API functions will return as 'true' GetVal = 1 End Function Following the logic of this code, you can see that we are checking the return value from our call to GetVal() and continuing or terminating our program based on what that return value is. Now, because our version of GetVal() always returns 1 (or true, as any non-zero value is true according to Boolean logic), we would expect the following statement: If Not GetVal() to always evaluate to true (or “not false”). However, in this code example it does not. If you run this code, you will see that our if statement evaluates to false (or “Not true”). Let’s examine why this happens. Our GetVal() function returns 1, which is binary 00000001. When we apply the Not operator to the return value of 1, we are not doing a logical Not, but a bitwise Not operation. The bitwise Not of the binary value 00000001 equates to 56 Morrison_001_224 8/22/00 11:53 AM Page 57 C++ 101 11111110, which equates to the decimal value (-2). The value of True in Visual Basic is equal to decimal (-1). And because (-1) and (-2) are not equal, our if statement will always fail. TIP: If you don’t understand bitwise operations, run, don’t walk, and learn basic binary mathematics. It will prove invaluable in your understanding of computer programming and computers in general. The reason that I point this out is that most application program interface (API) functions use 1 as their return value to indicate True and 0 to indicate False. Imagine what would happen if GetVal() was an API function. This could lead to some very hard to find bugs. Let’s look at a more realistic example of how this could happen: Option Explicit Public Declare Function SetWindowText Lib "user32" Alias "SetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String) As Long Public Sub main() 'Variable to catch the return value of the API call Dim lngRetVal As Long Load Form1 'Call SetWindowText to change the text in our window lngRetVal = SetWindowText(Form1.hwnd, "My Title has Been Changed") 'Check the return value of SetWindowText If Not lngRetVal Then 'SetWindowText returned false MsgBox "Error: Could not change the window's title." Else 'SetWindowText returned true so.... 'Continue with the program End If Form1.Show End Sub 57 Morrison_001_224 8/22/00 11:53 AM Page 58 Chapter 4 This simple example shows a very real bug. The return value of SetWindowText is documented to be “non-zero on success,” which means that SetWindowText is successful if it returns any value other than zero. Now when Visual Basic evaluates an expression such as: Dim x As Integer x = 1 If x Then it will evaluate the if statement as true. This is because in this grammar, Visual Basic is only checking for the absence of zero. However, the following statements would evaluate as false: Dim x As Integer x = 1 If x = True Then This is because Visual Basic will now compare x (1) and True (-1) and (correctly) return the fact that they are not equal; such that: (1 = (1 = True)) = False Fear not. There is a simple solution to this problem. Always assign values that are logically either true or false to a Boolean variable. Take our previous code for example. If we had made the small change noted here in bold, our program would run correctly: Option Explicit Public Declare Function SetWindowText Lib "user32" Alias "SetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String) As Long Public Sub Main() 'Variable to catch the return value of the API call Dim lngRetVal As Boolean 'Changed from Long to Boolean Load Form1 'Call SetWindowText to change the text in our window 58 Morrison_001_224 8/22/00 11:53 AM Page 59 C++ 101 lngRetVal = SetWindowText(Form1.hwnd, "My Title has Been Changed") 'Check the return value of SetWindowText If Not lngRetVal Then 'SetWindowText returned false MsgBox "Error: Could not change the window's title." Else 'SetWindowText returned true so.... 'Continue with the program End If Form1.Show End Sub The reason is that Visual Basic coerces any non-zero value to true upon assignment to a Boolean type variable. We could accomplish the same thing by putting the CBool function around our lngRetVal variable when it was dim-ed as a Long. Here’s the bottom line: in C++ you should always use the Boolean operators for comparison unless you actually need to perform a bitwise operation. For instance, if I wanted to compare the return value of a function for success I would do the following: if (!SetWindowLong(hWnd, "My App")); This statement uses the logical Not operator, which will give us the desired effect of checking for success or failure. However, the following would not: if (~SetWindowLong(hWnd, "My App")); Using this form of the Not operator would put us in the same boat that we were in with Visual Basic. Assignment and Equality Operators The last group of operators that I discuss is the assignment and equality operators. One of the most frequent bugs I used to create in my early C++ programs was a very innocent looking one: if (var = condition) { //Do something interesting } 59 Morrison_001_224 8/22/00 11:53 AM Page 60 Chapter 4 Looks pretty innocent, right? (Forget that we don’t know the exact syntax of an if statement yet.) Here is the equivalent code in Visual Basic: If var = condition Then 'Do something interesting... End If Do you see the problem yet? No? Let’s look at the C++ version a little closer. It appears at first glance that I am asking if the value var is equal to the value condition. However, what I am really asking is, if the return value of the statement var = condition is true or false. You see, in Visual Basic the equals sign means both assignment and equality. It uses the context of the call to decide which operation to use. In C++, however, the symbol “=” means to assign the value on the right side of the equals sign to the storage location on the left. So the question still remains, “What does var = condition really equal?” Let’s look at the following example: printf("%d\n",i1 = 10); printf("%d\n",i1 = 5); printf("%d\n",i1 = 3); This program will produce the values 10, 5, and 3, respectively. The answer to our question is: the return value of an assignment operation is always the value on the right hand side. Assuming that i1 is equal to zero at this point in time, the following statement will return 0 (or false): i1 = 0; You should be able to see the possibility for hard to find bugs here. If you had built a conditional statement based on this statement being true, you’d be in big trouble. Here’s a simple trick to alleviate this kind of bug when you are checking for a constant value. Always put your constant value on the left side of the equals sign as such: 0 = i1; This way, if you accidentally put in the assignment operator by mistake, your program won’t even compile. The reason is that you can’t assign a value to a constant. Okay, so how do we check for equality? With the == operator. Here is the correct version of our previous statement: 60 Morrison_001_224 8/22/00 11:53 AM Page 61 C++ 101 0 == i1; That’s it. Just put an extra equal sign next to the old one and we’re done. Assignment and … Operators The other types of operators that are new in C++ are the “two-for-one” operators, such as +=. These operators do two operations in one shot. Consider the following statement: x += 10; To see what this statement is doing, let’s look at an equivalent Visual Basic statement: x = x + 10 Pretty simple, right? All that these types of operators do is combine two operations into one. Refer to Table 4-2 and the sample program, ex2_ch4, for more uses of these operators. Loops and Control Statements C++ has loop and control facilities just like Visual Basic, although the syntax is different. We’ll go through each type of these logic control structures and their respective use. The if Statement The if statement is probably the most widely used control statement of any programming language. Regardless of the programming language implementing it, they all work pretty much the same. In Visual Basic, an if statement looks like this: If x = y Then 'Do something End If All of the statements between If and End If are executed if the conditional statement is True. The same if statement in C++ looks like this: If (x == y) //do something; 61 Morrison_001_224 8/22/00 11:53 AM Page 62 Chapter 4 Now this is where the stylistic approach to programming comes into play. I feel that the previous line is a little unclear. I would, and always do, write my if statements with a set of curly braces surrounding the conditional statements as follows: if (x == y) { //do something; } By using the curly braces I am creating a scope for the if statement. I feel that this makes the code easier to read. For Loop The C++ for-loop construct is pretty much identical in function to the Visual Basic version. To get a feel for the similarities and differences in the two let’s look at an example. The Visual Basic version is Dim i As Long For i = 0 To 100 'something interesting here Next i The C++ version is for (int i = 0;i < 100;i++) { //Something interesting here. } You already know what the Visual Basic version does so I’ll jump right into the description of the C++ section. The first line starts with our declaration of the for loop. We do this by using the keyword for. Now just as in the Visual Basic for-loop, we must give a starting value for the loop. We use the statement int i = 0 to accomplish this. Notice that unlike in Visual Basic, in the C++ version we can actually declare and initialize the iterating variable (in this case i) right inside the first parameter of the for-loop. Consequently, the variable i will have scope only in the for loop. Now all of the arguments of the for loop are separated by a semi-colon. The actual definition of the for-loop arguments are as follows: 62 Morrison_001_224 8/22/00 11:53 AM Page 63 C++ 101 for (iterting variable;conditional statement;action[s]) We satisfied the first parameter with our declaration and initialization of the variable i. Our conditional statement is i < 100 which means “while i is less than 100,” but I am sure you have already figured that out. Our final parameter is our action clause, which we have denoted as i++ This simply means that each time the loop iterates the value of i will be incremented by one. Now we aren’t tied to just iterating based on i increasing by one. I could have just as easily written a loop as follows: for (int i = 0;i < 100;i+=5) { //Loop statements here } This is identical to the following for-loop in Visual Basic: For I = 0 To 100 Step 5 'Loop statements Next I Do-While Loop The do-while-loop is similar to the Visual Basic version, just as was the for-loop construct. Let’s look at an example of these two constructs in the aforementioned languages. The Visual Basic version is Do 'Statements Here If x > 10 Then Exit Do End If Loop While True 63 Morrison_001_224 8/22/00 11:53 AM Page 64 Chapter 4 The C++ version is do { if (x > 10) { break; } }while (true) Notice the use of the keyword, break. This keyword essentially tells the program to “break”-out of the loop. There is also a continue keyword that could be used in the place of break. The difference in the two is that break actually starts executing the next line after the loop statement, but continue causes the program to ignore the statements after the continue keyword and begin the next iteration of the loop immediately. Break is the equivalent of the Exit keyword in Visual Basic. Consider the following Visual Basic code: Dim x As Integer Do If x > 10 Then Exit Do End If x = x + 1 Loop For x = 0 To 100 If x > 10 Then Exit For End If x = x + 1 Next Both the Exit Do and Exit For are used to exit loops. In C++, it is not necessary to explicitly state which structure you are breaking out of. Switch-Case Although the switch keyword may not look familiar to you, it is the first cousin of the Select-Case construct in Visual Basic. The switch-case construct takes the following form: 64 Morrison_001_224 8/22/00 11:53 AM Page 65 C++ 101 switch (condition) { case case[n]: [break]; case case[n + 1]: [break]; } To continue our previous compare and contrast format, let’s look at the control structure in both languages. The Visual Basic version is Dim I As Long I = 10 Select Case I Case I = 1 'Do something Case I = 3,4,5 'Do something Case 10 ' Do something great Case Else ' Default Action End Select The C++ version is int I = 0; switch (I) { case 1: //Do something break; case 3: case 4: case 5: break; case 10: //Do something great default: //default action } 65 Morrison_001_224 8/22/00 11:53 AM Page 66 Chapter 4 Notice that the structure of the statement is pretty much the same as the Visual Basic version, with a couple of exceptions. The first exception is that unlike in Visual Basic, in C++ we must explicitly break out of the switch statement by using the break keyword. Second, we can check several conditions by simply listing them with no break between them as we did with 3, 4, and 5. The third and last exception is that we use the keyword default as our “catch-all” condition as opposed to the Case Else that is used in Visual Basic. Comments I saved this topic for last in this chapter because it is an important one. In C++ it is extremely important to write comments in your code, because there are so many ways to represent the same operations and manipulations of a program’s data. Believe me, I have spent hours and hours trying to understand other programmers’ uncommented, cryptic code only to find out that the time spent could have been reduced to nothing had the code included even one comment. (I’ll leave you and your conscience to deal with the matter from here.) Following are two ways to specify comments in C++: //This is a comment line. It works just like VB. /* This is the C-style comment block. Unlike VB even the comments here are blocked. Nothing will be interpreted as code until the closing tag is encountered like this */ And that’s it. Either start a line with the // symbol to comment only that line (this works just like the apostrophe in Visual Basic), or use the /* */ pair to enclose a group of comments (I wish that Visual Basic had an equivalent for this one!). Conclusion Although far from all-inclusive, this chapter should have given you a good induction into the basics of C++. I suggest supplementing this chapter with the first few chapters of the definitive C++ guide, The C++ Programming Language by Bjarne Stroustrup, the inventor of C++ (2000, Addison-Wesley). It is available from any good technical book source. Other than that, get ready to attack the C++ language. In the next few chapters we will continue our journey into the world of C++ by learning about pointers, classes, and templates. We will also pick up additional techniques and insights regarding the things we have already learned as we go along. Are we having fun yet?! 66