www.gameinstitute.com Introduction to C and C++ : Week 1: Page 1 of 42 Game Institute by Stan Trujillo Week 1 Introduction to C and C++ www.gameinstitute.com Introduction to C and C++ : Week 1: Page 2 of 42 © 2001, eInstitute, Inc. You may print one copy of this document for your own personal use. You agree to destroy any worn copy prior to printing another. You may not distribute this document in paper, fax, magnetic, electronic or other telecommunications format to anyone else. This is the companion text to the www.gameinstitute.com course of the same title. With minor modifications made for print formatting, it is identical to the viewable text, but without the audio. www.gameinstitute.com Introduction to C and C++ : Week 1: Page 3 of 42 Table of Contents Introduction 4 Fundamentals 4 C++ Language Features 6 Development tools 7 C++ Mechanics 8 The preprocessor 9 The Compiler 10 The Linker 11 Compilation Process 11 Project files 12 Release and Debug Builds 13 Windows applications types 13 Hello World 14 Keywords 15 Code Formatting 16 Naming Conventions 17 Compile and Run 17 Comments 18 Data Types 19 The IntMult Sample 23 Functions 25 The Celcius Sample 27 Conditionals 30 Switch Statements 33 Loops 34 The Grid Sample 35 The Ascii Sample 38 More About Functions 40 Exercises 42 What’s next? 42 www.gameinstitute.com Introduction to C and C++ : Week 1: Page 4 of 42 Introduction A GameInstitute course by Stan Trujillo Although it certainly wasn’t always the case, there is a great deal of information on C++ available in the form of books, courses, and tutorials. This course differs from this existing body of information in several ways. First, as a GameInstitute course, the goal of this course is to teach C++ to programmers who intend to pursue game programming. Unlike most C++ courses, the final assignment is not to write a student enrollment system or a sports statistics application. Our goal is to write games, so this course strives to build the foundation necessary for game programming. Another way in which this course differs is that, in addition to covering language features, we’ll talk about how these features are typically used in game programming. Some C++ features that are rarely used in games are covered summarily, while other features those that are use in games extensively—are covered in detail. Finally, because games are complex applications that involve the mixing and coordination of many different types of systems (graphics, sound, user input, etc.), and because the structure of game applications differs significantly from the typical business or web application, we will focus on using C++ to define the game application as a whole. The alternative is to cover each topic independently, leaving you with the task of combining all of the topics into a single application, which usually serves to frustrate as much as to educate. Still, before we can successfully tackle most of the larger issues that face game programmers, we must achieve a certain level of familiarity with C++. We’ll therefore start with three lessons that focus on the C++ language. In the latter three lessons, we’ll use this foundation to explore game programming. Learning to program games requires an understanding of programming concepts and programming languages. We’ll cover both in this course, but the conceptual portions are fairly brief and to the point. This is a C++ course, and as such the primary focus is the C++ code required for game programming. The material is therefore full of code snippets and sample programs. This code is carefully explained, and every effort is made to keep the complexity to a minimum. Also, the important portions of the code typically appear in bold to draw attention to the specific commands or language syntax to which the surrounding material refers. Fundamentals In some ways, computers have changed radically since they were first invented. From early models, which occupied huge rooms and yet provided far less performance and storage space than today’s Palm Pilot, computers have gotten smaller and faster at an amazing pace. Computer languages have changed too. During the early years of computing there was only one computer language: machine language. The programmers that worked with these machines were working in the machine’s native tongue—a language in which very simple instructions such as “copy”, “add”, and “multiply” were represented as numeric codes. The data that was manipulated by these instructions was also referred to in numeric form—either as literal numeric values, or as the memory addresses where the data was stored. The machine language instructions, or codes, were used to manipulate the data stored at various memory addresses, and entire programs were written in this fashion. www.gameinstitute.com Introduction to C and C++ : Week 1: Page 5 of 42 Writing programs in machine language was both simple and complicated. It was simple because you were working with just a handful of instructions and a slew of memory addresses at which data could be stored and retrieved. But machine language was complicated because each program is nothing but numbers. Some numbers represent instructions, some represent memory addresses, and some represent literal values. As a result, writing, deciphering, and maintaining these programs was time consuming and painstaking work. Humans just aren’t good at manipulating pages and pages of numbers. The problem is that computers and humans speak very different languages. Forcing humans to speak the native language of the machine, although necessary at one time, is very taxing for humans. In order to write large, reliable, and easily upgradeable software systems, humans needed to design a better way to communicate with the computer. Today there are a slew of computer languages, all of which address the issue of human/machine communication in its own way. In all cases these languages are more natural for human use due to the use of abstraction. Even very detail-oriented languages, such as assembly language, use abstractions. Assembly language provides human-readable names to be used for both instructions and data, abstracting the underling numeric values. Other languages, such as Visual Basic, Java, C, and C++ also use abstractions, but to a much higher degree. The more abstract the language, the less the language is like machine language. Highly abstract languages cater to the way that humans think, and not to the way that computers operate. With a modern language, programmers don’t have manage numeric codes, but more importantly, are not limited to the commands that the hardware provides. These languages allow new, complex instructions to be defined that are just as easy to use as the native computer hardware instructions. Instead of being limited to commands such as “copy”, “add”, and “multiply”, these languages can be extended to include very powerful and specific commands such as “update database”, “fire weapons”, or “draw explosion”. Computer hardware has gotten much, much faster. And computer languages have gotten much, much more powerful. But, while computer hardware now provides millions of times more performance than early hardware, it really hasn’t changed that much. It still uses very simple numeric instructions that indicate operations such as “copy”, “add”, and “multiply”, and it still uses memory addresses to store and retrieve data. How can languages have evolved to be so abstract when the underlying hardware remains largely the same? The answer is that the computer now performs much more work than it used to. With machine language, the program, or source code that the programmer wrote was exactly the same as that which the computer executed. The source code was the program. With all other languages, the source code is not the same as the instructions that the computer executes. Instead there is at least one step that is required to convert source code into executable form. This is accomplished in one of two ways, depending on the language being used. Languages such as Java and early versions of Visual Basic, for example, are interpreted languages. This means that in order for source code to be executed, an interpreter is required. These interpreters are themselves programs—which are specialized to read source code and convert it into a set of operations that are in turn executed by the hardware. Alternatively, languages such as C, C++, and modern versions of Visual Basic are compiled languages. These languages require a program called a compiler that reads source code and converts it into machine language. Interpreted languages, because they require source code to be converted at runtime (at the time that execution is to take place), is slower than the compiled equivalent. Compiled languages, by requiring that the highly complex conversion process take place before execution, provide better runtime performance. www.gameinstitute.com Introduction to C and C++ : Week 1: Page 6 of 42 In both cases, the computer is expected to perform extra steps to convert source code into something that the computer understands. Now, instead of learning the computer’s native tongue, we write software in a language that makes sense to us, and we leave the task of conversion to the computer. The computer does more work than it used to, and humans do less. This arrangement works nicely because computers are very good at the type of work that is required to convert source code to machine languages, and, because computers are faster than ever, there is plenty of extra processing power for these conversions. Nevertheless, it is not as though we are free to explain to the computer what we want in plain English. We still must learn a computer language such as C++. And, while easier to understand than machine language, understanding C++ takes some practice. Modern computer languages are a compromise between machine language and truly human languages. C++ Language Features So far we’ve learned that C++, like all other programming languages except machine language, derives its power through abstraction, and at least part of its speed from the fact that it is a compiled language. Now let’s talk about how C++ compares to other languages, and what makes it a natural choice for game programming. C++ is sometimes referred to as a mid-level language a reference to its level of abstraction. C++ is more abstract and therefore higher level than assembly language, and yet less abstract and lower level than a scripting language such as JavaScript. This mid-level status is part of the reason that C++ makes a good game programming language. C++ is low-level enough to allow very detailed instruction, and yet high- level enough to allow for very complex concepts to be expressed and organized. Both are important for game programming, which must address the computer hardware at a very low-level, and yet express and manage high-level concepts such as the imaginary universe in which each game takes place. C++ gets much of its low-level ability from its predecessor C, which allows data to be manipulated with almost as much control as that provided by assembly language. Combined with higher-level data handling functionality, this means that data can be manipulated either in large, complex data structures, or down to the most fundamental level: the bit. Likewise, both C and C++ are procedural languages, meaning that they both require that code be grouped into functions a function being a set of one or more instructions that the computer is to execute. By providing support for the definition of functions and a variety of data types, C and C++ provide the basic building blocks upon which any program can be written. In this respect languages such as C and C++ are not unlike machine language except that names can be given to both functions and data. But what if, instead of using a name to represent data, we wanted to use a memory address to indicate where the data resides, as machine language does? This would allow us to control not only the value of the data, but it’s location in memory as well. C and C++ support this ability with pointers. A pointer is special data type that contains a memory address. It points to data by indicating its address. Pointers have a bad reputation. They are often mentioned as the primary reason why C and C++ are difficult languages to learn. But pointers are very popular with those who have grasped the concept. The truth is that pointers can be overused. Using pointers just for the heck of it tends to obfuscate code, and is a common practice for insecure programmers. There are situations, however, where pointers are invaluable. We’ll use pointers whenever it is appropriate throughout this course, starting in Lesson 2. While C++ provides the features provided by C (functions and data types), the same is not true in reverse. The C language is—for the most part—a subset of C++. C++ extends C by adding high-level features that make it much more powerful than C. The primarily addition takes the form of classes. which are www.gameinstitute.com Introduction to C and C++ : Week 1: Page 7 of 42 essentially the marriage of functions and data. (The name originally considered for C++ was C with classes.) In C++, an object is a single instance of a class. In C and C++, functions use data, and data can be manipulated by functions, but classes allow functions and data to be defined as part of a single object. This is a simple concept that has powerful connotations, because it allows programs to be modeled more closely on reality. By creating objects that contain both a state (the data), and a means for modifying that state (the functions), C++ allows real world entities to be modeled much more accurately and naturally. The object-oriented features of C++ make it an ideal language for designing complex systems. Combining functions and data to form objects is a powerful addition on its own, but is multiplied by the fact that objects can be extended to create new, more powerful objects, without modifying the original objects. This is called inheritance or polymorphism. Inheritance allows a new class of objects to be created by defining only the ways in which the new type of object differs from the original type of object. This course introduces C++ starting with the low-level features, and moving to the high-level features. In this lesson and continuing in lesson 2 we’ll cover the fundamental features that C++ inherits from C. In lesson 3 we’ll delve into C++ specific support for objects. In the remainder of the course we’ll build on this foundation by exploring game-specific uses for these language features. Before we get to the C++ language itself, we need to talk about the tools and processes that must be used in order to convert C++ source code into an executable form. Programming, like any other craft, such as carpentry, painting, or mechanics, requires that you become familiar with the tools of the trade before you can fully concentrate on the craft itself. Knowing the C++ language is useless if you can’t usher your source code into a usable form, just as knowing how to paint is useless if you don’t know how to acquire and mix the required pigments. Once we have an understanding of the tools with which we’ll be working, we will introduce the fundamental C++ language topics upon which the remainder of the course—and the rest of your C++ programming career relies. Development tools Programmers generally come out of two camps: Windows and Unix. Programmers familiar with Windows programming often start with Visual Basic, which provides an Integrated Development Environment (IDE). An IDE is an application that allows the programmer to create and manage projects, edit code, and compile source code into a form that is machine-readable. IDEs typically provide debugging support as well, which allows the workings of a program to be scrutinized by stepping through the code, line by line, as it is executed. IDEs often provide “Code Wizards” capable of generating small projects or injecting code into existing projects in order to add new features. When you use an IDE, you usually don’t see the contents of all of the code or the project files that make up each project. This has the advantage of allowing you to concentrate on the application specific portions of the code and ignore a significant portion of boilerplate code and configuration data. Unix programmers, on the other hand, usually learn to program using command-line tools. In this case, each of the tools required for programming is executed separately. There is no common graphical user interface that unifies the programming tools. Code is compiled with one tool, linked with another tool, and debugged with yet another tool (we’ll talk about each of these steps in more detail shortly.) In a command-line environment, the management of code modules and projects is done by hand. Command- line programming requires a higher level of familiarity with each project component than is required when programming with an IDE. www.gameinstitute.com Introduction to C and C++ : Week 1: Page 8 of 42 Windows C++ programming offers both options. Visual C++, for example, provides an IDE from which C++ applications can be developed and tested. For Visual Basic programmers, switching over to the Visual C++ IDE is fairly painless, as the interface and concepts are similar. But C++ has long-standing ties with Unix, and as such still provides command-line tools. The Visual C++ IDE, in fact, merely invokes these command-line tools behind the scenes. This means that programmers can opt to use these command-line tools directly, and forfeit the use of the IDE. But if you’re new to programming, your best bet is to start with an IDE. Despite the fact that Visual C++ currently dominates the C++ market, it is important not to forget that it is not the only option. Borland, for example (a company that dominated the C++ development tool market for years before Microsoft, and is in large part responsible for the popularity of C++ on the PC platform) offers a C++ development system called C++ Builder. Although it is not nearly as popular as Visual C++, it is a perfectly good C++ development tool. These are just two of the development tools available for Windows, and other platforms, such as Macintosh and Unix have C++ development tools of their own. Because the emphasis of this C++ course is game development, and because the vast majority of games and game-related tools are Windows based, we’ll concentrate on the Windows platform. This won’t be obvious at first, as the samples that we start with don’t use any Windows-specific features, but later, in order to introduce graphics, we’ll be using Microsoft’s DirectX toolkit, which is Window’s specific. The development tools that we’ll focus on are Microsoft Visual C++ and Borland C++, each for different reasons. We’ll target Visual C++ because it is by far the most prevalent C++ tool, both for game development and in general. And, whatever your personal feelings about Microsoft, Visual C++ is a very good tool. We include Borland in the mix both so that we don’t forget that Visual C++ isn’t the only C++ development tool, and because it is free. In an attempt to attract customers, Borland has made the command-line version of their C++ tools freely available. There’s no IDE, but a fully functional version of a very up-to-date tool is yours at not cost. (There are free development tools available for Unix, so Unix users will be less surprised at this, but Windows users are less accustomed to free development tools.) So, if you lack the funds to buy Visual C++, or are not inclined to do so for personal reasons, there is an option. Each of the samples in this course, in addition to including Visual C++ project files, includes support for the Borland compiler as well. The command-line Borland C++ tools are available at this URL: http://www.borland.com/bcppbuilder/freecompiler/ There is one additional reason why Borland is supported in this course. The Borland tools are ANSI compliant. ANSI (American National Standards Institute) is an organization through which proposed C++ language features are ratified and thereby ushered into the official version of the language. Visual C++ includes many features that are of Microsoft’s own design and are not ANSI compliant. By checking your work even if just occasionally with the Borland compiler, you can be sure that you aren’t inadvertently using Microsoft-specific C++ features. This is a non-issue if you’re positive that your code need only support Windows, but if you think you might want to use your code on Linux, for example, it is important to keep an ANSI compliant compiler around. C++ Mechanics The code required for any C++ program is stored in a standard text file, usually with a .cpp extension. The filename main.cpp, for example, might be used to store the primary source code for a project. www.gameinstitute.com Introduction to C and C++ : Week 1: Page 9 of 42 Because cpp files are text files, they can be edited with virtually any text editor, such as Windows Notepad. In practice it is best to use an editor that is intended for C++. The Visual C++ editor, for example, highlights language keywords and comments using different colors, making the code easier to read (in theory, at least). Starting with Visual C++ 6, the IDE editor also makes suggestions while you type. While this feature is of questionable value in a word processor, it is genuinely useful for writing source code, as it can reduce time spent looking up function names and arguments. The cpp file is human readable, but meaningless to Windows or any other operating system until it is compiled. Compiling cpp files is performed with a C++ compiler, but the compiler is actually just one of the tools required to convert source code into an executable form. C++ requires three steps in order to convert source code into an executable form: • Preprocessing • Compilation • Linking The first two steps, preprocessing and compiling, are typically performed by a single tool, and therefore appear to be the same step. Nevertheless, preprocessing is a separate and distinct step. The preprocessor responds to different C++ language constructs than the compiler, and the compiler does not recognize these constructs, so compilation cannot occur until the source code has been preprocessed. The preprocessor Preprocessing serves several purposes, one of which is to strip any comments out of the code. Because code comments are removed by the time the compiler is invoked, the compiler is free to treat everything encountered as code. In addition to comment removal, the preprocessor performs these two tasks: • Macro expansion • Header file inclusion C++ macros work on a simple search and replace basis. Macros are used to define text that, when found by the preprocessor, is replaced with other text. This is a simple and powerful tool. Macros can be used to substitute complex instructions or even sets of instructions with simple names. Macros can also be used to give frequently used values or strings simple and logical names. This allows multiple uses of a value to be changed just by changing the macro definition. Macros can even be used to redefine standard C++ data types. The simple search and replace nature of macros is also what makes them dangerous. Unlike the compiler, the preprocessor has a very limited and simplistic understanding of C++. This makes it is easy to write macros that use conflicting data types, or work in one case but fail in others. The preprocessor performs no type or syntax checking on macros, so the task of reporting problems falls to the compiler. And, while the compiler will detect and report these problems, it has trouble reporting these errors efficiently because the compiler is using the expanded version of the macro. When macros are used, the code you see in your editor is different from the code that the compiler is given. Any errors that the compiler reports are therefore reported in terms of the expanded macro, and do not reflect the name of the macro. There’s another reason to be wary of macros. Consider, for example, that your program requires a lengthy and frequently used operation. Rather than write code for the entire operation each time it is needed, you can write a macro to perform the operation. This has the desired effect of centralizing the code required www.gameinstitute.com Introduction to C and C++ : Week 1: Page 10 of 42 for the operation and simplifying the remaining code, but is has a possibly unwanted side effect. The preprocessor, each time the macro is used, expands the macro. If the macro is used more than once, multiple segments of identical code is expanded into your source code. To the compiler, it’s as though you typed in the entire contents of the macro for each usage. This can be a waste of memory, and can even have detrimental effects on performance. With C (as opposed to C++) there are several cases where using macros is required, but C++ provides safer alternatives for most of these cases. It is therefore usually best to avoid macros. Macros do have their place, but it is a good idea to consider other options before using them. Header file expansion is another task that is performed by the preprocessor. Header files, which typically have a .h extension, are used to define data structures, macros, and special functions that are common to multiple cpp files. Header files are never provided directly to the compiler. Instead they are inserted into cpp files, which in turn are passed to the compiler. The preprocessor, whenever it encounters a header file insertion command in a cpp file, inserts the contents of the header file into the cpp file. This is, in fact, another form of a macro substitution, except that in this case the contents of an entire file are being inserted into the code. The preprocessor doesn’t read header files except to remove comments and expand macros. In all other respects the header file is simply pasted into the cpp file. This means that the contents of a header file, once it has been inserted into a cpp file, are treated exactly like the content of a cpp file. The compiler doesn’t know or care that the code it is compiling came from a header file or a cpp file. Anything that can be put in a cpp file can be put into an h file, and vise versa. Despite this fact, there are rules that should be followed about what gets added to header files. We’ll talk about this distinction in Lesson 4. The Compiler The heart of any C++ development system is the compiler. This is the tool that reads C++ source code, in the form of cpp files, interprets the data structures and code, and converts then into a binary form more suitable for executables. The compiler does not, however, generate the executable output required to run the resulting program. This is the task of the linker, which we’ll talk about soon. Unlike the preprocessor, which understands just a few items in a cpp file, the compiler must understand every character in the source code. If the compiler encounters anything that it does not immediately understand, it generates at least one error message, and no output file will be generated. To say that the compiler protests each time it encounters anything that it does not immediately understand is not an exaggeration. In C++, with the exception of the standard data types, every construct that your code uses must first be declared or defined. If a variable appears in the code that has not been formally introduced, compilation will fail. If a function is called before being either defined or declared in advance, compilation fails. (We will talk about the difference between definition and declaration soon.) Unlike Visual Basic, which by default allows variables to be used without having been given a type in advance, C++ is extremely type sensitive. No ambiguity about the nature of a variable or function is allowed. If the compiler is able to interpret the contents of a cpp file without any errors, an output file is produced. The output takes the form of an obj, or object file. (The term object, in this case, doesn’t have the same meaning as that used in object oriented programming.) The obj file contains the compiled code in a form that is very close to that of an executable, but lacks fundamental mechanisms and formatting required for execution. [...]... C++, you can load the project either by double clicking on the HelloWorld.dsw file, or, from within the Visual C++ IDE, using the File|Load Workspace menu option to display the workspace selection dialog and navigating to the HellowWorld.dsw file Once the project is loaded into Visual C++ it can be compiled by pressing F7 or using the Build|Build HelloWorld.exe menu option If you’re using Borland C++, ... a single bit, C++ uses a byte (8 bits) to represent Booleans Until fairly recently, the Boolean type wasn’t a native C++ type, but currently C++ provides Boolean support with the bool type In addition, two new symbols have been added to the language to support the bool type: true and false, which can be used to assign and inspect the value of Booleans Whole numbers are supported by the C++ integer category,... because C++ uses null-terminated strings This means that the text in a C++ string is followed by a terminator that has a value of zero (C and C++ often use the term “NULL” as a synonym for zero) In our example, the compiler is automatically adding a null terminator after the exclamation point The null-terminator indicates the end of a string The IntMult Sample Now that we know about the intrinsic C++ data... explicitly mentioned except in the return type Unless a type is explicitly indicated, C++ assigns data types to literal numeric values that appear in the code If a number has no decimal point, C++ assumes the value to be an int If a decimal point is present, www.gameinstitute.com Introduction to C and C++ : Week 1: Page 28 of 42 C++ assumes the value to be a double Since the Fahrenheit2Celsius function has... let’s take a detailed look at the C++ data types www.gameinstitute.com Introduction to C and C++ : Week 1: Page 19 of 42 What is a data type? A data type is a language construct that is used to describe a single data element Data types are concepts, or descriptions—they don’t actually exist in a program They do, however, describe data that exists in those programs C++ uses data types to define how... to C and C++ : Week 1: Page 15 of 42 • • while do The word main is a special function name, but is not a C++ keyword Likewise, cout is part of the standard C++ library, but not part of the language Throughout this course, when introducing new symbols, we’ll take care to indicate whether they are C++ keywords, symbols provided by the standard library, or items created specifically for this course Also,... there aren’t many, some words are part of the C++ language These words are called keywords, and cannot be used as function or variable names So far we’ve used just one C++ keyword: return These are the keywords that we’ll use in this lesson: • • • • • • • • • return int float double long short signed unsigned for www.gameinstitute.com Introduction to C and C++ : Week 1: Page 15 of 42 • • while do The... called HelloWorld.exe Visual C++ will place these executables in sub-directories named Release and Debug The Borland tools will use the directory names bccRelease and bccDebug www.gameinstitute.com Introduction to C and C++ : Week 1: Page 17 of 42 To run the new executable, command line users can simply enter bccDebug\HelloWorld and press return (to run the debug version) Visual C++ can launch newly generated... statement is executed once for each loop iteration, and is typically used to increment the loop variable The C++ increment operator (++) increments a variable by 1, and is almost always used in this portion of a for loop www.gameinstitute.com Introduction to C and C++ : Week 1: Page 34 of 42 In typical C++ fashion, for loops are highly flexible The fact that we’ve using an integer as a loop counting variable,... The final step involves the linker, which converts obj files into an exe, dll, or lib file This process is illustrated below www.gameinstitute.com Introduction to C and C++ : Week 1: Page 11 of 42 Knowing what is involved in assembling C++ programs is important, but, regardless of whether you’re using an IDE or a command-line tool, it’s not necessary to perform each step separately The IDE, or a command-line . Introduction to C and C++ : Week 1: Page 8 of 42 Windows C++ programming offers both options. Visual C++, for example, provides an IDE from which C++ applications. are Microsoft Visual C++ and Borland C++, each for different reasons. We’ll target Visual C++ because it is by far the most prevalent C++ tool, both for