C++ Guide for CPT 105—Introduction to Computer Programming The C++ “Shell” An Example from Ted Tucker [comments that identify the program and you] // your name // your class number and section number #include <iostream> //directives using namespace std; int main() { your code goes here return 0; } Another Example (main shell.txt) from Ted Tucker /* Program Name: Programmer: Date: Purpose: Note: Block comments are contained here */ //Pre-processing instructions go here such as including iostream header file #include <iostream.h> //If using Microsoft C++, then include the following statement //Do not include using namespace std; if you are using Borland using namespace std; void main () { //declaration and initialization statements //Body of the program goes here } // end of main 1 of 3307/11/2016 An Example from John McGaha /* Figure 2-19 Program Name: Programmer: Date: Purpose: depicted College Admission John McGaha 2.9.2002 Illustrates C++ source code that implements logic on flowchart and psedudocode in Figure 2-19. This program will determine if students are admitted or rejected to college */ #include <iostream.h> #include <conio.h> //Needed for getch function to pause the output screen main() { //Declare variables used in program (type and name) //Pause screen for viewing cout << endl << endl; cout << "Press any key to end program."; getch(); } // end of main C++ Programmer’s Comments Notes from Ted Tucker A comment is text that the compiler ignores but that is useful for programmers. Comments are normally used to annotate code for future reference. The compiler treats them as white space. You can use comments in testing to make certain lines of code inactive; however, #if/#endif preprocessor directives work better for this because you can surround code that contains comments but you cannot nest comments. A C++ comment is written in one of the following ways: The /* (slash, asterisk) characters, followed by any sequence of characters (including new lines), followed by the */ characters. This syntax is the same as ANSI C. The // (two slashes) characters, followed by any sequence of characters. A new line not immediately preceded by a backslash terminates this form of comment. Therefore, it is commonly called a “single-line comment.” 2 of 3307/11/2016 The comment characters (/*, */, and //) have no special meaning within a character constant, string literal, or comment. Comments using the first syntax, therefore, cannot be nested. Consider this example: /* Intent: Comment out this block of code. Problem: Nested comments on each line of code are illegal. FileName = String( "hello.dat" ); /* Initialize file string */ cout << "File: " << FileName << "\n"; /* Print status message */ */ The preceding code will not compile because the compiler scans the input stream from the first /* to the first */ and considers it a comment. In this case, the first */ occurs at the end of the Initialize file string comment. The last */, then, is no longer paired with an opening /*. Note that the single-line form (//) of a comment followed by the line-continuation token (\) can have surprising effects. Consider this code: #include <stdio.h> void main() { printf( "This is a number %d", // \ 5 ); } After preprocessing, the preceding code contains errors and appears as follows: #include <stdio.h> void main() { printf( "This is a number %d", } Declaring Global Variables (Ch’s. 1, 3, 4) C++ Key (Reserved) Word (from Ted Tucker) Keywords are predefined reserved identifiers that have special meanings. They cannot be used as identifiers in your program. The following keywords are reserved for C++: Syntax keyword: one of asm1 auto bad_cast bad_typeid bool break case catch char class const const_cast continue default delete do double dynamic_cast else enum except explicit extern false 3 of 3307/11/2016 finally float for friend goto if inline int long mutable namespace new operator private protected public register reinterpret_cast return short signed sizeof static static_cast struct switch template this throw true try type_info typedef typeid typename union unsigned using virtual void volatile while 1 Reserved for compatibility with other C++ implementations, but not implemented. Use __asm. Microsoft Specific In Microsoft C++, identifiers with two leading underscores are reserved for compiler implementations. Therefore, the Microsoft convention is to precede Microsoft-specific keywords with double underscores. These words cannot be used as identifier names. allocate3 __inline property3 __asm1 __int8 selectany3 __based2 __int16 __single_inheritance __cdecl __int32 __stdcall __declspec __int64 thread3 dllexport3 __leave __try dllimport3 __multiple_inheritance uuid3 __except naked3 __uuidof __fastcall nothrow3 __virtual_inheritance __finally 1 2 Replaces C++ asm syntax. The __based keyword has limited uses for 32-bit target compilations. 4 of 3307/11/2016 These are special identifiers when used with __declspec; their use in other contexts is not restricted. Microsoft extensions are enabled by default. To ensure that your programs are fully portable, you can disable Microsoft extensions by specifying the ANSI-compatible /Za command-line option (compile for ANSI compatibility) during compilation. When you do this, Microsoft-specific keywords are disabled. When Microsoft extensions are enabled, you can use the previously-listed keywords in your programs. For ANSI compliance, these keywords are prefaced by a double underscore. For backward compatibility, single-underscore versions of all the keywords except __except, __finally, __leave, and __try are supported. In addition, __cdecl is available with no leading underscore. END Microsoft Specific 3 C++ Input and Output (Ch’s. 1, 4) This is a complex area for C++, especially if you want to exercise close control over the appearance of your input and output. Assignment (Ch. 1) Arithmetic Expressions (Ch. 1) Notes on Precedence of Operators (from Ted Tucker) Operators :: . -> [] () ++ -++ -! + * & new delete delete[] () * Description Scope resolution operator Dot operator Member selection Array indexing Function call Postfix increment operator (placed after the variable) Postfix decrement operator (placed after the variable) Prefix increment operator (placed before the variable) Prefix decrement operator (placed before the variable) Not Unary minus Unary plus Dereference Address of Create (allocate memory) Destroy (deallocate) Destroy array (deallocate) Type cast Multiply 5 of 3307/11/2016 / % + << >> < > <= >= == != && || = += -= *= /= %= ?: throw , Divide Remainder (modulo) Addition Subtraction Insertion operator (console output) Extraction operator (console input) Less than Greater than Less than or equal to Greater than or equal to Equal Not equal And Or Assignment Add and assign Subtract and assign Multiply and assign Divide and assign Modulo and assign Conditional operator Throw an exception Comma operator An Example from Ted Tucker #include <iostream> using namespace std; int main() { const double RATE = 6.9; double deposit; cout << "Enter the amount of your deposit $"; cin >> deposit; double newBalance; newBalance = deposit + deposit * ( RATE / 100); cout << "In one year, that deposit will grow to\n" << "$" << newBalance << " an amount worth waiting for,\n"; return 0; } 6 of 3307/11/2016 Relational Expressions and Logical Comparison Operators (Ch’s. 1, 5) The C++ “if” Selection Statements (Ch. 1) & the “if-then” and “ifthen-else” Selection Structures (Ch’s. 2, 5) Notes from Ted Tucker The C++ if Statement The if statement evaluates the expression enclosed in parentheses. The expression must be of arithmetic or pointer type, or it must be of a class type that defines an unambiguous conversion to an arithmetic or pointer type. In both forms of the if syntax, if the expression evaluates to a nonzero value (true), the statement dependent on the evaluation is executed; otherwise, it is skipped. In the if...else syntax, the second statement is executed if the result of evaluating the expression is zero. The else clause of an if...else statement is associated with the closest previous if statement that does not have a corresponding else statement. The following code fragment demonstrates how this works: if( condition1 == true ) if( condition2 == true ) cout << "condition1 true; condition2 true\n"; else cout << "condition1 true; condition2 false\n"; else cout << "condition 1 false\n"; Many programmers use curly braces ({ }) to explicitly clarify the pairing of complicated if and else clauses, such as in the following example: if( condition1 == true ) { if( condition1 == true ) cout << "condition1 true; condition2 true\n"; else cout << "condition1 true; condition2 false\n"; } else cout << "condition 1 false\n"; Although the braces are not strictly necessary, they clarify the pairing between if and else statements. C++ for Figure 2-19 (from John McGaha) /* Figure 2-19 Program Name: College Admission Programmer: John McGaha Date: 2.9.2002 7 of 3307/11/2016 Purpose: depicted Illustrates C++ source code that implements logic on flowchart and psedudocode in Figure 2-19. This program will determine if students are admitted or rejected to college */ #include <iostream.h> #include <conio.h> //Needed for getch function to pause the output screen main() { //Declare variable names and types used in the main module float test; float rank; //Get the test and rank values from the keyboard cout << "Enter test score "; cin >> test; cout << endl; cout << "Enter high school rank: "; cin >> rank; cout << endl; /* Evaluate test score and rank Accept: If test >= 90 and rank >= 25 (upper 75%) or If test >= 80 and rank >= 50 (upper 50%) or If test >= 70 and rank >= 75 (upper 25%) Reject: All other conditions */ if (test >= 90) if (rank >= 25) cout << "Accept" << endl; //test >= 90 and rank >= 25 (upper 75%) else cout << "Reject" << endl; else if (test >= 80) if (rank >= 50) cout << "Accept" << endl; //test >= 80 and rank >= 50 (upper 50%) else cout << "Reject" << endl; else if (test >= 70) if (rank >= 75) cout << "Accept" << endl;//test >= 70 and rank >= 75 (upper 25%) else 8 of 3307/11/2016 cout << "Reject" << endl; else cout << "Reject" << endl; cout << endl << endl; cout << "Press any key to end program."; getch(); } // end of main C++ Compound Statements and Blocks The C++ “while” Iteration Statement and the “do while” Pre-Test Loop Structure (Ch’s. 2, 6) Notes from Ted Tucker The C++ while Statement The while statement executes a statement repeatedly until the termination condition (the expression) specified evaluates to zero. The test of the termination condition takes place before each execution of the loop; therefore, a while loop executes zero or more times, depending on the value of the termination expression. The following code uses a while loop to trim trailing spaces from a string: char *trim( char *szSource ) { char *pszEOS; // Set pointer to end of string to point to the character just // before the 0 at the end of the string. pszEOS = szSource + strlen( szSource ) - 1; while( pszEOS >= szSource && *pszEOS == ' ' ) *pszEOS-- = '\0'; return szSource; } The termination condition is evaluated at the top of the loop. If there are no trailing spaces, the loop never executes. The expression must be of an integral type, a pointer type, or a class type with an unambiguous conversion to an integral or pointer type. An Example from Ted Tucker #include <iostream> using namespace std; int main() { 9 of 3307/11/2016 int countDown; cout << "How many greetings do you want? "; cin >> countDown; while (countDown > 0) { cout << "Hello "; countDown = countDown - 1; } cout << endl; cout << "That's all!\n"; return 0; } The C++ “break”, “continue”, and “return” Statements Notes from Ted Tucker The C++ break Statement The break statement is used to exit an iteration or switch statement. It transfers control to the statement immediately following the iteration substatement or switch statement. The break statement terminates only the most tightly enclosing loop or switch statement. In loops, break is used to terminate before the termination criteria evaluate to 0. In the switch statement, break is used to terminate sections of code — normally before a case label. The following example illustrates the use of the break statement in a for loop: for( ; ; ) // No termination condition. { if( List->AtEnd() ) break; List->Next(); } cout << "Control transfers to here.\n"; Note There are other simple ways to escape a loop. It is best to use the break statement in more complex loops, where it can be difficult to tell whether the loop should be terminated before several statements have been executed. The C++ continue Statement The continue statement forces immediate transfer of control to the loop-continuation statement of the smallest enclosing loop. (The “loop-continuation” is the statement that contains the controlling expression for the loop.) Therefore, the continue statement can appear only in the dependent statement of an iteration statement (although it may be the sole statement in that statement). In a for loop, execution of a continue statement causes evaluation of expression2 and then expression3. 10 of 3307/11/2016 The following example shows how the continue statement can be used to bypass sections of code and skip to the next iteration of a loop: #include <conio.h> // Get a character that is a member of the zero-terminated // string, szLegalString. Return the index of the character // entered. int GetLegalChar( char *szLegalString ) { char *pch; do { char ch = _getch(); // Use strchr library function to determine if the // character read is in the string. If not, use the // continue statement to bypass the rest of the // statements in the loop. if( (pch = strchr( szLegalString, ch )) == NULL ) continue; // A character that was in the string szLegalString // was entered. Return its index. return (pch - szLegalString); // The continue statement transfers control to here. } while( 1 ); return 0; } The C++ return Statement The return statement allows a function to immediately transfer control back to the calling function (or, in the case of the main function, transfer control back to the operating system). The return statement accepts an expression, which is the value passed back to the calling function. Functions of type void, constructors, and destructors cannot specify expressions in the return statement; functions of all other types must specify an expression in the return statement. The expression, if specified, is converted to the type specified in the function declaration, as if an initialization were being performed. Conversion from the type of the expression to the return type of the function can cause temporary objects to be created. When the flow of control exits the block enclosing the function definition, the result is the same as it would be if a return statement with no expression had been executed. This is illegal for functions that are declared as returning a value. A function can have any number of return statements. An Example of “continue” from Ted Tucker #include <iostream> 11 of 3307/11/2016 using namespace std; int main() { int number, sum = 0, count= 0; cout << "Enter 4 negative numbers, ONE PER LINE:\n"; while (count < 4) { cin >> number; if (number >= 0) { cout << "ERROR: positive number (or zero):\n" << "Reenter that number and continue:\n"; continue; } sum = sum + number; count++; } cout << sum << " is the sum of the " << count << " numbers.\n"; return 0; } The C++ “switch” Selection Statement and the Case Selection Structure (Ch. 2) The “switch” construct in C/C++/Java is limited to integer tests. It is very limited compared to case constructs in BASIC and COBOL. The “if … else if …” construct may be used where the C++ switch will not accommodate. Notes from John McGaha & Ted Tucker The C++ switch Statement The C++ switch statement allows selection among multiple sections of code, depending on the value of an expression. The expression enclosed in parentheses, the “controlling expression,” must be of an integral type or of a class type for which there is an unambiguous conversion to integral type. The switch statement causes an unconditional jump to, into, or past the statement that is the “switch body,” depending on the value of the controlling expression, the values of the case labels, and the presence or absence of a default label. The switch body is normally a compound statement (although this is not a syntactic requirement). Usually, some of the statements in the switch body are labeled with case labels or with the default label. Labeled statements are not syntactic 12 of 3307/11/2016 requirements, but the switch statement is meaningless without them. The default label can appear only once. Syntax case constant-expression : statement default : statement The constant-expression in the case label is converted to the type of the controlling expression and is then compared for equality. In a given switch statement, no two constant expressions in case statements can evaluate to the same value. The behavior is shown below. Condition Action Converted value matches that of the promoted controlling expression. Control is transferred to the statement following that label. None of the constants match the constants in the case labels; default label is present. Control is transferred to the default label. None of the constants match the constants in the case labels; default label is not present. Control is transferred to the statement after the switch statement. An inner block of a switch statement can contain definitions with initializations as long as they are reachable — that is, not bypassed by all possible execution paths. Names introduced using these declarations have local scope. The following code fragment shows how the switch statement works: switch( tolower( *argv[1] ) ) { // Error. Unreachable declaration. char szChEntered[] = "Character entered was: "; case 'a' : { // Declaration of szChEntered OK. Local scope. char szChEntered[] = "Character entered was: "; cout << szChEntered << "a\n"; } break; case 'b' : // Value of szChEntered undefined. cout << szChEntered << "b\n"; break; default: // Value of szChEntered undefined. cout << szChEntered << "neither a nor b\n"; break; } A switch statement can be nested. In such cases, case or default labels associate with the most deeply nested switch statements that enclose them. For example: switch( msg ) 13 of 3307/11/2016 { case WM_COMMAND: // Windows command. Find out more. switch( wParam ) { case IDM_F_NEW: // File New menu command. delete wfile; wfile = new WinAppFile; break; case IDM_F_OPEN: // File Open menu command. wfile->FileOpenDlg(); break; ... } case WM_CREATE: // Create window. ... break; case WM_PAINT: // Window needs repainting. ... break; default: return DefWindowProc( hWnd, Message, wParam, lParam ); } The preceding code fragment from a Microsoft Windows® message loop shows how switch statements can be nested. The switch statement that selects on the value of wParam is executed only if msg is WM_COMMAND. The case labels for menu selections, IDM_F_NEW and IDM_F_OPEN, associate with the inner switch statement. Control is not impeded by case or default labels. To stop execution at the end of a part of the compound statement, insert a break statement. This transfers control to the statement after the switch statement. This example demonstrates how control “drops through” unless a break statement is used: BOOL fClosing = FALSE; ... switch( wParam ) { case IDM_F_CLOSE: fClosing = TRUE; // fall through // File close command. case IDM_F_SAVE: // File save command. if( document->IsDirty() ) if( document->Name() == "UNTITLED" ) FileSaveAs( document ); else FileSave( document ); if( fClosing ) document->Close(); break; } The preceding code shows how to take advantage of the fact that case labels do not impede the flow of control. If the switch statement transfers control to IDM_F_SAVE, 14 of 3307/11/2016 fClosing is FALSE. Therefore, after the file is saved, the document is not closed. However, if the switch statement transfers control to IDM_F_CLOSE, fClosing is set to TRUE, and the code to save a file is executed. C++ for Figure 2-31 (from John McGaha) /* Figure 2-31 Program Name: Programmer: Date: Purpose: depicted College Fees John McGaha 2.9.2002 Illustrates C++ source code that implements logic on flowchart and psedudocode in Figure 2-31. This program will determine fee students must pay, based upon their class. */ #include <iostream.h> //Needed for input/output #include <ctype.h> //Needed for toupper function #include <conio.h> //Needed for getch function main() { //Declare variable names and types used in the main module char Class; int Fee; //Get Class from keyboard cout << "Enter the Class: <F> Freshman <S> Sophmore <J> Junior"; cout << endl << endl; cout << "Class? "; cin >> Class; //Change Class character to Upper Case so Case will work properly Class = toupper(Class); //Determine Fee switch(Class) { case 'F': //Freshman Fee = 75; break; case 'S': //Sophmore Fee = 50; break; case 'J': //Junior Fee = 30; break; default: //Assumes Senior Fee = 10; 15 of 3307/11/2016 break; } cout << endl << endl; //Output two blank lines cout << "The fee is $" << Fee << endl; cout << "Press any key to end program."; getch(); } // end of main An example from Ted Tucker, Using the Random Number Generator Functions and the Modulus Operator #include <iostream> #include <cstdlib> using namespace std; int main() { int month, day; cout << "Welcome to your friendly weather program.\n" << "Enter today's date as two integers for the month and the day:\n"; cin >> month; cin >> day; srand(month*day); int prediction; char ans; cout << "Weather for today:\n"; do { prediction = rand( )% 3; switch (prediction) { case 0: cout << "The day will be sunny!!\n"; break; case 1: cout << "The day will be cloudl.\n"; break; case 2: cout << "The day will be stormy!\n"; break; default: cout << "Weather program is not functioning properly.\n"; } cout << "Want the weather for the next day? (y/n): "; cin >> ans; }while (ans == 'y' || ans == 'Y'); cout << "That's it from your 24-hour weather program.\n"; return 0; 16 of 3307/11/2016 } The C++ “do … while” Iteration Statement and the “do until” Post-Test Loop Structure (Ch’s. 2, 6) Notes from Ted Tucker The do Statement The do statement executes a statement repeatedly until the specified termination condition (the expression) evaluates to zero. The test of the termination condition is made after each execution of the loop; therefore, a do loop executes one or more times, depending on the value of the termination expression. The following function uses the do statement to wait for the user to press a specific key: void WaitKey( char ASCIICode ) { char chTemp; do { chTemp = _getch(); } while( chTemp != ASCIICode ); } A do loop rather than a while loop is used in the preceding code — with the do loop, the _getch function is called to get a keystroke before the termination condition is evaluated. This function can be written using a while loop, but not as concisely: void WaitKey( char ASCIICode ) { char chTemp; chTemp = _getch(); while( chTemp != ASCIICode ) { chTemp = _getch(); } } The expression must be of an integral type, a pointer type, or a class type with an unambiguous conversion to an integral or pointer type. An Example from Ted Tucker #include <iostream> using namespace std; int main() { int countDown; cout << "How many greetings do you want? "; cin >> countDown; 17 of 3307/11/2016 do { cout << "Hello "; countDown = countDown - 1; }while (countDown > 0); cout << endl; cout << "That's all!\n"; return 0; } C++ Functions and Out-of-Line Modularization (Ch. 3) C++ for Ch. 3, Ex. 3, not using functions (from John McGaha) /* Exercise #3 Program Name: Programmer: Date: Purpose: depicted Exercise Solution John McGaha 2.9.2002 Illustrates C++ source code that implements logic in psedudocode on page 76, Exercise 3. This program will determine final values of variables A, B, and C. */ #include <iostream.h> #include <conio.h> //Needed for getch function to pause the output screen main() { //Declare all variables - type and name int A, B, C; A = 2; location B = 4; C = 10; //One equal symbol used to make assignment to memory //Display Original Values of A, B, and C cout << "Original Values" << endl; cout << "A = " << A << endl; cout << "B = " << B << endl; cout << "C = " << C << endl; cout << endl << endl; cout << "Press any key to continue." << endl << endl; getch(); while(C > 6) { B = B + 1; C = C - 1; 18 of 3307/11/2016 } // end while if (A == 2) //Two equal symbols used for equality test { A = A + 1; B = B - 1; } //end if if (C == 10) { A = A + 1; B = B - 1; } else { B = B + 1; C = C - 1; } //Display Final Values of A, B, and C cout << "Final Values" << endl; cout << "A = " << A << endl; cout << "B = " << B << endl; cout << "C = " << C << endl; cout << endl << endl; cout << "Press any key to end program."; getch(); } // end of main C++ for Ch. 3, Ex. 3, using functions (from John McGaha) /* Exercise #3 Program Name: Programmer: Date: Purpose: depicted Exercise Solution John McGaha 2.9.2002 Illustrates C++ source code that implements logic in psedudocode on page 76, Exercise 3. This program will determine final values of variables A, B, and C. */ #include <iostream.h> #include <conio.h> //Needed for getch function to pause the output screen //Global Variables and functions (modules) int A = 2; int B = 4; int C = 10; ChangeBC() 19 of 3307/11/2016 { B = B + 1; C = C - 1; } ChangeA() { A = A + 1; B = B - 1; } main() { //Display Original Values of A, B, and C cout << "Original Values" << endl; cout << "A = " << A << endl; cout << "B = " << B << endl; cout << "C = " << C << endl; cout << endl << endl; cout << "Press any key to continue." << endl << endl; getch(); while(C > 6) { ChangeBC(); } if (A == 2) //Two equal symbols used for equality test ChangeA(); if (C == 10) ChangeA(); else ChangeBC(); //Display Final Values of A, B, and C cout << "Final Values" << endl; cout << "A = " << A << endl; cout << "B = " << B << endl; cout << "C = " << C << endl; cout << endl << endl; cout << "Press any key to end program."; getch(); } // end of main Combining Decisions Using C++ Logical Expressions (Ch. 5) 20 of 3307/11/2016 C++ “while” Iteration Statement and Counter-Controlled Looping (Ch. 6) C++ “for” Iteration Statement (Ch. 6) Notes from Ted Tucker The C++ for Statement The for statement can be divided into three separate parts, as shown below. Syntax Name When Executed Contents for-init-statement Before any other element of the for Often used to initialize loop statement or the substatement. indices. It can contain expressions or declarations. expression1 Before execution of a given iteration of the loop, including the first iteration. An expression that evaluates to an integral type or a class type that has an unambiguous conversion to an integral type. expression2 At the end of each iteration of the loop; expression1 is tested after expression2 is evaluated. Normally used to increment loop indices. The for-init-statement is commonly used to declare and initialize loop-index variables. The expression1 is often used to test for loop-termination criteria. The expression2 is commonly used to increment loop indices. The for statement executes the statement repeatedly until expression1 evaluates to zero. The for-init-statement, expression1, and expression2 fields are all optional. The following for loop: for( for-init-statement; expression1; expression2 ) { // Statements } is equivalent to the following while loop: for-init-statement; while( expression1 ) { // Statements expression2; } A convenient way to specify an infinite loop using the for statement is: for( ; ; ) { // Statements to be executed. } 21 of 3307/11/2016 This is equivalent to: while( 1 ) { // Statements to be executed. } The initialization part of the for loop can be a declaration statement or an expression statement, including the null statement. The initializations can include any sequence of expressions and declarations, separated by commas. Any object declared inside a for-init-statement has local scope, as if it had been declared immediately prior to the for statement. Although the name of the object can be used in more than one for loop in the same scope, the declaration can appear only once. For example: #include <iostream.h> void main() { for( int i = 0; i < 100; ++i ) cout << i << "\n"; // The loop index, i, cannot be declared in the // for-init-statement here because it is still in scope. for( i = 100; i >= 0; --i ) cout << i << "\n"; } Although the three fields of the for statement are normally used for initialization, testing for termination, and incrementing, they are not restricted to these uses. For example, the following code prints the numbers 1 to 100. The substatement is the null statement: #include <iostream.h> void main() { for( int i = 0; i < 100; cout << ++i << endl ) ; } Accumulating Totals (Ch. 6) Multi-Level Reports and “Control Breaks” (Ch. 7) Notes from Ted Tucker SINGLE-LEVEL CONTROL BREAK PROCESSING Control break processing, sometimes called summary or group report processing, produces subtotals for a group of records. With this type of processing control fields are used to indicate when totals are to print. The ‘control’ field is the field upon which the data to be processed is sorted. Obviously, this means that the data file must be sorted in order to process a control break report. The sort does not produce records with unique 22 of 3307/11/2016 identifiers. Duplicate keyed records are produced. A field, defined as a ‘hold’ field is used to represent the current group of records that is being processed. The value of the ‘control’ field is stored in the ‘hold’ field following the initial read of the data file. The beginning of a pretest loop follows the initial read. The first processing that is performed inside the loop is an IF-THEN-ELSE structure with a null ELSE. This structure checks to see if the current value of the ‘control’ field is the same as the ‘hold’ field. If the ‘control’ field is the same as the ‘hold ’field, a detail line can be printed if desired (when detail reports are desired) and the subtotal for the current group is accumulated. This processing is followed by another read operation and the next iteration of the loop. If the current value of the ‘control’ field and the ‘hold’ field are not the same, the first record of the new group has been read. This forces a control break which requires the program to finalize the processing of the previous group of records. Break processing requires that subtotals for the last group of records are to be printed. The subtotal will be accumulated into a grand total at this time and the subtotal field will be initialized back to zero. The ‘hold’ field will be updated to the current value of the ‘control’ field at the end of the break processing. The detail processing and accumulation continues after the four break processing steps. This is then followed by another read operation and the next loop iteration. This processing continues until all of the records in the file have been processed. When end-of-file occurs, the break processing must be forced one last time in order to complete the processing on the last group of records. The last group’s subtotal must be printed and accumulated into a grand total. The grand total is then printed and processing is complete. 23 of 3307/11/2016 MULTI-LEVEL CONTROL BREAK PROCESSING Multi-level control break processing is not a great deal different from single level control break processing. The major difference, of course, is that instead of one ‘control’ field, there are multiple ‘control’ fields. This means that the data file contains multiple sorts. These sorts are embedded. For example, there may be departments sorted within divisions and divisions sorted within companies. This particular example would be considered a triple-level control break. (Triple-level break processing will be used in the following explanation of multi-level break processing.) For each ‘control’ field a ‘hold’ field exists as well as a separate subtotal field. After the initial record is read, all of the ‘hold’ fields must be initialized with the current values of the ‘control’ fields. For ease of understanding, we will reference each of these break levels as major (company), intermediate (division), and minor (department). As in single level control break processing, a check must be done to see if a ‘control’ field is still equal to its ‘hold’ field. This will be the first processing in the pretest loop that follows the initial data read operation. With a multi-level break, a decision structure must be created that will check the ‘control’ fields in a specific order. It stands to reason that if a company group changes that the division and the department groups have also changed. If the division group within a company group changes, the department group will also change. Therefore, the first check must be done on the major ‘control’ field. If the major ‘control’ field is still equal to its ‘hold’ field, a check must then be done to see if the intermediate ‘control’ field is still equal to its ‘hold’ field. If the intermediate ‘control’ field is still the same as its ‘hold’ field, a check must then be done to see if the minor ‘corneal’ field is still the same as its ‘hold’ field. If a change has occurred at any level, a subroutine must be executed that contains processing written for each specific break level. Each break subroutine contains the same four processing steps as the single level control break except that different subtotals are printed for each level and each subtotal is accumulated into the next higher level. A higher level subroutine calls the next lower level subroutine before its four steps are processed. For example, the first processing in the major subroutine is a call to execute the intermediate subroutine. The first processing in the intermediate subroutine is a call to execute the minor subroutine. In this manor, the subroutines are executed in reverse order. This is done so that the lower level subtotals can be accumulated into the higher level subtotals before the higher level subtotals are printed. The major level subtotal is accumulated into the grand total. The detail processing proceeds as in single level break processing. The minor level subtotals (for example, the department subtotal) is accumulated during the detail processing. After the detail processing, the next record will be read before proceeding with the next loop iteration. This processing will continue until all of the records have been processed. 24 of 3307/11/2016 As in single level break processing, the last break must be forced. The minor subtotal will be printed and accumulated into the intermediate subtotal followed by the printing of the intermediate subtotal and accumulation of the intermediate subtotal into the major subtotal. The major subtotal is then printed followed by the accumulation of the major subtotal into the grand total. This processing is then followed by the last processing step which is the printing of the grand total. Pseudocode for Multi-Level Control Break (from Ted Tucker) Read First Record Initialize hold areas (Major, Intermediate, and Minor) Begin Loop While not at end of file If Major hold <> Major control Execute Major Break Processing Else if Intermediate hold <> Intermediate control Execute Intermediate Break Processing Else if Minor hold <. Minor control Execute Minor Break Processing End if Print Detail line Accumulate Minor subtotals Read next record End Loop Execute Final Processing MAJOR BREAK Call Intermediate Break Print Major Subtotal INTERMEDIATE BREAK Call Minor Break Print Intermediate Subtotal Accum Major to Grand Initialize Major Subtotal Accum Inter to Major Initialize Intermediate Subtotal Update Intermediate Hold Field Update Major Hold Field FINAL PROCESSING 25 of 3307/11/2016 MINOR BREAK Print Minor Subtotal Accum Minor to Intermediate Initialize Minor Subtotal Update Minor Hold Field Print Minor Subtotal Accumulate Minor Subtotal into Intermediate Subtotal Print Intermediate Subtotal Accumulate Intermediate Subtotal into Major Subtotal Print Major Subtotal Accumulate Major Subtotal into Grand Total Print Grand Total C++ Arrays (Ch. 8, 9) Serial Search (Ch. 8) An Example from Ted Tucker #include <iostream> using namespace std; const int DECLARED_SIZE = 10; int search(int a[], int number_used, int target) { int index = 0; bool found = false; while ((!found) && (index < number_used)) if (target == a[index]) found = true; else index++; if (found) return index; else return -1; } int main() { char ans; int result; int target; do { cout << "Enter a number to search for: "; cin >> target; result = search(arr, DECLARED_SIZE, target); if (result == -1) cout << target << " is not in the list.\n"; 26 of 3307/11/2016 else cout << target << " is stored in array position " << result << endl << "(Remember: the first position is 0.\n)"; cout << "Search again? (y/n followed by return): "; cin >> ans; } while ((ans != 'n') && (ans != 'N')); cout << "End of program/\n"; return 0; } Binary Search (Ch. 8) Notes from Ted Tucker BINARY SEARCH The Sequential search through an array will always work. If the array contains 100 items, on the average a Sequential search will find the match after searching through 50 items. In the worst case, the search will have to look through all 100 items. A Binary search can be used on an sorted array and can be more efficient than a Sequential search. This search procedure involves first looking in the middle of the array. The value of the middle element is compared against the target element. If the middle element is greater than the target, the target must be in the lower half of the array. If the middle is less than the target, the target must be in the upper half. Having determined which half of the remaining array is to be searched, the middle of that half is compared against the target. This will continue until a match is found or until no more elements remain to be searched. Because the array is always divided in half, this search procedure is called a binary search. The following is Pseudocode for setting the index values in a Binary search. Establish BOTTOM = Bottom of Range TOP = Top of Range TARGET = Search Value WHILE there are more elements in the array and no match has been found LET INDEX = Middle of the remaining range ( TOP + BOTTOM ) / 2 IF element( INDEX ) > TARGET THEN set TOP of Remaining Range to INDEX ELSE set BOTTOM of Remaining Range to INDEX END IF Repeat WHILE Loop 27 of 3307/11/2016 Since we are continually dividing the range in half, we will never actually reach the bottom or top of the range. Therefore, we must set the search range to at least one beyond the actual top and bottom values in which we are searching. We use integer division so that the index will always have an integer value. An Example from Ted Tucker //example binary search #include <iostream> using namespace std; const int ARRAY_SIZE = 10; void search(const int a[], int first, int last, int key, bool& found, int &location) { int mid; if (first > last) { found = false; } else { mid = (first + last) /2; if (key == a[mid]) { found = true; location = mid; } else if (key < a[mid]) { search(a, first, mid -1, key, found, location); } else if (key > a[mid]) { search(a, mid + 1, last, key, found, location); } } } int main() { const int final_index = ARRAY_SIZE - 1; int a[ARRAY_SIZE] = {100,233,345,254,109,456,783,400,500,901}; int key, location; 28 of 3307/11/2016 bool found; cout << "Enter number to be located: "; cin >> key; search(a, 0, final_index, key, found, location); if (found) cout << key << " is in index location " << location << endl; else cout << key << " is not in the array." << endl; return 0; } “Bubble” Sort (Ch. 9) Notes from Ted Tucker BUBBLE SORT Bubble sort processing using an array consists of a nested loop structure that will sort the elements of the array in either ascending or descending order. The outer loop is a post-test loop that continues iteration until all of the array elements are sorted. A flag variable used for testing for a sorted array condition is set to a value of ‘true’ or ‘yes’ at the beginning of the loop. By setting this flag to ‘yes’, the sort algorithm assumes that the array is already sorted. This is done because the inner loop must process all of its iterations at least once even if the array is already sorted. This is one of the main reasons that the Bubble sort is actually one of the most inefficient sort algorithms. Unfortunately, it is one of the easiest sort algorithms to learn. Therefore, it is used quite often as an introduction to sorting. The inner loop is a counter controlled loop that will iterate one less time than the number of elements in the array. This is because two consecutive elements are compared at a time. Inside the counter controlled loop, an IF-THEN-ELSE structure with a null ELSE is used to test the current array element (array(count)) with the very next element in the array (array(count + 1)). To sort in ascending order, use a greater than comparison (array(counter) > array(counter + 1)). If the array(counter) element is greater than the array(count + 1) element, the values of the two array elements swap positions in the array. This is usually done by storing array(counter) in a temporary field , array(counter + 1) in array(counter) and finally the temporary field in array(counter + 1). In this way, element values swap positions. After the swap, the flag used to test whether the continue looping the outer loop, is set to ‘no’. This shows that the array is not sorted and forces another outer loop iteration. Remember, the inner loop must complete all of its loop iterations at least once even after the array is sorted. If even two elements must be swapped, the processing must continue. 29 of 3307/11/2016 An Example from John McGaha /* Sorting.cpp Date: November 2002 Programmer: Your Name Purpose: To illustrate the use of arrays in C++. This program will load an array from the keyboard, perform a bubble sort, find the average score, find the minimum score, find the maximum score, and then display them back to the screen in sorted order and then store the scores, average, minimum, and maximum in an output file. */ /****** Pre-Processing ******/ #include <iostream> #include <fstream> #include <stdlib.h> using namespace std; void main() { /****** Housekeeping Tasks ******/ //Declare and initialize variables and constants float gradeArray[10]; float average = 0, minimum = 0, maximum = 0, sum = 0; const int SIZE = 10; int pairsToCompare; int y = 0; int x = 0; int temp = 0; //Declare and open an output file - file does not exist, it will be created ofstream outFile("Grades.txt",ios::out); /***************** Processing Section******************/ //Loop 10 times. subscript++ adds ONE to the subscript at the END of each pass for(int subscript = 0; subscript < SIZE; subscript++) { cout << endl; //Prompt user for grade - Note that the grade number (1-10) is displayed too cout << "Enter grade " << (subscript + 1) << ": "; //Get the grade from the keyboard and store into the gradeArray. //Note: the subscript increments from 0 to 10 cin >> gradeArray[subscript]; sum = sum + gradeArray[subscript]; }//end of for loop 30 of 3307/11/2016 //Determine the statistics average = sum / SIZE; //Clear the screen system("cls"); /***** Sort the grades ****/ pairsToCompare = SIZE - 1; y = 0; while (y < SIZE) { x = 0; while (x < pairsToCompare) { if (gradeArray[x] > gradeArray[x+1]) { temp = gradeArray[x]; gradeArray[x] = gradeArray [x + 1]; gradeArray[x + 1] = temp; } x = x + 1; //Increment noting a comparison is made } //End inner while loop y = y + 1; pairsToCompare = pairsToCompare - 1; //Decrement comparison } //End outer while loop //Assign min and max scores minimum = gradeArray[0]; maximum = gradeArray[SIZE - 1]; //Display the grades and statistics back to the user //Display header outside of the loop cout << endl; cout << "The sorted grades and statistics are:\n"; cout << "---------------------------------\n"; //Display and save the sorted array for(int sub = 0; sub < SIZE; sub++) { cout << gradeArray[sub] << endl; outFile << gradeArray[sub] << endl; //to screen //to file } //end of for loop //Display and save the cout << "Average: " << cout << "Minimum: " << cout << "Maximum: " << average, minimum, and maximum grade average << endl; minimum << endl; maximum << endl; // Write data to the file outFile << "Average: " << average << endl; outFile << "Minimum: " << minimum << endl; 31 of 3307/11/2016 outFile << "Maximum: " << maximum << endl; //Display footer cout << "---------------------------------\n\n"; } //end of program An Example from Ted Tucker // example of sorting #include <iostream> using namespace std; const int DECLARED_SIZE = 10; int sample_array[DECLARED_SIZE] = {123,345,654,612,709,100,90,900,781,200}; int index_of_smallest(const int a[], int start_index, int number_used) { int min = a[start_index], index_of_min = start_index; for (int index = start_index + 1; index < number_used; index++) if (a[index] < min) { min = a[index]; index_of_min = index; } return index_of_min; } void swap_values(int& v1, int& v2) { int temp; temp = v1; v1 = v2; v2 = temp; } void sort(int a[], int number_used) { int index_of_next_smallest; for (int index = 0; index < number_used - 1; index++) { index_of_next_smallest = index_of_smallest(a, index, number_used); swap_values(a[index], a[index_of_next_smallest]); } } int main() { cout << "This program sorts numbers from lowest highest.\n"; 32 of 3307/11/2016 int number_used; number_used = DECLARED_SIZE; sort(sample_array, number_used); for ( int index = 0; index < number_used; index++) cout << sample_array[index] << " "; cout << endl; return 0; } 33 of 3307/11/2016