<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-1020589601308586811</id><updated>2011-04-21T19:16:42.469-07:00</updated><category term='C++'/><title type='text'>debug-guide</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://debug-guide.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1020589601308586811/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://debug-guide.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>doni</name><uri>http://www.blogger.com/profile/15287923128168749403</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>4</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1020589601308586811.post-1021872569039402768</id><published>2008-01-29T09:59:00.001-08:00</published><updated>2008-01-29T09:59:41.735-08:00</updated><title type='text'>Examples using structs</title><content type='html'>17 Examples using structs&lt;br /&gt;This chapter contains a few simple examples illustrating the use of structs in programs.&lt;br /&gt;In future programs, you will use structs mainly when communicating with existing C&lt;br /&gt;libraries. For example, you will get to write code that uses the graphics primitives on&lt;br /&gt;your system. The "applications programmer interface" (API) for the graphics will be&lt;br /&gt;defined by a set of C functions (in some cases based on an earlier interface defined by a&lt;br /&gt;set of Pascal functions). If you a programming on Unix, you will probably be using the&lt;br /&gt;Xlib graphics package, on an Apple system you would be using Quickdraw, and on an&lt;br /&gt;Intel system you would use a Windows API. The functions in the API will make&lt;br /&gt;extensive use of simple structs. Thus, the Xlib library functions use instances of the&lt;br /&gt;following structs:&lt;br /&gt;typedef struct {&lt;br /&gt;short x, y;&lt;br /&gt;} XPoint;&lt;br /&gt;typedef struct {&lt;br /&gt;short x, y;&lt;br /&gt;unsigned short width, height;&lt;br /&gt;} XRectangle;&lt;br /&gt;and&lt;br /&gt;typedef struct {&lt;br /&gt;short x, y;&lt;br /&gt;unsigned short width, height;&lt;br /&gt;short angle1, angle2;&lt;br /&gt;} XArc;&lt;br /&gt;So, if you are using the Xlib library, your programs will also define and use variables&lt;br /&gt;that are instances of these struct types.&lt;br /&gt;For new application-specific code you will tend to use variables of class types more&lt;br /&gt;often than variables of struct types.&lt;br /&gt;17&lt;br /&gt;Using structs with&lt;br /&gt;existing libraries&lt;br /&gt;Your own structs?&lt;br /&gt;476 Examples using structs&lt;br /&gt;The examples in this chapter are intended merely to illustrate how to declare struct&lt;br /&gt;types, use instances of these types, access fields and so forth. The example programs in&lt;br /&gt;section 17.3, introduce the idea of a "file of records". These programs have a single&lt;br /&gt;struct in memory and a whole set of others in a disk file. The struct in memory gets&lt;br /&gt;loaded with data from a requested record on disk. Record files are extremely common&lt;br /&gt;in simple business data processing applications, and represent a first step towards the&lt;br /&gt;more elaborate "databases" used in industry. You will learn more about "files of&lt;br /&gt;records" and "databases" in later subjects. While you can continue to use C/C++ when&lt;br /&gt;working with database systems, you are more likely to use specialized languages like&lt;br /&gt;COBOL and SQL.&lt;br /&gt;17.1 REORDERING THE CLASS LIST AGAIN&lt;br /&gt;The example in section 13.2 illustrated a "sorting" algorithm that was applied to reorder&lt;br /&gt;pupil records. The original used two arrays, pupils and marks:&lt;br /&gt;typedef char Name[40];&lt;br /&gt;Name pupils[] = {&lt;br /&gt;"Armstrong, Alice S.",&lt;br /&gt;…&lt;br /&gt;"Zarra, Daniela"&lt;br /&gt;};&lt;br /&gt;int marks[] = {&lt;br /&gt;57,&lt;br /&gt;…&lt;br /&gt;78&lt;br /&gt;};&lt;br /&gt;But the data in the two arrays "belong together" – Miss Armstrong's mark is 57 and she&lt;br /&gt;shouldn't be separated from it.&lt;br /&gt;This is a typical case where the introduction of a simple struct leads to code that&lt;br /&gt;reflects the problem structure more accurately (and is also slightly clearer to read). The&lt;br /&gt;struct has to package together a name and a mark:&lt;br /&gt;struct PupilRec {&lt;br /&gt;Name fName;&lt;br /&gt;int fMark;&lt;br /&gt;};&lt;br /&gt;(Naming conventions again: use 'f' as the first character of the name of a data member&lt;br /&gt;in all structs and classes that are defined for a program.)&lt;br /&gt;With this struct declared, an initialized array of PupilRecs can be defined:&lt;br /&gt;Reordering the class list 477&lt;br /&gt;PupilRec JuniorYear[] = {&lt;br /&gt;{ "Armstrong, Alice S.", 57 } ,&lt;br /&gt;{ "Azur, Hassan M.", 64 } ,&lt;br /&gt;{ "Bates, Peter", 33 } ,&lt;br /&gt;…&lt;br /&gt;{ "Zarra, Daniela", 81 }&lt;br /&gt;};&lt;br /&gt;The function MakeOrderedCopy() can be rewritten to use these PupilRec structs:&lt;br /&gt;void MakeOrderedCopy(const PupilRec orig[],int num,&lt;br /&gt;PupilRec reord[])&lt;br /&gt;{&lt;br /&gt;int mark_count[101];&lt;br /&gt;for(int i = 0; i&lt; 101; i++)&lt;br /&gt;mark_count[i] = 0;&lt;br /&gt;// Count number of occurrences of each mark&lt;br /&gt;for(i = 0; i &lt; num; i++) {&lt;br /&gt;int mark = orig[i].fMark;&lt;br /&gt;mark_count[mark]++;&lt;br /&gt;}&lt;br /&gt;// Make that count of number of pupils with marks less than&lt;br /&gt;// or equal to given mark&lt;br /&gt;for(i=1; i&lt;101; i++)&lt;br /&gt;mark_count[i] += mark_count[i-1];&lt;br /&gt;for(i=num - 1; i &gt;= 0; i--) {&lt;br /&gt;int mark = orig[i].fMark;&lt;br /&gt;int position = mark_count[mark];&lt;br /&gt;position--; // correct to zero based array&lt;br /&gt;// copy data&lt;br /&gt;reord[position] = orig[i];&lt;br /&gt;mark_count[mark]--;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;The function takes two arrays of PupilRec structs. The orig parameter is the array&lt;br /&gt;with the records in alphabetic order; it is an "input parameter" so it is specified as&lt;br /&gt;const. The reord parameter is the array that is filled with the reordered copy of the&lt;br /&gt;data.&lt;br /&gt;An expression like:&lt;br /&gt;orig[i].fMark&lt;br /&gt;illustrates how to access a data member of a chosen element from an array of structures.&lt;br /&gt;Note the structure assignment:&lt;br /&gt;Arrays of structs as&lt;br /&gt;"input" and&lt;br /&gt;"output" parameters&lt;br /&gt;Accessing data&lt;br /&gt;member of one of&lt;br /&gt;structs in the array&lt;br /&gt;Assignment of structs&lt;br /&gt;478 Examples using structs&lt;br /&gt;reord[position] = orig[i];&lt;br /&gt;The compiler "knows" the size of structs so allows struct assignment while assignment&lt;br /&gt;of arrays is not allowed.&lt;br /&gt;17.2 POINTS AND RECTANGLES&lt;br /&gt;The following structs and functions illustrate the functionality that you will find in the&lt;br /&gt;graphics libraries for your system. Many of the basic graphics functions will use points&lt;br /&gt;and rectangles; for example, a line drawing function might take a "start point" and an&lt;br /&gt;"end point" as its parameters, while a DrawOval() function might require a rectangle to&lt;br /&gt;frame the oval.&lt;br /&gt;The graphics packages typically use short integers to represent coordinate values. A&lt;br /&gt;"point" will need two short integer data fields:&lt;br /&gt;struct Point {&lt;br /&gt;short fx;&lt;br /&gt;short fy;&lt;br /&gt;};&lt;br /&gt;A struct to represent a rectangle can be defined in a number of alternative ways,&lt;br /&gt;including:&lt;br /&gt;struct Rectangle {&lt;br /&gt;short ftop;&lt;br /&gt;short fleft;&lt;br /&gt;short fwidth;&lt;br /&gt;short fheight;&lt;br /&gt;};&lt;br /&gt;and&lt;br /&gt;struct Rectangle {&lt;br /&gt;Point fcorner;&lt;br /&gt;short fwidth;&lt;br /&gt;short fheight;&lt;br /&gt;};&lt;br /&gt;Provided that struct Point has been declared before struct Rectangle, it is&lt;br /&gt;perfectly reasonable for struct Rectangle to have data members that are instances&lt;br /&gt;of type Point. The second declaration will be used for the rest of the examples in this&lt;br /&gt;section.&lt;br /&gt;The graphics libraries define numerous functions for manipulating points and&lt;br /&gt;rectangles. The libraries would typically include variations of the following functions:&lt;br /&gt;Points and Rectangles 479&lt;br /&gt;int Point_In_Rect(const Point&amp;amp; pt, const Rectangle&amp;amp; r);&lt;br /&gt;Returns "true" (1) if point pt lies with Rectangle r.&lt;br /&gt;int Equal_Points(const Point&amp;amp; p1, const Point&amp;amp; p2);&lt;br /&gt;Returns "true" if points p1 and p2 are the same.&lt;br /&gt;void Add_Point(const Point&amp;amp; p1, const Point&amp;amp; p2, Point&amp;amp; res);&lt;br /&gt;Changes the "output parameter" res to be the 'vector sum'&lt;br /&gt;of points p1 and p2.&lt;br /&gt;Point MidPoint(const Point&amp;amp; p1, const Point&amp;amp; p2);&lt;br /&gt;Returns the mid point of the given points.&lt;br /&gt;int ZeroRect(const Rectangle&amp;amp; r)&lt;br /&gt;Return "true" if r's width and height are both zero.&lt;br /&gt;Rectangle UnionRect(const Rectangle&amp;amp; r1, const Rectangle&amp;amp; r2);&lt;br /&gt;Returns the smallest circumscribing rectangle of the given&lt;br /&gt;rectangles r1 and r2.&lt;br /&gt;Rectangle IntersectRect(const Rectangle&amp;amp; r1,&lt;br /&gt;const Rectangle&amp;amp; r2);&lt;br /&gt;Returns a rectangle that represents the intersection of the&lt;br /&gt;given rectangles, or a zero rectangle if they don't&lt;br /&gt;intersect.&lt;br /&gt;Rectangle Points2Rect(const Point&amp;amp; p1, const Point&amp;amp; p2);&lt;br /&gt;Returns a rectangle that includes both points p1 and p2&lt;br /&gt;within its bounds or at least on its perimeter&lt;br /&gt;These functions are slightly inconsistent in their prototypes, some return structs as&lt;br /&gt;results while others have output parameters. This is deliberate. The examples are&lt;br /&gt;meant to illustrate the different coding styles. Unfortunately, it is also a reflection of&lt;br /&gt;most of the graphics libraries! They tend to lack consistency. If you were trying to&lt;br /&gt;design a graphics library you would do better to decide on a style; functions like&lt;br /&gt;Add_Point() and MidPoint() should either all have struct return types or all have&lt;br /&gt;output parameters. In this case, there are reasons to favour functions that return structs.&lt;br /&gt;Because points and rectangles are both small, it is acceptable for the functions to return&lt;br /&gt;structs as results. Code using functions that return structs tends to be a little more&lt;br /&gt;readable than code using functions with struct output parameters.&lt;br /&gt;This example is like the "curses" and "menu selection" examples in Chapter 12. We&lt;br /&gt;need a header file that describes the facilities of a "points and rectangles" package, and&lt;br /&gt;an implementation file with the code of the standard functions. Just so as to illustrate&lt;br /&gt;the approach, some of the simpler functions will be defined as "inline" and their&lt;br /&gt;definitions will go in the header file.&lt;br /&gt;The header file would as usual have its contents bracketed by conditional&lt;br /&gt;compilation directives:&lt;br /&gt;480 Examples using structs&lt;br /&gt;#ifndef __MYCOORDS__&lt;br /&gt;#define __MYCOORDS__&lt;br /&gt;the interesting bits&lt;br /&gt;#endif&lt;br /&gt;As explained in section 12.1, these conditional compilation directives protect against&lt;br /&gt;the possibility of erroneous multiple inclusion.&lt;br /&gt;The structs would be declared first, then the function prototypes would be listed.&lt;br /&gt;Any "inline" definitions would come toward the end of the file:&lt;br /&gt;#ifndef __MYCOORDS__&lt;br /&gt;#define __MYCOORDS__&lt;br /&gt;struct Point {&lt;br /&gt;short fx;&lt;br /&gt;short fy;&lt;br /&gt;};&lt;br /&gt;struct Rectangle {&lt;br /&gt;…&lt;br /&gt;};&lt;br /&gt;int Point_In_Rect(const Point&amp;amp; pt, const Rectangle&amp;amp; r);&lt;br /&gt;…&lt;br /&gt;inline int Equal_Points(const Point&amp;amp; p1, const Point&amp;amp; p2)&lt;br /&gt;{&lt;br /&gt;return ((p1.fx == p2.fx) &amp;amp;&amp;amp; (p1.fy == p2.fy));&lt;br /&gt;}&lt;br /&gt;inline int ZeroRect(const Rectangle&amp;amp; r)&lt;br /&gt;{&lt;br /&gt;return ((r.fwidth == 0) &amp;amp;&amp;amp; (r.fheight == 0));&lt;br /&gt;}&lt;br /&gt;#endif&lt;br /&gt;"Inline" functions have to be defined in the header. After all, if the compiler is to&lt;br /&gt;replace calls to the functions by the actual function code it must know what that code is.&lt;br /&gt;When compiling a program in another file that uses these functions, the compiler only&lt;br /&gt;knows the information in the #included header.&lt;br /&gt;The definitions of the other functions would be in a separate mycoords.cp file:&lt;br /&gt;#include "mycoords.h"&lt;br /&gt;Header file&lt;br /&gt;"mycoords.h"&lt;br /&gt;inline functions&lt;br /&gt;defined in the header&lt;br /&gt;file&lt;br /&gt;Points and Rectangles 481&lt;br /&gt;int Point_In_Rect(const Point&amp;amp; pt, const Rectangle&amp;amp; r)&lt;br /&gt;{&lt;br /&gt;// Returns "true" if point pt lies with Rectangle r.&lt;br /&gt;if((pt.fx &lt; r.fcorner.fx) || (pt.fy &lt; r.fcorner.fy))&lt;br /&gt;return 0;&lt;br /&gt;int dx = pt.fx - r.fcorner.fx;&lt;br /&gt;int dy = pt.fy - r.fcorner.fy;&lt;br /&gt;if((dx &gt; r.fwidth) || (dy &gt; r.fheight))&lt;br /&gt;return 0;&lt;br /&gt;return 1;&lt;br /&gt;}&lt;br /&gt;A rectangle has a Point data member which itself has int fx, fy data members.&lt;br /&gt;The name of the data member that represents the x-coordinate of the rectangle's corner&lt;br /&gt;is built up from the name of the rectangle variable, e.g. r1, qualified by the name of its&lt;br /&gt;point data member fcorner, qualified by the name of the field, fx.&lt;br /&gt;Function Add_Point() returns its result via the res argument:&lt;br /&gt;void Add_Point(const Point&amp;amp; p1, const Point&amp;amp; p2, Point&amp;amp; res)&lt;br /&gt;{&lt;br /&gt;// Changes the "output parameter" res to be the 'vector sum'&lt;br /&gt;// of points p1 and p2.&lt;br /&gt;res.fx = p1.fx + p2.fx;&lt;br /&gt;res.fy = p1.fy + p2.fy;&lt;br /&gt;}&lt;br /&gt;while MidPoint() returns its value via the stack (it might as well use Add_Point() in&lt;br /&gt;its implementation).&lt;br /&gt;Point MidPoint(const Point&amp;amp; p1, const Point&amp;amp; p2)&lt;br /&gt;{&lt;br /&gt;// Returns the mid point of the given points.&lt;br /&gt;Point m;&lt;br /&gt;Add_Point(p1, p2, m);&lt;br /&gt;m.fx /= 2;&lt;br /&gt;m.fy /= 2;&lt;br /&gt;return m;&lt;br /&gt;}&lt;br /&gt;Function Points2Rect() is typical of the three Rectangle functions:&lt;br /&gt;Rectangle Points2Rect(const Point&amp;amp; p1, const Point&amp;amp; p2)&lt;br /&gt;{&lt;br /&gt;// Returns a rectangle that includes both points p1 and p2&lt;br /&gt;// within its bounds or at least on its perimeter&lt;br /&gt;Note multiply&lt;br /&gt;qualified names of&lt;br /&gt;data members&lt;br /&gt;482 Examples using structs&lt;br /&gt;Rectangle r;&lt;br /&gt;int left, right, top, bottom;&lt;br /&gt;if(p1.fx &lt; p2.fx) { left = p1.fx; right = p2.fx; }&lt;br /&gt;else { left = p2.fx; right = p1.fx; }&lt;br /&gt;if(p1.fy &lt; p2.fy) { top = p1.fy; bottom = p2.fy; }&lt;br /&gt;else { top = p2.fy; bottom = p1.fy; }&lt;br /&gt;r.fcorner.fx = left;&lt;br /&gt;r.fcorner.fy = top;&lt;br /&gt;r.fwidth = right - left;&lt;br /&gt;r.fheight = bottom - top;&lt;br /&gt;return r;&lt;br /&gt;}&lt;br /&gt;If you were developing a small library of functions for manipulation of points and&lt;br /&gt;rectangles, you would need a test program like:&lt;br /&gt;#include &lt;iostream.h&gt;&lt;br /&gt;#include "mycoords.h"&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt;Point p1;&lt;br /&gt;p1.fx = 1;&lt;br /&gt;p1.fy = 7;&lt;br /&gt;Rectangle r1;&lt;br /&gt;r1.fcorner.fx = -3;&lt;br /&gt;r1.fcorner.fy = -1;&lt;br /&gt;r1.fwidth = 17;&lt;br /&gt;r1.fheight = 25;&lt;br /&gt;if(Point_In_Rect(p1, r1))&lt;br /&gt;cout &lt;&lt; "Point p1 in rect" &lt;&lt; endl;&lt;br /&gt;else cout &lt;&lt; "p1 seems to be lost" &lt;&lt; endl;&lt;br /&gt;Point p2;&lt;br /&gt;p2.fx = 11;&lt;br /&gt;p2.fy = 9;&lt;br /&gt;Point p3;&lt;br /&gt;Add_Point(p1, p2, p3);&lt;br /&gt;cout &lt;&lt; "I added the points, getting p3 at ";&lt;br /&gt;cout &lt;&lt; p3.fx &lt;&lt; ", " &lt;&lt; p3.fy &lt;&lt; endl;&lt;br /&gt;Point p4;&lt;br /&gt;p4.fx = 12;&lt;br /&gt;Points and Rectangles 483&lt;br /&gt;p4.fy = 16;&lt;br /&gt;if(Equal_Points(p3, p4))&lt;br /&gt;cout &lt;&lt; "which is where I expected to be" &lt;&lt; endl;&lt;br /&gt;else cout &lt;&lt; "which I find surprising!" &lt;&lt; endl;&lt;br /&gt;…&lt;br /&gt;…&lt;br /&gt;Rectangle r4 = UnionRect(r2, r3);&lt;br /&gt;cout &lt;&lt; "I made rectangle r4 to have a corner at ";&lt;br /&gt;cout &lt;&lt; r4.fcorner.fx &lt;&lt; ", " &lt;&lt; r4.fcorner.fy &lt;&lt; endl;&lt;br /&gt;cout &lt;&lt; "and width of " &lt;&lt; r4.fwidth &lt;&lt;&lt;br /&gt;", height " &lt;&lt; r4.fheight &lt;&lt; endl;&lt;br /&gt;Rectangle r5 = IntersectRect(r4, r1);&lt;br /&gt;cout &lt;&lt; "I made rectangle r5 to have a corner at ";&lt;br /&gt;cout &lt;&lt; r5.fcorner.fx &lt;&lt; ", " &lt;&lt; r5.fcorner.fy &lt;&lt; endl;&lt;br /&gt;cout &lt;&lt; "and width of " &lt;&lt; r5.fwidth &lt;&lt;&lt;br /&gt;", height " &lt;&lt; r5.fheight &lt;&lt; endl;&lt;br /&gt;return 0;&lt;br /&gt;}&lt;br /&gt;The test program would have calls to all the defined functions and would be organized&lt;br /&gt;to check the results against expected values as shown.&lt;br /&gt;You may get compilation errors relating to struct Point. Some IDEs&lt;br /&gt;automatically include the system header files that define the graphics functions, and&lt;br /&gt;these may already define some other struct Point. Either find how to suppress this&lt;br /&gt;automatic inclusion of headers, or change the name of the structure to Pt.&lt;br /&gt;17.3 FILE OF RECORDS&lt;br /&gt;Rather than writing their own program, anyone who really wants to keep files of&lt;br /&gt;business records would be better off using the "database" component of one of the&lt;br /&gt;standard "integrated office packages". But someone has to write the "integrated office&lt;br /&gt;package" of the future, maybe you. So you had better learn how to manipulate simple&lt;br /&gt;files of records.&lt;br /&gt;The idea of a file of records was briefly previewed in section 9.6 and is again&lt;br /&gt;illustrated in Figure 17.1.&lt;br /&gt;A file record will be exactly the same size as a memory based struct. Complete&lt;br /&gt;structs can be written to, and read from, files. The i/o transfer involves simply copying&lt;br /&gt;bytes (there is no translation between internal binary forms of data and textual strings).&lt;br /&gt;The operating system is responsible for fitting the records into the blocks on disk and&lt;br /&gt;keeping track of the end of the file.&lt;br /&gt;484 Examples using structs&lt;br /&gt;"Record structured" file:&lt;br /&gt;Customer-1 Customer-2 Customer-3&lt;br /&gt;End of file&lt;br /&gt;Get (and/or) Put&lt;br /&gt;pointer&lt;br /&gt;Figure 17.1 A file of "customer records"&lt;br /&gt;Files of record can be processed sequentially. The file can be opened and records&lt;br /&gt;read one by one until the end of file marker is reached. This is appropriate when you&lt;br /&gt;want to all records processed. For example, at the end of the month you might want to&lt;br /&gt;run through the complete file identifying customers who owed money so that letters&lt;br /&gt;requesting payment could be generated and dispatched.&lt;br /&gt;Files of records may also be processed using "random access". Random access does&lt;br /&gt;not mean that any data found at random will suffice (this incorrect explanation was&lt;br /&gt;proposed by a student in an examination). The "randomness" lies in the sequence in&lt;br /&gt;which records get taken from file and used. For example, if you had a file with 100&lt;br /&gt;customer records, you might access them in the "random" order 25, 18, 49, 64, 3, ….&lt;br /&gt;File systems allow "random access" because you can move the "get" (read) or "put"&lt;br /&gt;(write) pointer that the operating system associates with a file before you do the next&lt;br /&gt;read or write operation. You can read any chosen record from the file by moving the&lt;br /&gt;get pointer to the start of that record. This is easy provided you know the record&lt;br /&gt;number (imagine that the file is like an array of records, you need the index number into&lt;br /&gt;this array). You simply have to multiply the record number by the size of the record,&lt;br /&gt;this gives the byte position where the "get pointer" has to be located.&lt;br /&gt;You have to be able to identify the record that you want. You could do something&lt;br /&gt;like assign a unique identifying number (in the range 0…?) to each customer and&lt;br /&gt;require that this is specified in all correspondence, or you could make use of an&lt;br /&gt;auxiliary table (array) of names and numbers.&lt;br /&gt;You would want to use "random access" if you were doing something like taking&lt;br /&gt;new orders as customers came to, or phoned the office. When a customer calls, you&lt;br /&gt;want to be able to access their record immediately, you don't want to read all the&lt;br /&gt;preceding records in the file. (Of course reading the entire file wouldn't matter if you&lt;br /&gt;have only 100 records; but possibly you have ambitions and wish to grow to millions.)&lt;br /&gt;Sequential access&lt;br /&gt;Random access&lt;br /&gt;File of records 485&lt;br /&gt;New iostream and&lt;br /&gt;fstream features&lt;br /&gt;Working with record files and random access requires the use of some additional&lt;br /&gt;facilities from the iostream and fstream libraries.&lt;br /&gt;First, the file that holds the data records will be an "input-output" file. If you need to&lt;br /&gt;do something like make up an order for a customer, you need to read (input) the&lt;br /&gt;customer's record, change it, then save the changed record by writing it back to file&lt;br /&gt;(output). Previously we have used ifstream objects (for inputs from file) and&lt;br /&gt;ofstream objects (for outputs to file), now we need an fstream object (for&lt;br /&gt;bidirectional i/o). We will need an fstream variable:&lt;br /&gt;fstream gDataFile;&lt;br /&gt;which will have to be connected to a file by an open() request:&lt;br /&gt;gDataFile.open(gFileName, ios::in | ios::out );&lt;br /&gt;The open request would specify ios::in (input) and ios::out (output); in some&lt;br /&gt;environments, the call to open() might also have to specify ios::binary. As this file&lt;br /&gt;is used for output, the operating system should create the file if it does not already exist.&lt;br /&gt;Next, we need to use the functions that move the "get" and "put" pointers.&lt;br /&gt;(Conceptually, these are separate pointers that can reference different positions in the&lt;br /&gt;file; in most implementations, they are locked together and will always refer to the same&lt;br /&gt;position.) The get pointer associated with an input stream can be moved using the&lt;br /&gt;seekg() function; similarly, the put pointer can be moved using the seekp() function.&lt;br /&gt;These functions take as arguments a byte offset and a reference point. The reference&lt;br /&gt;point is defined using an enumerated type defined in the iostream library; it can be&lt;br /&gt;ios::beg (start of file), ios::cur (current position), or ios::end (end of the file).&lt;br /&gt;So, for example, the call:&lt;br /&gt;gDataFile.seekg(600, ios::beg);&lt;br /&gt;would position the get pointer 600 bytes after the beginning of the file; while the call:&lt;br /&gt;gDataFile.seekp(0, ios::end);&lt;br /&gt;would position the put pointer at the end of the file.&lt;br /&gt;The libraries also have functions that can be used to ask the positions of these&lt;br /&gt;pointers; these are the tellp() and tellg() functions. You can find the size of a file,&lt;br /&gt;and hence the number of records in a record file, using the following code:&lt;br /&gt;gDataFile.seekg(0, ios::end);&lt;br /&gt;long pos = gDataFile.tellg();&lt;br /&gt;gNumRecs = pos / sizeof(Customer);&lt;br /&gt;fstream&lt;br /&gt;Positioning the&lt;br /&gt;get/put pointers&lt;br /&gt;Finding your current&lt;br /&gt;position in a file&lt;br /&gt;486 Examples using structs&lt;br /&gt;The call to seekg() moves the get pointer to the end of the file; the call to tellg()&lt;br /&gt;returns the byte position where the pointer is located, and so gives the length of the file&lt;br /&gt;(i.e. the number of bytes in the file). The number of records in the file can be obtained&lt;br /&gt;by dividing the file length by the record size.&lt;br /&gt;Data bytes can be copied between memory and file using the read and write&lt;br /&gt;functions:&lt;br /&gt;read(void *data, int size);&lt;br /&gt;write(const void *data, size_t size);&lt;br /&gt;(The prototypes for these functions may be slightly different in other versions of the&lt;br /&gt;iostream library. The type size_t is simply a typedef equivalent for unsigned int.)&lt;br /&gt;These functions need to be told the location in memory where the data are to be placed&lt;br /&gt;(or copied from) and the number of bytes that must be transferred.&lt;br /&gt;The first argument is a void*. In the case of write(), the data bytes are copied&lt;br /&gt;from memory to the disk so the memory data are unchanged, so the argument is a const&lt;br /&gt;void*. These void* parameters are the first example that we've seen of pointers.&lt;br /&gt;(Actually, the string library uses pointers as arguments to some functions, but we&lt;br /&gt;disguised them by changing the function interfaces so that they seemed to specify&lt;br /&gt;arrays).&lt;br /&gt;A pointer variable is a variable that contains the memory address of some other data&lt;br /&gt;element. Pointers are defined as derived types based on built in or programmer defined&lt;br /&gt;struct (and class) types:&lt;br /&gt;int *iptr;&lt;br /&gt;double *dptr;&lt;br /&gt;Rectangle *rptr;&lt;br /&gt;and, as a slightly special case:&lt;br /&gt;void *ptr_to_somedata;&lt;br /&gt;These definitions make iptr a variable that can hold the address of some integer data&lt;br /&gt;item, dptr a variable that holds the address of a double, and rptr a variable that holds&lt;br /&gt;the address where a Rectangle struct is located.&lt;br /&gt;The variable, ptr_to_somedata, is a void*. This means that it can hold the&lt;br /&gt;address of a data item of any kind. (Here void is not being used to mean empty, it is&lt;br /&gt;more that it is "unknown" or at least "unspecified").&lt;br /&gt;As any C programmer who has read this far will have noted, we have been carefully&lt;br /&gt;avoiding pointers. Pointers are all right when you get to know them, but they can be&lt;br /&gt;cruel to beginners. From now on, almost all your compile time and run-time errors are&lt;br /&gt;going to relate to the use of pointers.&lt;br /&gt;But in these calls to the read() and write() functions, there are no real problems.&lt;br /&gt;All that these functions require is the memory address of the data that are involved in&lt;br /&gt;Read and write&lt;br /&gt;transfers&lt;br /&gt;Memory address&lt;br /&gt;"Pointers"&lt;br /&gt;File of records 487&lt;br /&gt;the transfer. In this example, that is going to mean the address of some variable of a&lt;br /&gt;struct type Customer.&lt;br /&gt;In C and C++, you can get the address of any variable by using the &amp;amp; "address-of"&lt;br /&gt;operator. Try running the following program (addresses are by convention displayed in&lt;br /&gt;hexadecimal rather than as decimal numbers):&lt;br /&gt;float pi = 3.142;&lt;br /&gt;int array[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt;int data = 101;&lt;br /&gt;void *ptr;&lt;br /&gt;ptr = &amp;data;&lt;br /&gt;cout &lt;&lt; "data is at " &lt;&lt; hex &lt;&lt; ptr &lt;&lt; endl;&lt;br /&gt;ptr = &amp;pi;&lt;br /&gt;cout &lt;&lt; "pi is at " &lt;&lt; hex &lt;&lt; ptr &lt;&lt; endl;&lt;br /&gt;ptr = &amp;amp;(array[2]);&lt;br /&gt;cout &lt;&lt; "array[2] is at " &lt;&lt; hex &lt;&lt; ptr &lt;&lt; endl;&lt;br /&gt;ptr = &amp;amp;(array[3]);&lt;br /&gt;cout &lt;&lt; "array[3] is at " &lt;&lt; hex &lt;&lt; ptr &lt;&lt; endl;&lt;br /&gt;return 0;&lt;br /&gt;}&lt;br /&gt;You should be able to relate the addresses that you get to the models that you now have&lt;br /&gt;for how programs and data are arranged in memory. (If you find it hard to interpret the&lt;br /&gt;hexadecimal numbers, switch back to decimal outputs.)&lt;br /&gt;The &amp;amp; operator is used to get the addresses that must be passed to the read() and&lt;br /&gt;write() functions. If we have a variable:&lt;br /&gt;Customer c;&lt;br /&gt;we can get it loaded with data from a disk file using the call:&lt;br /&gt;gDataFile.read(&amp;amp;c, sizeof(Customer));&lt;br /&gt;or we can save its contents to disk by the call:&lt;br /&gt;gDataFile.write(&amp;amp;c, sizeof(Customer));&lt;br /&gt;(Some compilers and versions of the iostream library may require "(char*)" before the&lt;br /&gt;&amp;amp; in these calls. The &amp;amp; operator and related matters are discussed in Chapter 20.)&lt;br /&gt;The &amp;amp; "address-of"&lt;br /&gt;operator&lt;br /&gt;488 Examples using structs&lt;br /&gt;Specification&lt;br /&gt;Write a program that will help a salesperson keep track of customer records.&lt;br /&gt;The program is to:&lt;br /&gt;1 Maintain a file with records of customers. These records are to include the&lt;br /&gt;customer name, address, postcode, phone number, amount owed, and details of any&lt;br /&gt;items on order.&lt;br /&gt;2 Support the following options:&lt;br /&gt;• list all customers&lt;br /&gt;• list all customers who currently owe money&lt;br /&gt;• show the record of a named customer&lt;br /&gt;• create a record for a new customer&lt;br /&gt;• fill in an order for a customer (or clear the&lt;br /&gt;record for goods delivered and paid for)&lt;br /&gt;• quit.&lt;br /&gt;3 Have a list of all products stocked, along with their costs, and should provide a&lt;br /&gt;simple way for the salesperson to select a product by name when making up a&lt;br /&gt;customer order.&lt;br /&gt;The program is not to load the entire contents of the data file into memory. Records are&lt;br /&gt;to be fetched from the disk file when needed. The file will contain at most a few&lt;br /&gt;hundred records so sophisticated structures are not required.&lt;br /&gt;Design&lt;br /&gt;The overall structure of the program will be something like:&lt;br /&gt;Open the file and do any related initializations&lt;br /&gt;Interact with the salesperson, processing commands until&lt;br /&gt;the Quit command is entered&lt;br /&gt;Close the file&lt;br /&gt;These steps obviously get expanded into three main functions.&lt;br /&gt;The "Close File" function will probably be trivial, maybe nothing more than actually&lt;br /&gt;closing the file. Apart from actually opening the file (terminating execution if it won't&lt;br /&gt;open) the "Open File" function will probably have to do additional work, like&lt;br /&gt;determining how many records exist. During design of the main processing loop, we&lt;br /&gt;may identify data that should be obtained as the file is opened. So, function "open file"&lt;br /&gt;should be left for later consideration.&lt;br /&gt;The main processing loop in the routine that works with the salesperson will be&lt;br /&gt;along the following lines:&lt;br /&gt;First Iteration&lt;br /&gt;File of records 489&lt;br /&gt;"Run the shop"&lt;br /&gt;quitting = false&lt;br /&gt;while (not quitting)&lt;br /&gt;prompt salesperson for a command&lt;br /&gt;use command number entered to select&lt;br /&gt;• list all customers&lt;br /&gt;• list debtors&lt;br /&gt;…&lt;br /&gt;• deal with quit command, i.e. quitting = true&lt;br /&gt;Obviously, each of the options like "list all customers" becomes a separate function.&lt;br /&gt;Several of these functions will have to "get" a record from the file or "put" a record&lt;br /&gt;to file, so we can expect that they will share "get record", "put record" and possibly&lt;br /&gt;some other lower level functions.&lt;br /&gt;The UT functions developed in Chapter 12 (selection from menu, and keyword&lt;br /&gt;lookup) might be exploited. Obviously, the menu selection routine could handle the&lt;br /&gt;task of getting a command entered by the salesperson. In fact, if that existing routine is&lt;br /&gt;to be used there will be no further design work necessary for "Run the shop". The&lt;br /&gt;sketched code is easy to implement.&lt;br /&gt;So, the next major task is identifying the way the functions will handle the tasks like&lt;br /&gt;listing customers and getting orders. The routines for listing all customers and listing&lt;br /&gt;debtors are going to be very similar:&lt;br /&gt;"list all customers"&lt;br /&gt;if there are no customers&lt;br /&gt;just print some slogan and return&lt;br /&gt;for i = 0 , i &lt; number of customers,. i++&lt;br /&gt;get customer record i&lt;br /&gt;print details of customer&lt;br /&gt;"list debtors"&lt;br /&gt;initialize a counter to zero&lt;br /&gt;for i = 0 , i &lt; number of customers,. i++&lt;br /&gt;get customer record i&lt;br /&gt;if customer owes money&lt;br /&gt;print details of customer&lt;br /&gt;increment counter&lt;br /&gt;if counter is zero report "no debtors"&lt;br /&gt;You might be tempted to fold these functions into one, using an extra boolean argument&lt;br /&gt;to distinguish whether we are interested in all records or just those with debts.&lt;br /&gt;However, if the program were later made more elaborate you would probably find&lt;br /&gt;greater differences in the work done in these two cases (e.g. you might want the "list&lt;br /&gt;Second Iteration&lt;br /&gt;The "list …"&lt;br /&gt;functions&lt;br /&gt;490 Examples using structs&lt;br /&gt;debtors" operation to generate form letters suggesting that payment would be&lt;br /&gt;appreciated). Since the functions can be expected to become increasingly different, you&lt;br /&gt;might as well code them as two functions from the start.&lt;br /&gt;The sketched pseudo-code uses for loops to work through the file and relies on a&lt;br /&gt;(global?) variable that holds the number of records. As noted in the introduction to this&lt;br /&gt;section, it is easy to work out the number of records in a file like this; this would get&lt;br /&gt;done when the file is opened.&lt;br /&gt;The two functions could work with a local variable of a struct type "Customer".&lt;br /&gt;This would get passed as a reference parameter to a "get record" function, and as a&lt;br /&gt;"const reference" to a "print details" function.&lt;br /&gt;The two additional auxiliary functions, "get record" and "print details", also need to&lt;br /&gt;be sketched:&lt;br /&gt;"get record"&lt;br /&gt;convert record number to byte position&lt;br /&gt;read from file into record&lt;br /&gt;if i/o error&lt;br /&gt;print error message and exit&lt;br /&gt;"print details"&lt;br /&gt;output name, address, postcode, phone number&lt;br /&gt;if amount owed&lt;br /&gt;print the amount&lt;br /&gt;else ? (either blank, or "nothing owing")&lt;br /&gt;if any items on order&lt;br /&gt;print formatted list of items&lt;br /&gt;The specification didn't really tie down how information was to be displayed; we can&lt;br /&gt;chose the formats when doing the implementation.&lt;br /&gt;The other required functions are those to add a new customer record to the file, show&lt;br /&gt;the record of a customer identified by name, and place an order for a customer (the&lt;br /&gt;specification is imprecise, but presumably this is again going to be a customer identified&lt;br /&gt;by name).&lt;br /&gt;Adding a customer should be easy. The "add customer" routine would prompt the&lt;br /&gt;salesperson for the name, address, postcode, and phone number of the new customer.&lt;br /&gt;These data would be filled into a struct, then the struct would be written to the end of&lt;br /&gt;the existing file. The routine would have to update the global counter that records the&lt;br /&gt;number of records so that the listing routines would include the new record.&lt;br /&gt;"add customer"&lt;br /&gt;? check any maximum limits on the number of customers&lt;br /&gt;prompt for data like name, filling in a struct,&lt;br /&gt;set amount owed, and items on order to zero&lt;br /&gt;Auxiliary "get&lt;br /&gt;record" and "print&lt;br /&gt;details" functions&lt;br /&gt;Functions for other&lt;br /&gt;processing options&lt;br /&gt;File of records 491&lt;br /&gt;write the struct to the file&lt;br /&gt;update number of customers&lt;br /&gt;The file itself would not limit the number of records (well, not until it got to contain so&lt;br /&gt;many millions of records that it couldn't fit on a disk). But limits could (and in this case&lt;br /&gt;do) arise from other implementation decisions.&lt;br /&gt;Writing the struct to file would be handled by a "put record" function that matches&lt;br /&gt;the "get record":&lt;br /&gt;"put record"&lt;br /&gt;convert record number to byte position&lt;br /&gt;copy data from memory struct to file&lt;br /&gt;if i/o error&lt;br /&gt;print error message and exit&lt;br /&gt;The other two functions, "show customer" and "record order", both apparently need&lt;br /&gt;to load the record for a customer identified by name. The program could work reading&lt;br /&gt;each record from the file until it found the correct one. This would be somewhat costly&lt;br /&gt;even for little files of a few hundred records. It would be easier if the program kept a&lt;br /&gt;copy of the customer names in memory. This should be practical, the names would&lt;br /&gt;require much less storage than the complete records.&lt;br /&gt;It would be possible for the "open file" routine to read all existing records, copying&lt;br /&gt;the customer names into an array of names. This only gets done when the program&lt;br /&gt;starts up so later operations are not slowed. When new records are added to the file, the&lt;br /&gt;customer name gets added to this array.&lt;br /&gt;The memory array could hold small structures combining a name and a record&lt;br /&gt;number; these could be kept sorted by name so allowing binary search. This would&lt;br /&gt;necessitate a sort operation after the names were read from file in the "open file"&lt;br /&gt;routine, and an "insert" function that would first find the correct place for a new name&lt;br /&gt;and then move other existing names to higher array locations so as to make room for it.&lt;br /&gt;Alternatively, the array could just contain the names; the array indexes would&lt;br /&gt;correspond to the record numbers. Since the names wouldn't be in any particular order,&lt;br /&gt;the array would have to be searched linearly to find a customer. Although crude, this&lt;br /&gt;approach is quite acceptable in this specific context. These searches will occur when&lt;br /&gt;the salesperson enters a "show customer" or "place order" command. The salesperson&lt;br /&gt;will be expecting to spend many seconds reading the displayed details, or minutes&lt;br /&gt;adding new orders. So a tiny delay before the data appear isn't going to matter. A&lt;br /&gt;program can check many hundreds of names in less than a tenth of a second; so while a&lt;br /&gt;linear search of the table of names might be "slow" in computer terms, it is not slow in&lt;br /&gt;human terms and the times of human interaction are going to dominate the workings of&lt;br /&gt;this program.&lt;br /&gt;Further, we've got that UT_PickKeyWord() function. It searches an array of&lt;br /&gt;"words", either finding the match or listing the different possible matches. This would&lt;br /&gt;Getting the record for&lt;br /&gt;a named customer&lt;br /&gt;492 Examples using structs&lt;br /&gt;actually be quite useful here. If names can be equated with the UT_Words we can use&lt;br /&gt;the pick key word function to identify a name from its first few characters. Such an&lt;br /&gt;"intelligent" interactive response would help the salesperson who would only have to&lt;br /&gt;enter the first few characters of a name before either getting the required record or a list&lt;br /&gt;of the name and similar names.&lt;br /&gt;So, decisions: Customer names will be UT_Words (this limits them to less than 15&lt;br /&gt;characters which could be a problem), things like addresses might as well be UT_Texts.&lt;br /&gt;There will be a global array containing all the names. Functions that require the&lt;br /&gt;salesperson to select a customer will use the keyword search routine (automatically&lt;br /&gt;getting its ability to handle abbreviations.&lt;br /&gt;These decisions lead to the following design sketches for the two remaining&lt;br /&gt;processing options:&lt;br /&gt;"show customer"&lt;br /&gt;use keyword picker function to prompt for data (customer name)&lt;br /&gt;and find its match in names array&lt;br /&gt;(returns index of match)&lt;br /&gt;use Get Record to get the record corresponding to index&lt;br /&gt;Print Details of record got from file&lt;br /&gt;and&lt;br /&gt;"record order"&lt;br /&gt;use keyword picker function to prompt for data (customer name)&lt;br /&gt;and find its match in names array&lt;br /&gt;(returns index of match)&lt;br /&gt;use get record to get the record corresponding to index&lt;br /&gt;reset customer record to nothing ordered, nothing owed&lt;br /&gt;loop&lt;br /&gt;get next order item&lt;br /&gt;update amount owed&lt;br /&gt;until either no more order items or maximum number ordered&lt;br /&gt;put updated record back in file&lt;br /&gt;The loop getting items in function "record order" is once again a candidate for&lt;br /&gt;promotion to being a separate function. If it had to do all the work, function "record&lt;br /&gt;order" would become too complex. Its role should be one of setting up the context in&lt;br /&gt;which some other function can get the order items. So, its sketch should be revised to:&lt;br /&gt;"record order"&lt;br /&gt;use keyword picker function to prompt for data (customer name)&lt;br /&gt;and find its match in names array&lt;br /&gt;File of records 493&lt;br /&gt;(returns index of match)&lt;br /&gt;use get record to get the record corresponding to index&lt;br /&gt;get items&lt;br /&gt;put updated record back in file&lt;br /&gt;This new "get items" function now has to be planned. The specification stated that&lt;br /&gt;this routine should either make up an order for a customer or clear the record for goods&lt;br /&gt;delivered and paid for. This might not prove ideal in practice because it means that you&lt;br /&gt;can't deliver partial orders, and you can't accept supplementary orders; but it will do to&lt;br /&gt;start with. It means that the "get items" routine should start by clearing any existing&lt;br /&gt;record of amount owed and items ordered. Then the routine should ask the user&lt;br /&gt;whether any item is to be ordered. While the reply is "yes", the routine should get&lt;br /&gt;details of the item, add it to the record and its cost to the amount owed, and then again&lt;br /&gt;ask whether another item is to be ordered. The Customer struct can have an array to&lt;br /&gt;hold information about ordered items. Since this array will have a fixed size, the loop&lt;br /&gt;asking for items would need to terminate if the array gets filled up.&lt;br /&gt;The specification requires the program to have a table of goods that can be ordered&lt;br /&gt;and a convenient mechanism allowing the salesperson to enter the name of a chosen&lt;br /&gt;article. The key word picking function can again be pressed into service. There will&lt;br /&gt;need to be a global array with the names of the goods articles, and an associated array&lt;br /&gt;with their costs.&lt;br /&gt;An initial sketch for "get items" is:&lt;br /&gt;"get items"&lt;br /&gt;change amount owing data member of record to zero&lt;br /&gt;ask (YesNo() function) whether another item to be ordered&lt;br /&gt;while item to be ordered and space left&lt;br /&gt;use keyword picker function to prompt for&lt;br /&gt;data (item name) and find its match&lt;br /&gt;in goods array&lt;br /&gt;(returns index of match)&lt;br /&gt;copy name of item into record&lt;br /&gt;update amount owed by cost of item&lt;br /&gt;update count of items ordered&lt;br /&gt;ask (YesNo()) whether another item needed&lt;br /&gt;This has sketch has identified another function, YesNo(), that gets a yes/no input from&lt;br /&gt;the user.&lt;br /&gt;494 Examples using structs&lt;br /&gt;Open File revisited Earlier consideration of the open file function was deferred. Now, we have a better&lt;br /&gt;idea of what it must do. It should open the file (or terminate the program if the file&lt;br /&gt;won't open). It should then determine the number of records. Finally, it should scan&lt;br /&gt;through all records copying the customer names into an array in memory.&lt;br /&gt;The array for names has a fixed size. So there will be a limit on the number of&lt;br /&gt;customers. This will have to be catered for in the "add customer" function.&lt;br /&gt;What is a Customer? It is about time to make some decisions regarding the data.&lt;br /&gt;A Customer had better be a struct that has data members for:&lt;br /&gt;1 customer name (already decided to use a UT_Word, i.e. up to about 15 characters);&lt;br /&gt;2 customer address (a UT_Text, i.e. up to 60 characters);&lt;br /&gt;3 postcode and phone, these could also be UT_Words;&lt;br /&gt;4 amount owing (a double);&lt;br /&gt;5 a count of items on order;&lt;br /&gt;6 an array with the names of the items on order, need to fix a size for this.&lt;br /&gt;Other fields might need to be added later. The following structs declarations should&lt;br /&gt;suffice:&lt;br /&gt;#ifndef __MYCUSTOMER__&lt;br /&gt;#define __MYCUSTOMER__&lt;br /&gt;#include "UT.h"&lt;br /&gt;struct Date {&lt;br /&gt;int fDay, fMonth, fYear;&lt;br /&gt;};&lt;br /&gt;const int kMAXITEMS = 5;&lt;br /&gt;struct Customer {&lt;br /&gt;UT_Word fName;&lt;br /&gt;UT_Text fAddress;&lt;br /&gt;UT_Word fPostcode;&lt;br /&gt;UT_Word fDialcode;&lt;br /&gt;UT_Word fOrders[kMAXITEMS];&lt;br /&gt;int fNumOrder;&lt;br /&gt;double fAmountOwing;&lt;br /&gt;Date fLastOrder;&lt;br /&gt;};&lt;br /&gt;#endif&lt;br /&gt;Third iteration&lt;br /&gt;through the design&lt;br /&gt;File of records 495&lt;br /&gt;An extra Date struct has been declared and a Date data member has been included in&lt;br /&gt;the Customer struct. This code doesn't use dates; one of the exercises at the end of the&lt;br /&gt;chapter involves implementing a code to handle dates.&lt;br /&gt;As a Customer uses UT_Word and UT_Text this header file has to include the UT.h&lt;br /&gt;header that contains the typedef defining these character array types.&lt;br /&gt;The main implementation file is going to contain a number of global arrays:&lt;br /&gt;• gStock[], an array of UT_Words with names of items stocked by shop;&lt;br /&gt;• gItemCosts[], an array of doubles with the costs of items;&lt;br /&gt;• gCommands[], an array with the phrases that describe the commands that the&lt;br /&gt;salesperson can select;&lt;br /&gt;• gNames[], an array to hold customer names.&lt;br /&gt;Other global (or filescope) variables will be needed for the file name, the fstream object&lt;br /&gt;that gets attached to the file, and for a number of integer counters (number of records,&lt;br /&gt;number of commands, number of items in stock list).&lt;br /&gt;The functions have already been considered in detail and don't require further&lt;br /&gt;iterations of design. Their prototypes can now be defined:&lt;br /&gt;void GetRecord(Customer&amp;amp; rec, int cNum);&lt;br /&gt;void PutRecord(Customer&amp;amp; rec, int cNum);&lt;br /&gt;void PrintDetails(const Customer&amp;amp; c);&lt;br /&gt;void ShowCustomer(void);&lt;br /&gt;void ListAll(void);&lt;br /&gt;void ListDebtors(void);&lt;br /&gt;void AddCustomer(void);&lt;br /&gt;int YesNo(void);&lt;br /&gt;void GetItems(Customer&amp;amp; c);&lt;br /&gt;void RecordOrder(void);&lt;br /&gt;void RunTheShop(void);&lt;br /&gt;void OpenTheDataFile(void);&lt;br /&gt;void CloseTheDataFile(void);&lt;br /&gt;int main();&lt;br /&gt;Function prototypes&lt;br /&gt;496 Examples using structs&lt;br /&gt;Other linked files The code written specifically for this problem will have to be linked with the UT code&lt;br /&gt;from Chapter 12 so as to get the key word and menu selection functions.&lt;br /&gt;Quite a large number of systems header files will be needed. The programs uses&lt;br /&gt;both iostream and fstream libraries for files. The exit() function will get used to&lt;br /&gt;terminate the program if an i/o error occurs with the file accesses, so stdlib is also&lt;br /&gt;needed. Strings representing names will have to be copied using strcpy() from the&lt;br /&gt;string library. The "yes/no" function will have to check characters so may need the&lt;br /&gt;ctype header that defines standard functions like tolower(). Error checking might use&lt;br /&gt;assert.&lt;br /&gt;Implementation&lt;br /&gt;The implementation file will start with the #includes:&lt;br /&gt;#include &lt;iostream.h&gt;&lt;br /&gt;#include &lt;fstream.h&gt;&lt;br /&gt;#include &lt;stdlib.h&gt;&lt;br /&gt;#include &lt;string.h&gt;&lt;br /&gt;#include &lt;assert.h&gt;&lt;br /&gt;#include &lt;ctype.h&gt;&lt;br /&gt;#include "UT.h"&lt;br /&gt;#include "mycustomer.h"&lt;br /&gt;(If you think about it carefully, you will see that we end up #including UT.h twice; that&lt;br /&gt;sort of thing happens very easily and it is why we have those #ifdef … #endif brackets&lt;br /&gt;on all header files).&lt;br /&gt;The declarations of globals come next. Several are initialized:&lt;br /&gt;const char gFileName[] = "CustRec.XXX";&lt;br /&gt;const int kMAXCUSTOMERS = 200;&lt;br /&gt;fstream gDataFile;&lt;br /&gt;int gNumRecs;&lt;br /&gt;UT_Word gNames[kMAXCUSTOMERS];&lt;br /&gt;UT_Word gStock[] = {&lt;br /&gt;"Floppy disks",&lt;br /&gt;…&lt;br /&gt;"Marker pens",&lt;br /&gt;"Laser pointer"&lt;br /&gt;};&lt;br /&gt;double gItemCosts[] = {&lt;br /&gt;18.50, /* disks $18.50 per box */&lt;br /&gt;…&lt;br /&gt;6.50, /* pens */&lt;br /&gt;Header files needed&lt;br /&gt;File of records 497&lt;br /&gt;180.0 /* laser pointer */&lt;br /&gt;};&lt;br /&gt;int gNStock = sizeof(gStock) / sizeof(UT_Word);&lt;br /&gt;UT_Text gCommands[] = {&lt;br /&gt;"Quit",&lt;br /&gt;"List all customers",&lt;br /&gt;"List all debtors",&lt;br /&gt;"Add Customer",&lt;br /&gt;"Show Customer",&lt;br /&gt;"Record Order"&lt;br /&gt;};&lt;br /&gt;int gNCommands = sizeof(gCommands) / sizeof(UT_Text);&lt;br /&gt;In many ways it would be better to introduce a new struct that packages together an&lt;br /&gt;item name and its cost. Then we could have an array of these "costed item" structs&lt;br /&gt;which would reduce the chance of incorrect costs being associated with items.&lt;br /&gt;However, that would preclude the use of the existing keyword functions that require a&lt;br /&gt;simple array of UT_Words.&lt;br /&gt;The function definitions come next:&lt;br /&gt;void GetRecord(Customer&amp;amp; rec, int cNum)&lt;br /&gt;{&lt;br /&gt;/* cNum is customer number (0-based) */&lt;br /&gt;/* convert into offset into file */&lt;br /&gt;long where = cNum * sizeof(Customer);&lt;br /&gt;gDataFile.seekg(where, ios::beg);&lt;br /&gt;gDataFile.read(&amp;amp;rec, sizeof(Customer));&lt;br /&gt;if(!gDataFile.good()) {&lt;br /&gt;cout &lt;&lt; "Sorry, can't read the customer file"&lt;br /&gt;&lt;&lt; endl;&lt;br /&gt;exit(1);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;void PutRecord(Customer&amp;amp; rec, int cNum)&lt;br /&gt;{&lt;br /&gt;long where = cNum * sizeof(Customer);&lt;br /&gt;gDataFile.seekp(where, ios::beg);&lt;br /&gt;gDataFile.write(&amp;amp;rec, sizeof(Customer)); //maybe&lt;br /&gt;(char*)&amp;amp;rec&lt;br /&gt;if(!gDataFile.good()) {&lt;br /&gt;cout &lt;&lt; "Sorry, can't write to the customer file"&lt;br /&gt;&lt;&lt; endl;&lt;br /&gt;exit(1);&lt;br /&gt;}&lt;br /&gt;498 Examples using structs&lt;br /&gt;}&lt;br /&gt;Terminating the program after an i/o error may seem a bit severe, but really there isn't&lt;br /&gt;much else that we can do in those circumstances.&lt;br /&gt;void PrintDetails(const Customer&amp;amp; c)&lt;br /&gt;{&lt;br /&gt;cout &lt;&lt; "----" &lt;&lt; endl;&lt;br /&gt;cout &lt;&lt; "Customer Name : " &lt;&lt; c.fName &lt;&lt; endl;&lt;br /&gt;cout &lt;&lt; "Address : " &lt;&lt; c.fAddress &lt;&lt; ", "&lt;br /&gt;&lt;&lt; c.fPostcode &lt;&lt; endl;&lt;br /&gt;cout &lt;&lt; "Phone : " &lt;&lt; c.fDialcode &lt;&lt; endl;&lt;br /&gt;if(c.fAmountOwing &gt; 0.0)&lt;br /&gt;cout &lt;&lt; "Owes : $" &lt;&lt; c.fAmountOwing&lt;br /&gt;&lt;&lt; endl;&lt;br /&gt;else cout &lt;&lt; "Owes nothing" &lt;&lt; endl;&lt;br /&gt;if(c.fNumOrder == 1) cout &lt;&lt; "On order: " &lt;&lt; c.fOrders[0]&lt;br /&gt;&lt;&lt; endl;&lt;br /&gt;else&lt;br /&gt;if(c.fNumOrder &gt;0) {&lt;br /&gt;cout &lt;&lt; "On order" &lt;&lt; endl;&lt;br /&gt;for(int j = 0; j &lt; (c.fNumOrder-1); j++)&lt;br /&gt;cout &lt;&lt; c.fOrders[j] &lt;&lt; ", ";&lt;br /&gt;cout &lt;&lt; "and ";&lt;br /&gt;cout &lt;&lt; c.fOrders[c.fNumOrder-1] &lt;&lt; endl;&lt;br /&gt;}&lt;br /&gt;cout &lt;&lt; "---" &lt;&lt; endl;&lt;br /&gt;}&lt;br /&gt;The PrintDetails() function gets a little elaborate, but it is trying to provide a nice&lt;br /&gt;listing of items with commas and the word "and" in the right places.&lt;br /&gt;void ShowCustomer(void)&lt;br /&gt;{&lt;br /&gt;UT_Text aPrompt = "Customer Name : ";&lt;br /&gt;int who = UT_PickKeyWord(aPrompt, gNames, gNumRecs);&lt;br /&gt;if(who &lt; 0)&lt;br /&gt;return;&lt;br /&gt;Customer c;&lt;br /&gt;GetRecord(c, who);&lt;br /&gt;PrintDetails(c);&lt;br /&gt;}&lt;br /&gt;There is one problem in using the "pick keyword" function. If the salesperson enters&lt;br /&gt;something that doesn't match the start of any of the names, the error message is "there is&lt;br /&gt;no keyword …". Possibly the "pick keyword" function should have been designed to&lt;br /&gt;take an extra parameter that would be used for the error message.&lt;br /&gt;void ListAll(void)&lt;br /&gt;File of records 499&lt;br /&gt;{&lt;br /&gt;if(gNumRecs == 0) {&lt;br /&gt;cout &lt;&lt; "You have no customers" &lt;&lt; endl;&lt;br /&gt;return;&lt;br /&gt;}&lt;br /&gt;for(int i = 0; i &lt; gNumRecs; i++) {&lt;br /&gt;Customer c;&lt;br /&gt;GetRecord(c, i);&lt;br /&gt;PrintDetails(c);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;void ListDebtors(void)&lt;br /&gt;{&lt;br /&gt;int count = 0;&lt;br /&gt;for(int i = 0; i &lt; gNumRecs; i++) {&lt;br /&gt;Customer c;&lt;br /&gt;GetRecord(c, i);&lt;br /&gt;if(c.fAmountOwing &gt; 0.0) {&lt;br /&gt;PrintDetails(c);&lt;br /&gt;count++;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;if(count == 0) cout &lt;&lt; "Nothing owed" &lt;&lt; endl;&lt;br /&gt;}&lt;br /&gt;Function AddCustomer() checks whether the program's array of names is full and&lt;br /&gt;prevents extra names being added. The input statements use getline(). This is&lt;br /&gt;because addresses are going to be things like "234 High Street" which contain spaces.&lt;br /&gt;If we tried to read an address with something like cin &gt;&gt; c.fAddress, the address&lt;br /&gt;field would get "234", leaving "High …" etc to confuse the input to post code. The&lt;br /&gt;routine isn't robust; you can cause lots of troubles by entering names that are too long to&lt;br /&gt;fit in the specified data member.&lt;br /&gt;void AddCustomer(void)&lt;br /&gt;{&lt;br /&gt;if(gNumRecs == kMAXCUSTOMERS) {&lt;br /&gt;cout &lt;&lt; "Sorry, you will have to edit program "&lt;br /&gt;"before it can handle" &lt;&lt; endl;&lt;br /&gt;cout &lt;&lt; " more customers." &lt;&lt; endl;&lt;br /&gt;return;&lt;br /&gt;}&lt;br /&gt;Customer c;&lt;br /&gt;// N.B. This input routine is "unsafe"&lt;br /&gt;// there are no checks successful reads etc&lt;br /&gt;cout &lt;&lt; "Name : ";&lt;br /&gt;cin.getline(c.fName, UT_WRDLENGTH-1, '\n');&lt;br /&gt;500 Examples using structs&lt;br /&gt;cout &lt;&lt; "Address : ";&lt;br /&gt;cin.getline(c.fAddress, UT_TXTLENGTH-1, '\n');&lt;br /&gt;cout &lt;&lt; "Post code : ";&lt;br /&gt;cin.getline(c.fPostcode, UT_WRDLENGTH-1, '\n');&lt;br /&gt;cout &lt;&lt; "Phone : ";&lt;br /&gt;cin.getline(c.fDialcode, UT_WRDLENGTH-1, '\n');&lt;br /&gt;c.fNumOrder = 0;&lt;br /&gt;c.fAmountOwing = 0.0;&lt;br /&gt;PutRecord(c, gNumRecs);&lt;br /&gt;strcpy(gNames[gNumRecs], c.fName);&lt;br /&gt;gNumRecs++;&lt;br /&gt;}&lt;br /&gt;int YesNo(void)&lt;br /&gt;{&lt;br /&gt;char ch;&lt;br /&gt;cout &lt;&lt; "Order an item? (Y or N)";&lt;br /&gt;cin &gt;&gt; ch;&lt;br /&gt;ch = tolower(ch);&lt;br /&gt;return (ch == 'y');&lt;br /&gt;}&lt;br /&gt;void GetItems(Customer&amp;amp; c)&lt;br /&gt;{&lt;br /&gt;c.fAmountOwing = 0.0;&lt;br /&gt;int count = 0;&lt;br /&gt;while(YesNo() &amp;amp;&amp;amp; (count &lt;kMAXITEMS)) {&lt;br /&gt;UT_Text aPrompt = "Identify Type of Goods";&lt;br /&gt;int which =&lt;br /&gt;UT_PickKeyWord(aPrompt, gStock, gNStock);&lt;br /&gt;strcpy(c.fOrders[count], gStock[which]);&lt;br /&gt;c.fAmountOwing += gItemCosts[which];&lt;br /&gt;count++;&lt;br /&gt;}&lt;br /&gt;c.fNumOrder = count;&lt;br /&gt;}&lt;br /&gt;Look a bug in GetItems()! Can you spot it? It isn't serious, the program won't&lt;br /&gt;crash. But it makes the user interaction clumsy.&lt;br /&gt;If you can't spot the bug, run the code and try to order more than the limit of five&lt;br /&gt;items. You should then observe a certain clumsiness.&lt;br /&gt;The bug can be fixed by a trivial change to the code.&lt;br /&gt;File of records 501&lt;br /&gt;void RecordOrder(void)&lt;br /&gt;{&lt;br /&gt;UT_Text aPrompt = "Enter Customer Name";&lt;br /&gt;int who = UT_PickKeyWord(aPrompt, gNames, gNumRecs);&lt;br /&gt;Customer c;&lt;br /&gt;GetRecord(c, who);&lt;br /&gt;GetItems(c);&lt;br /&gt;PutRecord(c, who);&lt;br /&gt;}&lt;br /&gt;void RunTheShop(void)&lt;br /&gt;{&lt;br /&gt;UT_Text aPrompt = "Command";&lt;br /&gt;int quitting = 0;&lt;br /&gt;while(!quitting) {&lt;br /&gt;int command =&lt;br /&gt;UT_MenuSelect(aPrompt,gCommands,&lt;br /&gt;gNCommands, 0);&lt;br /&gt;// Need to consume any trailing spaces or newlines&lt;br /&gt;// after the command number&lt;br /&gt;char ch;&lt;br /&gt;cin.get(ch);&lt;br /&gt;while(ch != '\n')&lt;br /&gt;cin.get(ch);&lt;br /&gt;switch(command) {&lt;br /&gt;case 0: /* Quit */&lt;br /&gt;quitting = 1;&lt;br /&gt;break;&lt;br /&gt;case 1: /* List all customers */&lt;br /&gt;ListAll();&lt;br /&gt;break;&lt;br /&gt;case 2: /* List all debtors */&lt;br /&gt;ListDebtors();&lt;br /&gt;break;&lt;br /&gt;case 3: /* Add Customer */&lt;br /&gt;AddCustomer();&lt;br /&gt;break;&lt;br /&gt;case 4: /* Show Customer */&lt;br /&gt;ShowCustomer();&lt;br /&gt;break;&lt;br /&gt;case 5: /* Record Order */&lt;br /&gt;RecordOrder();&lt;br /&gt;break;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;502 Examples using structs&lt;br /&gt;}&lt;br /&gt;The RunTheShop() function has one complication. The salesperson has to enter a&lt;br /&gt;number when picking the required menu option. The digits get read but any trailing&lt;br /&gt;spaces and newlines will still be waiting in the input stream. These could confuse&lt;br /&gt;things if the next function called needed to read a string, a character, or a complete line.&lt;br /&gt;So the input stream is cleaned up by reading characters until get the '\n' at the end of the&lt;br /&gt;input line.&lt;br /&gt;void OpenTheDataFile(void)&lt;br /&gt;{&lt;br /&gt;// Open the file, allow creation if not already there&lt;br /&gt;gDataFile.open(gFileName, ios::in | ios::out );&lt;br /&gt;// may need also ios::binary&lt;br /&gt;if(!gDataFile.good()) {&lt;br /&gt;cout &lt;&lt; "? Couldn't open the file. Sorry." &lt;&lt; endl;&lt;br /&gt;exit(1);&lt;br /&gt;}&lt;br /&gt;gDataFile.seekg(0, ios::end);&lt;br /&gt;long pos = gDataFile.tellg();&lt;br /&gt;gNumRecs = pos / sizeof(Customer);&lt;br /&gt;assert(gNumRecs &lt;= kMAXCUSTOMERS);&lt;br /&gt;for(int i = 0; i &lt; gNumRecs; i++) {&lt;br /&gt;Customer c;&lt;br /&gt;GetRecord(c, i);&lt;br /&gt;strcpy(gNames[i], c.fName);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;Logically, there is no way that the file could hold more than the maximum number of&lt;br /&gt;records (the file has to be created by this program and the "add customer" function&lt;br /&gt;won't let it happen).&lt;br /&gt;Don't believe it. Murphy's law applies. The program can go wrong if the file is too&lt;br /&gt;large, so it will go wrong. (Something will happen like a user concatenating two data&lt;br /&gt;files to make one large one.) Since the program will overwrite arrays if the file is too&lt;br /&gt;large, it better not even run. Hence the assert() checking the number of records.&lt;br /&gt;void CloseTheDataFile(void)&lt;br /&gt;{&lt;br /&gt;gDataFile.close();&lt;br /&gt;}&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt;OpenTheDataFile();&lt;br /&gt;RunTheShop();&lt;br /&gt;File of records 503&lt;br /&gt;CloseTheDataFile();&lt;br /&gt;return 0;&lt;br /&gt;}&lt;br /&gt;On the whole, the program runs OK:&lt;br /&gt;Command&lt;br /&gt;Enter option number in range 1 to 6, or ? for help&lt;br /&gt;6&lt;br /&gt;Enter Customer Name&lt;br /&gt;Ga&lt;br /&gt;Possible matching keywords are:&lt;br /&gt;Gates, B.&lt;br /&gt;Garribaldi, J&lt;br /&gt;Gamble,P&lt;br /&gt;Gam&lt;br /&gt;i.e. Gamble,P&lt;br /&gt;Order an item? (Y or N)y&lt;br /&gt;Identify Type of Goods&lt;br /&gt;Las&lt;br /&gt;i.e. Laser pointer&lt;br /&gt;Order an item? (Y or N)Y&lt;br /&gt;Identify Type of Goods&lt;br /&gt;Tone&lt;br /&gt;i.e. Toner&lt;br /&gt;Order an item? (Y or N)n&lt;br /&gt;Command&lt;br /&gt;Enter option number in range 1 to 6, or ? for helpEnter option&lt;br /&gt;number in range 1 to 6, or ? for help&lt;br /&gt;3&lt;br /&gt;----&lt;br /&gt;Customer Name : Gates, B.&lt;br /&gt;Address : The Palace, Seattle, 923138&lt;br /&gt;Phone : 765 456 222&lt;br /&gt;Owes : $186.5&lt;br /&gt;On order&lt;br /&gt;Laser pointer, and Marker pens&lt;br /&gt;---&lt;br /&gt;----&lt;br /&gt;Customer Name : Gamble,P&lt;br /&gt;Address : 134 High St, 89143&lt;br /&gt;Phone : 1433&lt;br /&gt;Owes : $220&lt;br /&gt;On order&lt;br /&gt;Laser pointer, and Toner&lt;br /&gt;---&lt;br /&gt;504 Examples using structs&lt;br /&gt;EXERCISES&lt;br /&gt;1 Fix the "bug" in GetItems() from 17.3.&lt;br /&gt;2 Change the way that the Customer records program handles the names to the alternative&lt;br /&gt;approach described in the text (sorted array of name-index number structures).&lt;br /&gt;3 Implement code to support the Date data structure and arrange that Customer orders include a&lt;br /&gt;Date.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1020589601308586811-1021872569039402768?l=debug-guide.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://debug-guide.blogspot.com/feeds/1021872569039402768/comments/default' title='Poskan Komentar'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1020589601308586811&amp;postID=1021872569039402768' title='0 Komentar'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1020589601308586811/posts/default/1021872569039402768'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1020589601308586811/posts/default/1021872569039402768'/><link rel='alternate' type='text/html' href='http://debug-guide.blogspot.com/2008/01/examples-using-structs.html' title='Examples using structs'/><author><name>doni</name><uri>http://www.blogger.com/profile/15287923128168749403</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1020589601308586811.post-999075380933215598</id><published>2008-01-29T09:58:00.001-08:00</published><updated>2008-01-29T09:58:56.056-08:00</updated><title type='text'>Enum, Struct, and Union</title><content type='html'>16 Enum, Struct, and Union&lt;br /&gt;This chapter introduces three simple kinds of programmer defined data types.&lt;br /&gt;You aren't limited to the compiler provided char, int, double data types and their&lt;br /&gt;derivatives like arrays. You can define your own data types. Most of Part IV of this&lt;br /&gt;text is devoted to various forms of programmer defined type. Here we introduce just&lt;br /&gt;three relatively simple kinds. These are almost the same as the equivalents provided in&lt;br /&gt;C; in contrast, C has no equivalent to the more sophisticated forms of programmer&lt;br /&gt;defined type introduced in Part IV.&lt;br /&gt;Enumerated types, treated in section 16.1, are things that you should find useful in&lt;br /&gt;that they can improve the readability of your programs and they allow the compiler to&lt;br /&gt;do a little extra type checking that can eliminate certain forms of error. Although&lt;br /&gt;useful, enumerated types are not that major a factor in C++ programming.&lt;br /&gt;Structs are the most important of the three language elements introduced here,&lt;br /&gt;section 16.2. They do have wider roles, but here we are interested in their primary role&lt;br /&gt;which is the grouping of related data.&lt;br /&gt;Unions: treat them as a "read only" feature of C++. You will sometimes see unions&lt;br /&gt;being employed in library code that you use, but it is unlikely that you will find any real&lt;br /&gt;application for unions in the programs that you will be writing.&lt;br /&gt;16&lt;br /&gt;16.1 ENUMERATED TYPES&lt;br /&gt;You often need to use simple integer constants to represent domain specific data. For&lt;br /&gt;example, suppose you needed to represent the colour of an automobile. You could have&lt;br /&gt;the following:&lt;br /&gt;const int cRED = 0;&lt;br /&gt;const int cBLUE = 1;&lt;br /&gt;…&lt;br /&gt;int auto_colour;&lt;br /&gt;460 Enum, struct, and union&lt;br /&gt;auto_colour = cBLUE;&lt;br /&gt;This is quite workable. But there are no checks. Since variable auto_colour is an&lt;br /&gt;integer, the following assignments are valid:&lt;br /&gt;auto_colour = -1;&lt;br /&gt;…&lt;br /&gt;auto_colour = rand();&lt;br /&gt;But of course, these statements lose the semantics; variable auto_colour doesn't any&lt;br /&gt;longer represent a colour, it is just an integer.&lt;br /&gt;It is nice to be able to tell the compiler:&lt;br /&gt;"Variable auto_colour is supposed to represent a colour, that is one of the&lt;br /&gt;defined set of choices RED, BLUE, …."&lt;br /&gt;and then have the compiler check, pretty thoroughly, that auto_colour is only used in&lt;br /&gt;this way throughout the code.&lt;br /&gt;This is the role of enums or enumerated types. If you think how they are actually&lt;br /&gt;implemented, they are just an alternative way of declaring a set of integer constants and&lt;br /&gt;defining some integer variables. But they also introduce new distinct types and allow&lt;br /&gt;the compiler to do type checking. It is this additional type checking that makes enums&lt;br /&gt;worthwhile.&lt;br /&gt;The following is a simple example of an enum declaration:&lt;br /&gt;enum Colour { eRED, eBLUE, eYELLOW, eGREEN, eSILVERGREY,&lt;br /&gt;eBURGUNDY };&lt;br /&gt;The entries in the enumeration list are just names (of constant values). The same rules&lt;br /&gt;apply as for any other C++ names: start with a letter, contain letters digits and&lt;br /&gt;underscores. However, by convention, the entries in the enum list should have names&lt;br /&gt;that start with 'e' and continue with a sequence of capital letters. This makes enum&lt;br /&gt;values stand out in your code.&lt;br /&gt;With Colour now defined, we can have variables of type Colour:&lt;br /&gt;Colour auto_colour;&lt;br /&gt;…&lt;br /&gt;auto_colour = eBURGUNDY;&lt;br /&gt;The compiler will now reject things like auto_colour = 4. Depending on the&lt;br /&gt;compiler you are using you may get just "Warning – anachronism", or you may get an&lt;br /&gt;error (really, you should get an error).&lt;br /&gt;What about the "enumerators" eRED, eBLUE etc? What are they?&lt;br /&gt;enums and type&lt;br /&gt;checking&lt;br /&gt;Naming convention&lt;br /&gt;for enums&lt;br /&gt;enumerators&lt;br /&gt;Enumerated types 461&lt;br /&gt;Really, they are integers of some form. The compiler may chose to represent them&lt;br /&gt;using shorts, or as unsigned chars. Really, it isn't any of your business how your&lt;br /&gt;compiler represents them.&lt;br /&gt;The compiler chooses distinct values for each member of an enumeration.&lt;br /&gt;Normally, the first member has value 0, the second is 1, and so forth. So in this&lt;br /&gt;example, eRED would be a kind of integer constant 0, eSILVERGREY would be 4.&lt;br /&gt;Note the effect of the type checking:&lt;br /&gt;auto_colour = 4; // Wrong, rejected or at least&lt;br /&gt;// warned by compiler&lt;br /&gt;auto_colour = eSILVERGREY; // Fine, set auto_colour to&lt;br /&gt;// (probably) the value 4&lt;br /&gt;It isn't the values that matter, it is the types. The value 4 is an integer and can't be&lt;br /&gt;directly assigned to a Colour variable. The constant eSILVERGREY is a Colour&lt;br /&gt;enumerator and can be assigned to a Colour variable.&lt;br /&gt;You can select for yourself the integer values for the different members of the&lt;br /&gt;enumeration, provided of course that you keep them all distinct. You won't have cause&lt;br /&gt;to do this yourself; but you should be able to read code like:&lt;br /&gt;enum MagicEnum { eMERLIN = 17, eGANDALF = 103, eFRED = 202 };&lt;br /&gt;Treat this as one of those "read only" features of C++. It is only in rare circumstances&lt;br /&gt;that you want to define specific values for the members of the enumeration. (Defining&lt;br /&gt;specific values means that you aren't really using the enum consistently; at some places&lt;br /&gt;in your code you intend to treat enums as characters or integers.)&lt;br /&gt;Output of enums&lt;br /&gt;Enums are fine in the program, but how do you get them transferred using input and&lt;br /&gt;output statements?&lt;br /&gt;Well, with some difficulty!&lt;br /&gt;An enum is a form of integer. You can try:&lt;br /&gt;cout &lt;&lt; auto_colour;&lt;br /&gt;and you might get a value like 0, or 3 printed. More likely, the compiler will give you&lt;br /&gt;an error message (probably something rather obscure like "ambiguous reference to&lt;br /&gt;overloaded operator function"). While the specific message may not be clear, the&lt;br /&gt;compiler's unhappiness is obvious. It doesn't really know how you want the enum&lt;br /&gt;printed.&lt;br /&gt;You can tell the compiler that it is OK to print the enum as an integer:&lt;br /&gt;462 Enum, struct, and union&lt;br /&gt;cout &lt;&lt; int(auto_colour);&lt;br /&gt;The function like form int(auto_colour) tells the compiler to convert the data value&lt;br /&gt;auto_colour to an integer. The statement is then cout &lt;&lt; integer which the&lt;br /&gt;compiler knows to convert into a call to a PrintInteger() routine. (The compiler&lt;br /&gt;doesn't need to generate any code to do the conversion from enum to integer. This&lt;br /&gt;conversion request is simply a way for the programmer to tell the compiler that here it&lt;br /&gt;is intended that the enum be regarded as just another integer).&lt;br /&gt;Printing an enum as an integer is acceptable if the output is to a file that is going to&lt;br /&gt;be read back later by the program. Human users aren't going to be pleased to get output&lt;br /&gt;like:&lt;br /&gt;Engine: 1.8 litre&lt;br /&gt;Doors: 4&lt;br /&gt;Colour: 5&lt;br /&gt;If you are generating output that is to be read by a human user, you should convert the&lt;br /&gt;enum value into an appropriate character string. The easiest way is to use a switch&lt;br /&gt;statement:&lt;br /&gt;switch(auto_colour) {&lt;br /&gt;eRED: cout &lt;&lt; "Red"; break;&lt;br /&gt;eBLUE: cout &lt;&lt; "Blue"; break;&lt;br /&gt;…&lt;br /&gt;eBURGUNDY:&lt;br /&gt;cout &lt;&lt; "Burgundy"; break;&lt;br /&gt;}&lt;br /&gt;Input&lt;br /&gt;If your program is reading a file, then this will contain integer values for enums; for&lt;br /&gt;example, the file could have the partial contents&lt;br /&gt;1.8 4 5&lt;br /&gt;for an entry describing a four door, burgundy coloured car with 1.8 litre engine But you&lt;br /&gt;can't simply have code like:&lt;br /&gt;double engine_size;&lt;br /&gt;int num_doors;&lt;br /&gt;Colour auto_colour;&lt;br /&gt;…&lt;br /&gt;input &gt;&gt; engine_size;&lt;br /&gt;input &gt;&gt; num_doors ;&lt;br /&gt;input &gt;&gt; auto_colour;&lt;br /&gt;Enumerated types 463&lt;br /&gt;The compiler gets stuck when it reaches input &gt;&gt; auto_colour;. The compiler's&lt;br /&gt;translation tables let it recognize input &gt;&gt; engine_size as a form of "input gives to&lt;br /&gt;double" (so it puts in a call to a ReadDouble() routine), and similarly input &gt;&gt;&lt;br /&gt;num_doors can be converted to a call to ReadInt(). But the compiler's standard&lt;br /&gt;translation tables say nothing about input &gt;&gt; Colour.&lt;br /&gt;The file contains an integer; so you had better read an integer:&lt;br /&gt;int temp;&lt;br /&gt;input &gt;&gt; temp;&lt;br /&gt;Now you have an integer value that you hope represents one of the members of the&lt;br /&gt;enum Colour. Of course you must still set the Colour variable auto_colour. You&lt;br /&gt;can't simply have an assignment:&lt;br /&gt;auto_colour = temp;&lt;br /&gt;because the reason we started all of this fuss was to make such assignments illegal!&lt;br /&gt;Here, you have to tell the compiler to suspend its type checking mechanisms and&lt;br /&gt;trust you. You can say "I know this integer value will be a valid Colour, do the&lt;br /&gt;assignment." The code is&lt;br /&gt;auto_colour = Colour(temp);&lt;br /&gt;Input from file will use integers, but what of input from a human user?&lt;br /&gt;Normally, if you are working with enumerated types like this, you will be prompting&lt;br /&gt;the user to make a selection from a list of choices:&lt;br /&gt;Colour GetColour()&lt;br /&gt;{&lt;br /&gt;cout &lt;&lt; "Please enter preferred colour, select from "&lt;br /&gt;&lt;&lt; endl;&lt;br /&gt;cout &lt;&lt; "1\tRed" &lt;&lt; endl;&lt;br /&gt;cout &lt;&lt; "2\tBlue" &lt;&lt; endl;&lt;br /&gt;…&lt;br /&gt;cout &lt;&lt; "6\tBurgundy" &lt;&lt; endl;&lt;br /&gt;for(;;) {&lt;br /&gt;int choice;&lt;br /&gt;cin &gt;&gt; choice;&lt;br /&gt;switch(choice) {&lt;br /&gt;case 1: return eRED;&lt;br /&gt;case 2: return eBLUE;&lt;br /&gt;…&lt;br /&gt;case 6: return eBURGUNDY;&lt;br /&gt;}&lt;br /&gt;cout &lt;&lt; "There is no choice #" &lt;&lt; choice &lt;&lt; endl;&lt;br /&gt;464 Enum, struct, and union&lt;br /&gt;cout &lt;&lt; "Please select from 1 Red, 2 Blue … "&lt;br /&gt;" 6, Burgundy" &lt;&lt; endl;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;(Note, internally eRED may be 0, eBLUE may be 1 etc. But you will find users generally&lt;br /&gt;prefer option lists starting with 1 rather than 0. So list the choices starting from 1 and&lt;br /&gt;make any adjustments necessary when converting to internal form.)&lt;br /&gt;Other uses of enums&lt;br /&gt;You do get cases like Colour auto_colour where it is appropriate to use an&lt;br /&gt;enumerated type; but they aren't that common (except in text books). But there is&lt;br /&gt;another place where enums are very widely used.&lt;br /&gt;Very often you need to call a routine specifying a processing option from a fixed set:&lt;br /&gt;DrawString – this function needs to be given the string to be&lt;br /&gt;displayed and a style which should be one of&lt;br /&gt;Plain&lt;br /&gt;Bold&lt;br /&gt;Italic&lt;br /&gt;Outline&lt;br /&gt;AlignObjects – this function needs to be given an array with the&lt;br /&gt;object identifiers, a count, and an alignment specification which&lt;br /&gt;should be one of&lt;br /&gt;LeftAligned&lt;br /&gt;Centred&lt;br /&gt;RightAligned&lt;br /&gt;You can define integer constants:&lt;br /&gt;const int cPLAIN = 0;&lt;br /&gt;const int cBOLD = 1;&lt;br /&gt;…&lt;br /&gt;and have an integer argument in your argument list:&lt;br /&gt;void DrawString(char txt[], int style);&lt;br /&gt;but the compiler can't check that you only use valid styles from the set of defined&lt;br /&gt;constants and so erroneous code like&lt;br /&gt;DrawString("silly", 12345);&lt;br /&gt;Enumerated types 465&lt;br /&gt;gets through the compiler to cause problems at run time.&lt;br /&gt;But, if you code using an enumerated type, you do get compile time checks:&lt;br /&gt;enum TextStyles { ePLAIN, eBOLD, eITALIC, eOUTLINE };&lt;br /&gt;void DrawString(char txt[], TextStyles style);&lt;br /&gt;Now, calls like:&lt;br /&gt;DrawString("Home run", eBOLD);&lt;br /&gt;are fine, while erroneous calls like&lt;br /&gt;DrawString("Say what?", 59);&lt;br /&gt;are stomped on by the compiler (again, you may simply get a warning, but it is more&lt;br /&gt;likely that you will get an error message about a missing function).&lt;br /&gt;16.2 STRUCTS&lt;br /&gt;It is rare for programs to work with simple data values, or even arrays of data values,&lt;br /&gt;that are individually meaningful. Normally, you get groups of data values that belong&lt;br /&gt;together.&lt;br /&gt;Let's pick on those children again. This time suppose we want records of children's&lt;br /&gt;heights in cm, weights in kilos, age (years and months), and gender. We expect to have&lt;br /&gt;a collection of about one thousand children and need to do things like identify those&lt;br /&gt;with extreme heights or extreme weights.&lt;br /&gt;We can simply use arrays:&lt;br /&gt;const int kMAXCHILDREN = 1000;&lt;br /&gt;double heights[kMAXCHILDREN];&lt;br /&gt;double weights[kMAXCHILDREN];&lt;br /&gt;int years[kMAXCHILDREN];&lt;br /&gt;int months[kMAXCHILDREN];&lt;br /&gt;char gender[kMAXCHILDREN];&lt;br /&gt;…&lt;br /&gt;// read file with data, file terminated by sentinel data&lt;br /&gt;// value with zero height&lt;br /&gt;count = 0;&lt;br /&gt;infile &gt;&gt; h;&lt;br /&gt;while(h &gt; 0.0) {&lt;br /&gt;heights[count] = h;&lt;br /&gt;infile &gt;&gt; weights[count] &gt;&gt; years[count] &gt;&gt;&lt;br /&gt;months[count] &gt;&gt; gender[count];&lt;br /&gt;The need for&lt;br /&gt;"structs"&lt;br /&gt;466 Enum, struct, and union&lt;br /&gt;count++;&lt;br /&gt;infile &gt;&gt; h;&lt;br /&gt;}&lt;br /&gt;…&lt;br /&gt;Now heights[5], weights[5], …, gender[5] all relate to the same child. But if&lt;br /&gt;we are using arrays, this relationship is at most implicit.&lt;br /&gt;There is no guarantee that we can arrange that the data values that logically belong&lt;br /&gt;together actually stay together. After all, there is nothing to stop one from sorting the&lt;br /&gt;heights array so that these values are in ascending order, at the same time losing the&lt;br /&gt;relationship between the data value in heights[5] and those in weights[5] …&lt;br /&gt;gender[5].&lt;br /&gt;A programming language must provide a way for the programmer to identify groups&lt;br /&gt;of related data. In C++, you can use struct.&lt;br /&gt;A C++ struct declaration allows you to specify a grouping of data variables:&lt;br /&gt;struct Child {&lt;br /&gt;double height;&lt;br /&gt;double weight;&lt;br /&gt;int years;&lt;br /&gt;int months;&lt;br /&gt;char gender;&lt;br /&gt;};&lt;br /&gt;After reading such a declaration, the compiler "knows what a Child is"; for the&lt;br /&gt;purposes of the rest of the program, the compiler knows that a Child is a data structure&lt;br /&gt;containing two doubles, two integers, and a character. The compiler works out the&lt;br /&gt;basic size of such a structure (it would probably be 25 bytes); it may round this size up&lt;br /&gt;to some larger size that it finds more convenient (e.g. 26 bytes or 28 bytes). It adds the&lt;br /&gt;name Child to its list of type names.&lt;br /&gt;This grouping of data is the primary role of a struct. In C++, structs are simply a&lt;br /&gt;special case of classes and they can have perform roles other than this simple grouping&lt;br /&gt;of data elements. However it is useful to make a distinction. In the examples in this&lt;br /&gt;book, structs will only be used as a way of grouping related data elements.&lt;br /&gt;The term "record" is quite often used instead of struct when describing programs.&lt;br /&gt;The individual data elements within a struct are said to be "fields", or "data members".&lt;br /&gt;The preferred C++ terminology is "data members".&lt;br /&gt;A declaration doesn't create any variables, it just lets the compiler know about a new&lt;br /&gt;type of data element that it should add to the standard char, long, double etc. But, once&lt;br /&gt;a struct declaration has been read, you can start to define variables of the new type:&lt;br /&gt;Child cute;&lt;br /&gt;Child kid;&lt;br /&gt;Child brat;&lt;br /&gt;Have to be able to&lt;br /&gt;group related data&lt;br /&gt;elements&lt;br /&gt;Declaring structs&lt;br /&gt;"record", "field",&lt;br /&gt;"data member"&lt;br /&gt;Defining variables of&lt;br /&gt;struct types&lt;br /&gt;Definitions of some&lt;br /&gt;variables of struct&lt;br /&gt;type&lt;br /&gt;Structs 467&lt;br /&gt;and arrays of variables of this type:&lt;br /&gt;Child surveyset[kMAXCHILDREN];&lt;br /&gt;Each of these Child variables has its own doubles recording height and weight, its own&lt;br /&gt;ints for age details, and a char gender flag.&lt;br /&gt;The definition of a variable of struct type can include the data needed to initialize its&lt;br /&gt;data members:&lt;br /&gt;Child example = {&lt;br /&gt;125.0, 32.4, 13, 2, 'f'&lt;br /&gt;};&lt;br /&gt;An instance of a struct can be defined along with the declaration:&lt;br /&gt;struct Rectangle {&lt;br /&gt;int left, top;&lt;br /&gt;int width, height;&lt;br /&gt;} r1, r2, r3;&lt;br /&gt;This declares the form of a Rectangle and defines three instances. This style is widely&lt;br /&gt;use in C programs, but it is one that you should avoid. A declaration should introduce a&lt;br /&gt;new data type; you should make this step separate from any variable definitions. The&lt;br /&gt;construct is actually a source of some compile-time errors. If you forget the ';' that&lt;br /&gt;terminates a structure declaration, the compiler can get quite lost trying to interpret the&lt;br /&gt;next few program elements as being names of variables (e.g. the input struct Rect {&lt;br /&gt;… } struct Point {…} int main() { … } will make a compiler quite unhappy).&lt;br /&gt;Programs have to be able to manipulate the values in the data members of a struct&lt;br /&gt;variable. Consequently, languages must provide a mechanism for referring to a&lt;br /&gt;particular data member of a given struct variable.&lt;br /&gt;Most programming languages use the same approach. They use compound names&lt;br /&gt;made up of the variable name and a qualifying data member name. For example, if you&lt;br /&gt;wanted to check whether brat's height exceeded a limit, then in C++ you would write:&lt;br /&gt;if(brat.height &gt; h_limit)&lt;br /&gt;…&lt;br /&gt;Similarly, if you want to set cute's weight to 35.4 kilos, you would write:&lt;br /&gt;cute.weight = 35.4;&lt;br /&gt;Most statements and expressions will reference individual data members of a struct,&lt;br /&gt;but assignment of complete structures is permitted:&lt;br /&gt;Child Tallest;&lt;br /&gt;Initialization of&lt;br /&gt;structs&lt;br /&gt;Accessing data&lt;br /&gt;members of a&lt;br /&gt;variable of a struct&lt;br /&gt;type&lt;br /&gt;Fields of a variable&lt;br /&gt;identified using&lt;br /&gt;compound names&lt;br /&gt;Assignment of structs&lt;br /&gt;468 Enum, struct, and union&lt;br /&gt;Tallest.height = 0;&lt;br /&gt;for(int i = 0; i &lt; NumChildren; i++)&lt;br /&gt;if(surveyset[i].height &gt; Tallest.height)&lt;br /&gt;Tallest = surveyset[i];&lt;br /&gt;Compilers generally handle such assignments by generating code using a "blockmove".&lt;br /&gt;A blockmove (which is often an actual instruction built into the machine hardware)&lt;br /&gt;copies a block of bytes from one location to another. The compiler knows the size of&lt;br /&gt;the structs so it codes a blockmove for the appropriate number of bytes.&lt;br /&gt;In C++, a struct declaration introduces a new type. Once you have declared:&lt;br /&gt;struct Point {&lt;br /&gt;int x;&lt;br /&gt;int y;&lt;br /&gt;};&lt;br /&gt;You can define variables of type Point:&lt;br /&gt;Point p1, p2;&lt;br /&gt;In C, struct (and enum) declarations don't make the struct name (or enum name) a&lt;br /&gt;new type. You must explicitly tell the compiler that you want a new type name to be&lt;br /&gt;available. This is done using a typedef. There are a variety of styles. Two common&lt;br /&gt;styles are:&lt;br /&gt;struct Point {&lt;br /&gt;int x;&lt;br /&gt;int y;&lt;br /&gt;};&lt;br /&gt;typedef struct Point Point;&lt;br /&gt;or:&lt;br /&gt;typedef struct _xpt {&lt;br /&gt;int x;&lt;br /&gt;int y;&lt;br /&gt;} Point;&lt;br /&gt;Similarly, enums require typedefs.&lt;br /&gt;enum Colour { eRED, eBLUE, eGREEN };&lt;br /&gt;typedef enum Colour Colour;&lt;br /&gt;You will see such typedefs in many of the C libraries that you get to use from your C++&lt;br /&gt;programs.&lt;br /&gt;Struct and enum&lt;br /&gt;declarations in C&lt;br /&gt;libraries&lt;br /&gt;typedef&lt;br /&gt;Structs 469&lt;br /&gt;Structs and functions A function can have arguments of struct types. Like simple variables, structs can be&lt;br /&gt;passed by value or by reference. If a struct is passed by value, it is handled like an&lt;br /&gt;assignment – a blockmove is done to copy the bytes of the structure onto the stack.&lt;br /&gt;Generally, because of the cost of the copying and the need to use up stack space, you&lt;br /&gt;should avoid passing large structs by value. If a function uses a struct as an "input&lt;br /&gt;parameter", its prototype should specify the struct as const reference, e.g.:&lt;br /&gt;void PrintChildRecord(const struct&amp;amp; theChild)&lt;br /&gt;{&lt;br /&gt;cout &lt;&lt; "Height " &lt;&lt; theChild.height &lt;&lt; …&lt;br /&gt;…&lt;br /&gt;}&lt;br /&gt;Functions can have structs as their return values. The following illustrates a function&lt;br /&gt;that gets an "car record" filled in:&lt;br /&gt;struct car {&lt;br /&gt;double engine_size;&lt;br /&gt;int num_doors;&lt;br /&gt;Colour auto_colour;&lt;br /&gt;};&lt;br /&gt;car GetAutoDetails()&lt;br /&gt;{&lt;br /&gt;car temp;&lt;br /&gt;cout &lt;&lt; "Select model, GL (1), GLX (2), SX (3), TX2(4) : "&lt;br /&gt;;&lt;br /&gt;int model;&lt;br /&gt;cin &gt;&gt; model;&lt;br /&gt;while((model &lt;&gt;4)) {&lt;br /&gt;cout &lt;&lt; "Model value must be in range 1…4" &lt;&lt; endl;&lt;br /&gt;cout &lt;&lt; "Select model :"&lt;br /&gt;cin &gt;&gt; model;&lt;br /&gt;}&lt;br /&gt;switch(model) {&lt;br /&gt;case 1: temp.engine_size = 1.8; temp.num_doors = 3; break;&lt;br /&gt;case 2: …&lt;br /&gt;}&lt;br /&gt;temp.colour = GetColour();&lt;br /&gt;return temp;&lt;br /&gt;}&lt;br /&gt;Although this is permitted, it should not be overused. Returning a struct result doesn't&lt;br /&gt;matter much with a small structure like struct car, but if your structures are large&lt;br /&gt;this style becomes expensive both in terms of space and data copying operations.&lt;br /&gt;Code using the GetAutoDetails() function would have to be something like:&lt;br /&gt;car purchasers_choice;&lt;br /&gt;Function with a&lt;br /&gt;struct as a returned&lt;br /&gt;value&lt;br /&gt;Local variable of&lt;br /&gt;return type defined&lt;br /&gt;Data entered into&lt;br /&gt;fields of local struct&lt;br /&gt;variable&lt;br /&gt;return the local struct&lt;br /&gt;as the value&lt;br /&gt;470 Enum, struct, and union&lt;br /&gt;…&lt;br /&gt;purchasers_choice = GetAutoDetails();&lt;br /&gt;The instructions generated would normally be relatively clumsy. The stack frame setup&lt;br /&gt;for the function would have a "return value area" sufficient in size to hold a car record;&lt;br /&gt;there would be a separate area for the variable temp defined in the function. The return&lt;br /&gt;statement would copy the contents of temp into the "return value area" of the stack&lt;br /&gt;frame. The data would then again be copied in the assignment to purchasers_choice.&lt;br /&gt;If you needed such a routine, you might be better to have a function that took a&lt;br /&gt;reference argument:&lt;br /&gt;void GetAutoDetails(struct car&amp;amp; temp)&lt;br /&gt;{&lt;br /&gt;cout &lt;&lt; "Select model, GL (1), GLX (2), SX (3), TX2(4) : "&lt;br /&gt;;&lt;br /&gt;…&lt;br /&gt;temp.colour = GetColour();&lt;br /&gt;return ;&lt;br /&gt;}&lt;br /&gt;with calling code like:&lt;br /&gt;car purchasers_choice;&lt;br /&gt;…&lt;br /&gt;GetAutoDetails(purchasers_choice);&lt;br /&gt;16.3 UNIONS&lt;br /&gt;Essentially, unions define a set of different interpretations that can be placed on the data&lt;br /&gt;content area of a struct. For you, "unions" should be a "read-only" feature of C++. It&lt;br /&gt;may be years before you get to write code where it might be appropriate for you to&lt;br /&gt;define a new union. However, you will be using libraries of C code, and some C++&lt;br /&gt;libraries, where unions are employed and so you need to be able to read and understand&lt;br /&gt;code that utilizes unions.&lt;br /&gt;Unions are most easily understood from real examples The following examples are&lt;br /&gt;based on code from Xlib. This is a C library for computers running Unix (or variations&lt;br /&gt;like Mach or Linux). The Xlib library provides the code needed for a program running&lt;br /&gt;on a computer to communicate with an X-terminal. X-terminals are commonly used&lt;br /&gt;when you want a multi-window style of user interface to Unix.&lt;br /&gt;An X-terminal is a graphics display device that incorporates a simple&lt;br /&gt;microprocessor and memory. The microprocessor in the X-terminal does part of the&lt;br /&gt;work of organizing the display, so reducing the computational load on the main&lt;br /&gt;computer.&lt;br /&gt;Unions 471&lt;br /&gt;When the user does something like move the mouse, type a character, or click an&lt;br /&gt;action button, the microprocessor packages this information and sends it in a message to&lt;br /&gt;the controlling program running on the main computer.&lt;br /&gt;In order to keep things relatively simple, all such messages consist of a 96 byte&lt;br /&gt;block of data. Naturally, different actions require different data to be sent. A mouse&lt;br /&gt;movement needs a report of where the mouse is now located, a keystroke action needs&lt;br /&gt;to be reported in terms of the symbol entered.&lt;br /&gt;Xlib-based programs use XEvent unions to represent these 96 byte blocks of data.&lt;br /&gt;The declaration for this union is&lt;br /&gt;typedef union _XEvent {&lt;br /&gt;int type;&lt;br /&gt;XAnyEvent xany;&lt;br /&gt;XButtonEvent xbutton;&lt;br /&gt;XMotionEvent xmotion;&lt;br /&gt;XCreateWindowEvent xcreatewindow;&lt;br /&gt;…&lt;br /&gt;…&lt;br /&gt;} XEvent;&lt;br /&gt;This declaration means that an XEvent may simply contain an integer (and 92 bytes of&lt;br /&gt;unspecified data), or it may contain an XAnyEvent, or it may contain an XButtonEvent,&lt;br /&gt;or …. There are about thirty different messages that an Xterminal can send, so there are&lt;br /&gt;thirty different alternative interpretations specified in the union declaration.&lt;br /&gt;Each of these different messages has a struct declaration that specifies the data that&lt;br /&gt;that kind of message will contain. Two of these structs are:&lt;br /&gt;typedef struct {&lt;br /&gt;int type;&lt;br /&gt;unsigned long serial;&lt;br /&gt;Bool send_event;&lt;br /&gt;Display *display;&lt;br /&gt;Window window;&lt;br /&gt;Window root;&lt;br /&gt;Window subwindow;&lt;br /&gt;Time time;&lt;br /&gt;int x, y;&lt;br /&gt;int x_root, y_root;&lt;br /&gt;unsigned int state;&lt;br /&gt;unsigned int button;&lt;br /&gt;Bool same_screen;&lt;br /&gt;} XButtonEvent;&lt;br /&gt;typedef struct {&lt;br /&gt;int type;&lt;br /&gt;unsigned long serial;&lt;br /&gt;Bool send_event;&lt;br /&gt;union declaration&lt;br /&gt;Declaration of&lt;br /&gt;alternative structs&lt;br /&gt;that can be found in&lt;br /&gt;an XEvent&lt;br /&gt;472 Enum, struct, and union&lt;br /&gt;Display *display;&lt;br /&gt;Window window;&lt;br /&gt;int x, y;&lt;br /&gt;int width, height;&lt;br /&gt;int border_width;&lt;br /&gt;Bool override_redirect;&lt;br /&gt;} XCreateWindowEvent;&lt;br /&gt;As illustrated in Figure 16.1, the first part of any message is a type code. The way&lt;br /&gt;that the rest of the message bytes are used depends on the kind of message.&lt;br /&gt;type type type type&lt;br /&gt;serial&lt;br /&gt;send_event&lt;br /&gt;window&lt;br /&gt;root&lt;br /&gt;subwindow&lt;br /&gt;time&lt;br /&gt;x&lt;br /&gt;y&lt;br /&gt;x_root&lt;br /&gt;y_root&lt;br /&gt;state&lt;br /&gt;button&lt;br /&gt;same_screen&lt;br /&gt;serial&lt;br /&gt;send_event&lt;br /&gt;window&lt;br /&gt;x&lt;br /&gt;y&lt;br /&gt;width&lt;br /&gt;height&lt;br /&gt;border&lt;br /&gt;override&lt;br /&gt;serial&lt;br /&gt;send_event&lt;br /&gt;window&lt;br /&gt;root&lt;br /&gt;subwindow&lt;br /&gt;time&lt;br /&gt;x&lt;br /&gt;y&lt;br /&gt;x_root&lt;br /&gt;y_root&lt;br /&gt;state&lt;br /&gt;hint&lt;br /&gt;same_screen&lt;br /&gt;XCreateWindowEvent XEvent&lt;br /&gt;XButtonEvent XMotionEvent&lt;br /&gt;Figure 16.1 XEvents – an example of a union from the Xlib library.&lt;br /&gt;Unions 473&lt;br /&gt;If you have an XEvent variable ev, you can access its type field using the "."&lt;br /&gt;operator just like accessing a data field in a normal structure:&lt;br /&gt;XEvent ev;&lt;br /&gt;…&lt;br /&gt;switch(ev.type) {&lt;br /&gt;…&lt;br /&gt;}&lt;br /&gt;If you know that ev really encodes an XCreateWindowEvent and you want to work&lt;br /&gt;out the area of the new window, you can use code like:&lt;br /&gt;area = ev.xcreatewindow.width * eve.xcreatewindow.height;&lt;br /&gt;The appropriate data fields are identified using a doubly qualified name. The variable&lt;br /&gt;name is qualified by the name of the union type that is appropriate for the kind of record&lt;br /&gt;known to be present (so, for an XCreateWindowEvent, you start with&lt;br /&gt;ev.xcreatewindow). This name is then further qualified by the name of the data&lt;br /&gt;member that should be accessed (ev.xcreatewindow.width).&lt;br /&gt;A programmer writing the code to deal with such messages knows that a message&lt;br /&gt;will start with a type code. The Xlib library has a series of #defined constants, e.g.&lt;br /&gt;ButtonPress, DestroyNotify, MotionNotify; the value in the type field of a message will&lt;br /&gt;correspond to one of these constants. This allows messages from an Xterminal to be&lt;br /&gt;handled as follows:&lt;br /&gt;XEvent eV;&lt;br /&gt;…&lt;br /&gt;/* Code that gets calls the Unix OS and&lt;br /&gt;gets details of the next message from the Xterminal&lt;br /&gt;copied into eV */&lt;br /&gt;…&lt;br /&gt;switch(ev.type) {&lt;br /&gt;caseButtonPress:&lt;br /&gt;/* code doing something depending on where the button was&lt;br /&gt;pressed, access using xbutton variant from union */&lt;br /&gt;if((ev.xbutton.x &gt; x_low) &amp;amp;&amp;amp; (ev.xbutton.x &lt; x_high) &amp;amp;&amp;amp; …&lt;br /&gt;break;&lt;br /&gt;case MotionNotify:&lt;br /&gt;/* user has moved pointer, get details of when this&lt;br /&gt;happened&lt;br /&gt;and decide what to do, access using xmotion variant from&lt;br /&gt;union */&lt;br /&gt;thetime = ev.xmotion.time;&lt;br /&gt;…&lt;br /&gt;Doubly qualified&lt;br /&gt;names to access data&lt;br /&gt;members of variants&lt;br /&gt;in union&lt;br /&gt;474 Enum, struct, and union&lt;br /&gt;break;&lt;br /&gt;case CreateNotify:&lt;br /&gt;/* We have a new window, code here that looks at where and&lt;br /&gt;what size, access using xcreatewindow variant of union */&lt;br /&gt;int hpos = ev.xcreatewindow.x;&lt;br /&gt;…&lt;br /&gt;break;&lt;br /&gt;…&lt;br /&gt;}&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1020589601308586811-999075380933215598?l=debug-guide.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://debug-guide.blogspot.com/feeds/999075380933215598/comments/default' title='Poskan Komentar'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1020589601308586811&amp;postID=999075380933215598' title='0 Komentar'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1020589601308586811/posts/default/999075380933215598'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1020589601308586811/posts/default/999075380933215598'/><link rel='alternate' type='text/html' href='http://debug-guide.blogspot.com/2008/01/enum-struct-and-union.html' title='Enum, Struct, and Union'/><author><name>doni</name><uri>http://www.blogger.com/profile/15287923128168749403</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1020589601308586811.post-4197101593884749582</id><published>2008-01-29T09:57:00.000-08:00</published><updated>2008-01-29T09:58:11.610-08:00</updated><title type='text'>Design C++</title><content type='html'>15 Design and&lt;br /&gt;documentation : 1&lt;br /&gt;The examples given in earlier chapters, particularly Chapter 12, have in a rather&lt;br /&gt;informal way illustrated a design style known as "top-down functional decomposition".&lt;br /&gt;"Top down functional decomposition" is a limited approach to design. It works very&lt;br /&gt;well for simple scientific and engineering applications that have the basic structure:&lt;br /&gt;initialize&lt;br /&gt;get the input data&lt;br /&gt;process the data&lt;br /&gt;print the results&lt;br /&gt;and where the "data" are something simple and homogeneous, like the array of double&lt;br /&gt;precision numbers in the heat diffusion example. Top down functional decomposition&lt;br /&gt;is not a good strategy for the overall design of more complex programs, such as those&lt;br /&gt;that are considered in Parts IV and V. However, it does reappear there too, in a minor&lt;br /&gt;role, when defining the individual "behaviours of objects".&lt;br /&gt;Although the approach is limited, it is simple and it does apply to a very large&lt;br /&gt;number of simple programs. So, it is worth learning this design approach at least as a&lt;br /&gt;beginning.&lt;br /&gt;The first section in this chapter simply summarizes materials from earlier examples&lt;br /&gt;using them to illustrate a basic strategy for program development. The second section&lt;br /&gt;looks briefly at some of the ways that you can document design decisions. Here, simple&lt;br /&gt;textual documentation is favoured.&lt;br /&gt;15&lt;br /&gt;15.1 TOP DOWN FUNCTIONAL DECOMPOSITION&lt;br /&gt;As it says, you start the top with "program", decompose it into functions, then you&lt;br /&gt;iterate along the list of functions going down one level into each to decompose into&lt;br /&gt;452 Design and documentation&lt;br /&gt;auxiliary functions. The process is repeated until the auxiliary functions are so simple&lt;br /&gt;that they can be coded directly.&lt;br /&gt;Note the focus on functions. You don't usually have to bother much about the data&lt;br /&gt;because the data will be simple – a shared array or something similar.&lt;br /&gt;You begin with a phrase or one sentence summary that defines the program:&lt;br /&gt;• The program models a two-dimensional heat diffusion experiment. e.g..&lt;br /&gt;• The program plays the game of "hangman".&lt;br /&gt;and try to get a caricature sketch of the main() function. This should be some slight&lt;br /&gt;variation of the code at the start of this chapter.&lt;br /&gt;Figure 15.1 illustrates initial decompositions for main() in the two example&lt;br /&gt;programs.&lt;br /&gt;Program Heat&lt;br /&gt;Program Hangman&lt;br /&gt;main&lt;br /&gt;Initialize PlayGame AnotherGame&lt;br /&gt;Initialize Heat Diffuse Show&lt;br /&gt;main&lt;br /&gt;Figure 15.1 From program specification to initial functional decomposition.&lt;br /&gt;The sketched functions are:&lt;br /&gt;Program Heat's main():&lt;br /&gt;get iteration limit and print frequency&lt;br /&gt;call Initialize() to initialize grid&lt;br /&gt;loop&lt;br /&gt;call HeatDiffuse() to update values in grid&lt;br /&gt;if time to print, call Show()&lt;br /&gt;Program Hangman's main():&lt;br /&gt;Initialize()&lt;br /&gt;Beginning&lt;br /&gt;Top down functional decomposition 453&lt;br /&gt;do&lt;br /&gt;PlayGame()&lt;br /&gt;while AnotherGame()&lt;br /&gt;You aim for this first stage is to get this initial sketch for main() and a list of "top&lt;br /&gt;level functions", each of which should be characterized by a phrase or sentence that&lt;br /&gt;summarizes what it does:&lt;br /&gt;AnotherGame()&lt;br /&gt;Get and use a Yes/No input from user,&lt;br /&gt;return "true" if Yes was input&lt;br /&gt;PlayGame()&lt;br /&gt;Organize the playing of one complete hangman game&lt;br /&gt;HeatDiffuse()&lt;br /&gt;Recalculate temperature at each point on grid.&lt;br /&gt;You now consider each of these top-level functions in isolation. It is all very well to&lt;br /&gt;say "Organize playing of game" or "Recalculate temperatures" but what do these&lt;br /&gt;specifications really mean.&lt;br /&gt;It is essentially the same process as in the first step. You identify component&lt;br /&gt;operations involved in "organizing the playing of the game" or whatever, and you work&lt;br /&gt;out the sequence in which these operations occur. This gives you a caricature sketch for&lt;br /&gt;the code of the top-level function that you are considering and a list of second level&lt;br /&gt;functions. Thus, HeatDiffuse() gets expanded to:&lt;br /&gt;HeatDiffuse&lt;br /&gt;get copy of grid values&lt;br /&gt;double loop&lt;br /&gt;for each row do&lt;br /&gt;for each col do&lt;br /&gt;using copy work out average temp.&lt;br /&gt;of environment of grid pt.&lt;br /&gt;calculate new temp based on current&lt;br /&gt;and environment&lt;br /&gt;store in grid&lt;br /&gt;reset centre point to flame temperature&lt;br /&gt;with a number of additional functions identified: CopyGrid(), Averageof-&lt;br /&gt;Neighbors(), and NewTemperature().&lt;br /&gt;The products of this design step are once again the sketches of functions and the lists&lt;br /&gt;of the additional auxiliary functions together with one sentence specifications for each:&lt;br /&gt;CopyGrid()&lt;br /&gt;Copy contents of grid into separate data area.&lt;br /&gt;AverageofNeighbors()&lt;br /&gt;Products of&lt;br /&gt;beginning step&lt;br /&gt;Second step&lt;br /&gt;454 Design and documentation&lt;br /&gt;For a given grid point, this finds the average of the&lt;br /&gt;temperatures of the eight neighboring points.&lt;br /&gt;This process is repeated until no more functions need be introduced. The&lt;br /&gt;relationships amongst the functions can be summarized in a "call graph" like that shown&lt;br /&gt;in Figure 15.2.&lt;br /&gt;Program Heat&lt;br /&gt;main&lt;br /&gt;Initialize Heat Diffuse Show&lt;br /&gt;CopyGrid AverageofNeighbors NewTemperature CodeTemperature&lt;br /&gt;TemperatureAt&lt;br /&gt;Figure 15.2 "Call graph" for a program.&lt;br /&gt;The next stage in the design process really focuses on data. You have to resolve&lt;br /&gt;how the functions communicate and decide whether they share access to any global (or&lt;br /&gt;filescope) data. For example, you may have decided that function TemperatureAt()&lt;br /&gt;gets the temperature at a specified grid point, but you have still to determine how the&lt;br /&gt;function "knows" which point and how it accesses the grid.&lt;br /&gt;The "input/output" argument lists of all the functions, together with their return&lt;br /&gt;types should now be defined. A table summarizing any filescope or global data should&lt;br /&gt;be made up.&lt;br /&gt;Generally, you need to plan some tests. A program like Hangman does not require&lt;br /&gt;any elaborate preplanned tests; the simple processing performed can be easily checked&lt;br /&gt;through interaction with the program. The Heat program was checked by working out&lt;br /&gt;in advance the expected results for some simple cases (e.g. the heated point at 1000°C&lt;br /&gt;and eight neighbors at 25°C) and using the debugger to check that the calculated "new&lt;br /&gt;temperatures" were correct. Such methods suffice for simple programs but as you get&lt;br /&gt;to move elaborate programs, you need more elaborate preplanned tests. These should&lt;br /&gt;be thought about at this stage, and a suitable test plan composed.&lt;br /&gt;The final outputs that you want from the design process will include:&lt;br /&gt;1. A sketch for main() summarising the overall processing sequence.&lt;br /&gt;2. A list of function prototypes along with one sentence descriptions of what these&lt;br /&gt;functions do.&lt;br /&gt;N-steps&lt;br /&gt;Sorting out data and&lt;br /&gt;communications&lt;br /&gt;Planning test data&lt;br /&gt;Finalising the design&lt;br /&gt;Top down functional decomposition 455&lt;br /&gt;char CodeTemperature(double temp);&lt;br /&gt;Converts temperature to a letter code; different&lt;br /&gt;letters correspond to different temperature ranges.&lt;br /&gt;void Show(const Grid g);&lt;br /&gt;Iterates over grid printing contents row by row,&lt;br /&gt;uses CodeTemperature() to convert temp. to letter.&lt;br /&gt;3. A list of any typedef types (e.g. typedef double Grid[kROWS][kCOLS]) and a list&lt;br /&gt;defining constants used by the program.&lt;br /&gt;4. A table summarizing global and filescope data.&lt;br /&gt;5. A more detailed listing of the functions giving the pseudo-code outlines for each.&lt;br /&gt;6. A test plan.&lt;br /&gt;It is only once you have this information that it becomes worth starting coding.&lt;br /&gt;Although you will often complete the design for the entire program and then start on&lt;br /&gt;implementation, if the program is "large" like the Hangman example then it may be&lt;br /&gt;worth designing and implementing a simplified version that is then expanded to&lt;br /&gt;produce the final product.&lt;br /&gt;15.2 DOCUMENTING A DESIGN&lt;br /&gt;For most programs, the best documentation will be the design outlines just described.&lt;br /&gt;You essentially have two documents. The first contains items 1…4. It is a kind of&lt;br /&gt;executive summary and is what you would provide to a manager, to coworkers, or to the&lt;br /&gt;teaching assistant who will be marking your program. The second document contains&lt;br /&gt;the pseudo-code outlines for the functions. This you will use to guide your&lt;br /&gt;implementation. Pseudo-code is supposed to be easier to read than actual code, so you&lt;br /&gt;keep this document for use by future "maintenance programmers" who may have to&lt;br /&gt;implement extensions to your code (they should also be left details of how to retest the&lt;br /&gt;code to check that everything works after alterations).&lt;br /&gt;This documentation is all textual. Sometimes, diagrams are required. Call graphs,&lt;br /&gt;like that in Figure 15.2, are often requested. While roughly sketched call graphs are&lt;br /&gt;useful while you are performing the design process and need to keep records as you&lt;br /&gt;move from stage to stage, they don't really help that much in documenting large&lt;br /&gt;programs. Figure 15.3 illustrates some of the problems with "call graphs".&lt;br /&gt;The call graph for the sort program using Quicksort may be correct but it really fails&lt;br /&gt;to capture much of the behaviour of that recursive system. The call graph for the&lt;br /&gt;Hangman program is incomplete. But even the part shown is overwhelming in its&lt;br /&gt;complexity.&lt;br /&gt;456 Design and documentation&lt;br /&gt;Program Sort&lt;br /&gt;Program Hangman&lt;br /&gt;main&lt;br /&gt;Initialize PlayGame AnotherGame&lt;br /&gt;Partition&lt;br /&gt;main&lt;br /&gt;SelectionSort&lt;br /&gt;Quicksort&lt;br /&gt;CG_Initialize&lt;br /&gt;ShowGuess PickWord GetGuessedChar ShowCartoonPart CG_FrameWindow&lt;br /&gt;srand&lt;br /&gt;TickCount&lt;br /&gt;strlen&lt;br /&gt;CG_PutCharacter&lt;br /&gt;Figure 15.3 Further examples of "call graphs".&lt;br /&gt;Psychologists estimate that the human brain can work with about seven items of&lt;br /&gt;information; if you attempt to handle more, you forget or confuse things. A complete&lt;br /&gt;call graph has too much information present and so does not provide a useful abstract&lt;br /&gt;overview.&lt;br /&gt;You could break the graph down into subparts, separating PlayGame's call graph&lt;br /&gt;from the overall program call graph. But such graphs still fail to convey any idea of the&lt;br /&gt;looping structure (the fact that some functions are called once, others many times).&lt;br /&gt;Overall, call graph diagrams aren't a particularly useful form of documentation.&lt;br /&gt;Sometimes, "flowcharts" are requested rather than pseudo-code outlines for&lt;br /&gt;functions. "Flowcharts" are old; they are contemporary with early versions of&lt;br /&gt;FORTRAN. Flowcharting symbols correspond pretty much to the basic programming&lt;br /&gt;constructs of that time, and lack convenient ways of representing multiway selections&lt;br /&gt;(switch() statements etc).&lt;br /&gt;At one time, complete programs were diagrammed using flowcharts. Such program&lt;br /&gt;flowcharts aren't that helpful, for the same reason that full call graphs aren't that helpful.&lt;br /&gt;There is too much detail in the diagram, so it doesn't effectively convey a program's&lt;br /&gt;structure.&lt;br /&gt;It is possible to "flowchart" individual functions, an example in flowcharting style is&lt;br /&gt;shown in Figure 15.4. Most people find pseudo-code outlines easier to understand.&lt;br /&gt;Documenting a design 457&lt;br /&gt;Frame window&lt;br /&gt;Pick word&lt;br /&gt;Show guess&lt;br /&gt;initialize counts&lt;br /&gt;etc&lt;br /&gt;Is game&lt;br /&gt;over?&lt;br /&gt;Get guessed character&lt;br /&gt;Check for match&lt;br /&gt;Match any?&lt;br /&gt;ShowGuess&lt;br /&gt;increment count of matched&lt;br /&gt;Show Cartoon Part&lt;br /&gt;increment count of errors&lt;br /&gt;work out setting of game-&lt;br /&gt;over flag&lt;br /&gt;No Yes&lt;br /&gt;No Yes&lt;br /&gt;Figure 15.4 "Flowchart" representation, an alternative to a pseudo-code outline of a&lt;br /&gt;function.&lt;br /&gt;On the whole, diagrams aren't that helpful. The lists of functions, the pseudo-code&lt;br /&gt;outlines etc are easier to understand and so it isn't worth trying to generate&lt;br /&gt;diagrammatic representations of the same information.&lt;br /&gt;However, you may have access to a CASE ("Computer Assisted Software&lt;br /&gt;Engineering") program. These programs have a user interface somewhat similar to a&lt;br /&gt;drawing package. There is a palette of tools that you can use to place statement&lt;br /&gt;sequences, conditional tests, iterative constructs etc. Dialogs allow you to enter details&lt;br /&gt;of the operations involved.&lt;br /&gt;The CASE tool can generate the final function prototypes, and the code for their&lt;br /&gt;implementation, from the information that you provide in these dialogs. If you use such&lt;br /&gt;a program, you automatically get a diagrammatic representation of your program along&lt;br /&gt;with the code.&lt;br /&gt;458 Design and documentation&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1020589601308586811-4197101593884749582?l=debug-guide.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://debug-guide.blogspot.com/feeds/4197101593884749582/comments/default' title='Poskan Komentar'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1020589601308586811&amp;postID=4197101593884749582' title='0 Komentar'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1020589601308586811/posts/default/4197101593884749582'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1020589601308586811/posts/default/4197101593884749582'/><link rel='alternate' type='text/html' href='http://debug-guide.blogspot.com/2008/01/design-c.html' title='Design C++'/><author><name>doni</name><uri>http://www.blogger.com/profile/15287923128168749403</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1020589601308586811.post-2624034335219629042</id><published>2008-01-29T09:41:00.000-08:00</published><updated>2008-01-29T09:56:50.565-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>Tools C++</title><content type='html'>14 Tools&lt;br /&gt;This chapter introduces two powerful support tools.&lt;br /&gt;14&lt;br /&gt;14.1 THE "CODE COVERAGE" TOOL&lt;br /&gt;A "code coverage" tool helps you determine whether you have properly tested your&lt;br /&gt;program. Think about all the if … else … and switch() statements in your code.&lt;br /&gt;Each involves optionally executed code, with calls to other functions where there are&lt;br /&gt;further conditional constructs. These conditional constructs mean that there can be a&lt;br /&gt;very large number of different execution paths through your code.&lt;br /&gt;You can never be certain that you have tested all paths through a complex program;&lt;br /&gt;so there will always be chances that some paths have errors where data are combined&lt;br /&gt;incorrectly. But if you have never tested certain paths, there can be gross errors that&lt;br /&gt;will crash your program.&lt;br /&gt;The simple programs that we have considered so far don't really justify the use of a&lt;br /&gt;code coverage tool; after all, things simulating the π-cannon don't involve that many&lt;br /&gt;choices and there is very little dependence on different inputs. But as you move to&lt;br /&gt;more complex things, like some of the "tree algorithms" in Part IV, code coverage tools&lt;br /&gt;become more and more necessary. The "tree algorithms" build up complicated data&lt;br /&gt;structures that depend on the input data. Rules implemented in the algorithms define&lt;br /&gt;how these structures are to change as new data elements get added. Some of these rules&lt;br /&gt;apply to relatively rare special cases that occur only for specific combinations of data&lt;br /&gt;values. The code for the "tree algorithms" has numerous paths, including those for&lt;br /&gt;these rare special cases.&lt;br /&gt;How can you test all paths? Basically, you have to chose different input data so as&lt;br /&gt;to force all processing options to be executed. Choosing the data is sometimes hard;&lt;br /&gt;you may think that you have included examples that cover all cases, but it is difficult to&lt;br /&gt;be certain.&lt;br /&gt;This is where code coverage tools get involved. The basic idea is that the compiler&lt;br /&gt;and run-time system should help you check that your tests have at least executed the&lt;br /&gt;Role of a code&lt;br /&gt;coverage tool&lt;br /&gt;446 Tools&lt;br /&gt;code on every conditional branch of your program. Unfortunately, a "code coverage"&lt;br /&gt;tool is not yet a standard part of the IDEs available for personal computers. The&lt;br /&gt;example in this section uses the "tcov" tool on Sun Unix.&lt;br /&gt;Special compile time options have to be specified when preparing a program for this&lt;br /&gt;form of analysis. These options direct the compiler to add extra instructions to those&lt;br /&gt;that would be generated from the program's source code. As well as extra instructions&lt;br /&gt;embedded in the code, the compiler adds an extra data table, a startup function and a&lt;br /&gt;termination function. The extra instructions, and related components, arrange for&lt;br /&gt;counts to be kept of the number of times that each conditional block is executed, when&lt;br /&gt;the program terminates these counts are saved to a data file.&lt;br /&gt;You can imagine the process as being one where the compiler converts the following&lt;br /&gt;simple program:&lt;br /&gt;#include &lt;iostream.h&gt;&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt;cout &lt;&lt; "Enter Number ";&lt;br /&gt;int num;&lt;br /&gt;cin &gt;&gt; num;&lt;br /&gt;if(num &gt;= 0)&lt;br /&gt;cout &lt;&lt; "that was positive" &lt;&lt; endl;&lt;br /&gt;else&lt;br /&gt;cout &lt;&lt; "that was negative" &lt;&lt; endl;&lt;br /&gt;return 0;&lt;br /&gt;}&lt;br /&gt;into an "instrumented" version:&lt;br /&gt;#include &lt;iostream.h&gt;&lt;br /&gt;int __counters[3];&lt;br /&gt;extern void __InitializeCounters(int d[], int n);&lt;br /&gt;extern void __SaveCountersToFile(int d[], int n);&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt;__InitializeCounters(__counters, 3);&lt;br /&gt;__counters[0]++;&lt;br /&gt;cout &lt;&lt; "Enter Number ";&lt;br /&gt;int num;&lt;br /&gt;cin &gt;&gt; num;&lt;br /&gt;if(num &gt;= 0) {&lt;br /&gt;__counters[1]++;&lt;br /&gt;cout &lt;&lt; "that was positive" &lt;&lt; endl;&lt;br /&gt;}&lt;br /&gt;else {&lt;br /&gt;__counters[2]++;&lt;br /&gt;cout &lt;&lt; "that was negative" &lt;&lt; endl;&lt;br /&gt;Compiler adds record&lt;br /&gt;keeping code&lt;br /&gt;Code Coverage Tool 447&lt;br /&gt;}&lt;br /&gt;__SaveCountersToFile(__counters, 3);&lt;br /&gt;return 0;&lt;br /&gt;}&lt;br /&gt;The "instrumented" program will run just like the original, except that it saves the&lt;br /&gt;counts to a file before terminating. In a properly implemented system, each time the&lt;br /&gt;program is run the new counts are added to those in any existing record file.&lt;br /&gt;When you have run your program several times on different data, you get the&lt;br /&gt;analysis tool to interpret the contents of the file with the counts. This analysis tool&lt;br /&gt;combines the counts data with the original source code to produce a listing that&lt;br /&gt;illustrates the number of times each different conditional block of code has been&lt;br /&gt;executed. Sections of code that have never been executed are flagged, warning the&lt;br /&gt;tester that checks are incomplete. The following output from the Unix tcov tool run on&lt;br /&gt;a version of the Quicksort program from Chapter 13.&lt;br /&gt;const int kSMALL_ENOUGH = 15;&lt;br /&gt;const int kBIG = 50000;&lt;br /&gt;int data[kBIG];&lt;br /&gt;void SelectionSort(int data[], int left, int right)&lt;br /&gt;6246 -&gt; {&lt;br /&gt;for(int i = left; i &lt; right; i++) {&lt;br /&gt;43754 -&gt; int min = i;&lt;br /&gt;for(int j=i+1; j&lt;= right; j++)&lt;br /&gt;248487 -&gt; if(data[j] &lt; data[min]) min =&lt;br /&gt;35251 -&gt; int temp = data[min];&lt;br /&gt;43754 -&gt; data[min] = data[i];&lt;br /&gt;data[i] = temp;&lt;br /&gt;}&lt;br /&gt;6246 -&gt; }&lt;br /&gt;int Partition( int d[], int left, int right)&lt;br /&gt;6245 -&gt; {&lt;br /&gt;int val =d[left];&lt;br /&gt;int lm = left-1;&lt;br /&gt;int rm = right+1;&lt;br /&gt;for(;;) {&lt;br /&gt;152418 -&gt; do&lt;br /&gt;rm--;&lt;br /&gt;518367 -&gt; while (d[rm] &gt; val);&lt;br /&gt;152418 -&gt; do&lt;br /&gt;lm++;&lt;br /&gt;412418 -&gt; while( d[lm] &lt; val);&lt;br /&gt;152418 -&gt; if(lm&lt;rm) {&lt;br /&gt;146173 -&gt; int tempr = d[rm];&lt;br /&gt;d[rm] = d[lm];&lt;br /&gt;d[lm] = tempr;&lt;br /&gt;}&lt;br /&gt;Analysis tool&lt;br /&gt;448 Tools&lt;br /&gt;else&lt;br /&gt;6245 -&gt; return rm;&lt;br /&gt;##### -&gt; }&lt;br /&gt;##### -&gt; }&lt;br /&gt;void Quicksort( int d[], int left, int right)&lt;br /&gt;12491 -&gt; {&lt;br /&gt;if(left &lt; (right-kSMALL_ENOUGH)) {&lt;br /&gt;6245 -&gt; int split_pt = Partition(d,left,&lt;br /&gt;Quicksort(d, left, split_pt);&lt;br /&gt;Quicksort(d, split_pt+1, right);&lt;br /&gt;}&lt;br /&gt;6246 -&gt; else SelectionSort(d, left, right);&lt;br /&gt;6246 -&gt; }&lt;br /&gt;int main()&lt;br /&gt;1 -&gt; {&lt;br /&gt;int i;&lt;br /&gt;long sum = 0;&lt;br /&gt;for(i=0;i &lt;kBIG;i++)&lt;br /&gt;50000 -&gt; sum += data[i] = rand() % 15000;&lt;br /&gt;50000 -&gt; Quicksort(data, 0, kBIG-1);&lt;br /&gt;1 -&gt; int last = -1;&lt;br /&gt;long sum2 = 0;&lt;br /&gt;for(i = 0; i &lt; kBIG; i++)&lt;br /&gt;50000 -&gt; if(data[i] &lt; last) {&lt;br /&gt;##### -&gt; cout &lt;&lt; "Oh ....; the data&lt;br /&gt;cout &lt;&lt; "Noticed the problem at&lt;br /&gt;exit(1);&lt;br /&gt;}&lt;br /&gt;50000 -&gt; else {&lt;br /&gt;sum2 += last = data[i];&lt;br /&gt;}&lt;br /&gt;50000 -&gt; if(sum != sum2) {&lt;br /&gt;##### -&gt; cout &lt;&lt; "Oh ...; we seem to have&lt;br /&gt;}&lt;br /&gt;1 -&gt; return 0;&lt;br /&gt;All the parts of the program that we want executed have indeed been executed.&lt;br /&gt;For real programs, code coverage tools are an essential part of the test and&lt;br /&gt;development process.&lt;br /&gt;14.2 THE PROFILER&lt;br /&gt;A code coverage tool helps you check that you have tested your code, a "Profiler" helps&lt;br /&gt;you make your code faster.&lt;br /&gt;Profiler 449&lt;br /&gt;First some general advice on speeding up programs:&lt;br /&gt;General advice 1:&lt;br /&gt;1. Make it work.&lt;br /&gt;2. Make it fast.&lt;br /&gt;Step 2 is optional.&lt;br /&gt;General advice 2:&lt;br /&gt;The slow bit isn't the bit you thought would be slow.&lt;br /&gt;General advice 3:&lt;br /&gt;Fiddling with "register" declarations, changing from arrays to pointers and general&lt;br /&gt;hacking usually makes not one measurable bit of difference.&lt;br /&gt;General advice 4&lt;br /&gt;The only way to speed something up significantly will involve a fundamental change&lt;br /&gt;of algorithm (and associated data structure).&lt;br /&gt;General advice 5&lt;br /&gt;Don't even think about making changes to algorithms until you have acquired some&lt;br /&gt;detailed timing statistics.&lt;br /&gt;Most environments provide some form of "profiling" tool that can be used to get the&lt;br /&gt;timing statistics that show where a program really is spending its time. To use a&lt;br /&gt;profiling tool, a program has to be compiled with special compile-time flags set. When&lt;br /&gt;these flags are set, the compiler and link-loader set up auxiliary data structures that&lt;br /&gt;allow run time addresses to be mapped back to source code. They also arrange for&lt;br /&gt;some run-time component that can be thought of as regularly interrupting a running&lt;br /&gt;program and asking where its program counter (PC) is. This run-time component takes&lt;br /&gt;the PC value and works out which function it corresponds to and updates a counter for&lt;br /&gt;that function. When the program finishes, the counts (and mapping data) are saved to&lt;br /&gt;file. The higher the count associated with a particular bit of code, the greater the time&lt;br /&gt;spent in that code.&lt;br /&gt;A separate utility program can take the file with counts, and the source files and&lt;br /&gt;produce a summary identifying which parts of a program use most time. The following&lt;br /&gt;data were obtained by using the Unix profiler to analyze the performance of the&lt;br /&gt;extended Quicksort program:&lt;br /&gt;450 Tools&lt;br /&gt;%Time Seconds Cumsecs Name&lt;br /&gt;44.7 0.42 0.42 __0FJPartitionPiiTC&lt;br /&gt;23.4 0.22 0.64 __0FNSelectionSortPiiTC&lt;br /&gt;11.7 0.11 0.75 main&lt;br /&gt;6.4 0.06 0.81 .rem&lt;br /&gt;6.4 0.06 0.87 .mul&lt;br /&gt;1.1 0.01 0.94 rand&lt;br /&gt;Both the Symantec and the Borland IDEs include profilers that can provide information&lt;br /&gt;to that illustrated.&lt;br /&gt;The largest portion of this program's time is spent in the Partition() function, the&lt;br /&gt;Quicksort() function itself doesn't even register because it represents less than 1% of&lt;br /&gt;the time. (The odd names, like __0FNSelectionSortPiiTC are examples of&lt;br /&gt;"mangled" names produced by a C++ compiler.)&lt;br /&gt;Once you know where the program spends its time, then you can start thinking about&lt;br /&gt;minor speed ups (register declarations etc) or major improvements (better algorithms).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1020589601308586811-2624034335219629042?l=debug-guide.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://debug-guide.blogspot.com/feeds/2624034335219629042/comments/default' title='Poskan Komentar'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1020589601308586811&amp;postID=2624034335219629042' title='0 Komentar'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1020589601308586811/posts/default/2624034335219629042'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1020589601308586811/posts/default/2624034335219629042'/><link rel='alternate' type='text/html' href='http://debug-guide.blogspot.com/2008/01/tools-c.html' title='Tools C++'/><author><name>doni</name><uri>http://www.blogger.com/profile/15287923128168749403</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
