This example begins with the first version of the name_box program, and continues to display a person's name inside a box drawn with ASCII characters. However, rather than prompting for and reading the name from the console, this version takes the name from the command line. The different input techniques result in different data organizations in memory, significantly impacting two program sub-problems.
Data Organization
Number Of Dashes
Printing The Name
char name[100];
strlen(name)
cout << "|" << name << "|" << endl;
name_box1▢Cranston▢Q.▢Snort
Version 1 data organization and program operations.
The first version stores the full name, including the separating spaces (the red boxes), in a single C-string. Consequently, determining the number of dashes in the top and bottom of the box and printing the name are straightforward operations. The first column illustrates the definition of the name C-string and its contents following the input operation.
The command-line version of name_box stores the individual names as C-strings in a variable named argv. Programs can define argv in one of two ways: char* argv[] (an array of C-strings, see Figure 2(f)), or char** argv (a character pointer to C-strings). In my experience, the first definition is easier for new programmers to conceptualize, but you will see both notations used in production code. This organization makes it more challenging to determine the number of dashes and to print the full name. name_box2 solves each problem in two ways.
Solution 1: Command-line concatenation.
I believe the easiest solution is the one that requires the fewest changes to version 1. The difference between the versions is how the programs store the full name. If we can restore the name to a single C-string, we can reuse the remainder of the first version. This figure discards the prompt and read operations of the first version, and replaces them with a series of concatenation operations (using the "traditional" strcat function). The red characters denote the program initializing the name variable to an empty C-string, as required by strcat. The for-loops begin at 1, skipping the program name.
The strings stored in argv (not program operations).
The command-line processes discards the spaces between the individual names. So, concatenating the arguments prints the names without separating spaces, failing problem requirement ii.
Simply concatenating a space at the end of each name puts a space between the last name and the right side of the box, violating requirement iii.
Inserting a space between the names is another example of the fence post problem, with the names corresponding to the fence posts and the spaces to the fence spans. The solution shortens the loop by 1 and prints the last name as a special case without a space after the loop.
#include <iostream>
#include <cstring>
using namespace std;
int main(int argc, char* argv[]) // (a)
{
if (argc == 1) // (b)
exit(1);
char name[100] = ""; // (c)
for (int i = 1; i < argc - 1; i++) // (d)
{
strcat(name, argv[i]);
strcat(name, " ");
}
strcat(name, argv[argc - 1]); // (e)
cout << "+"; // (f)
for (size_t i = 0; i < strlen(name); i++)
cout << "-";
cout << "+" << endl;
cout << "|" << name << "|" << endl;
cout << "+";
for (size_t i = 0; i < strlen(name); i++)
cout << "-";
cout << "+" << endl;
return 0;
}
name_box2.cpp (version 1).
This solution creates and initializes a C-string, and fills it by concatenating the elements of argv.
Adds the command-line arguments to main.
Ends the program if there are no command-line elements beyond the program's name, ensuring the program doesn't index argv out of bounds.
Creates a C-string used as an accumulator, initializing it to an empty string.
Concatenates all but the last name to name, appending a space to the end.
Concatenates the last name to name without appending a space.
The rest of the program is identical to name_box1.
As is often the case, there are often many ways of programming a problem solution. Although the next version is more complex than the previous one, it includes a simple example of developing an algorithm for solving a sub-problem, making it worth our consideration. This version counts the total number of characters in all the names to determine, in part, how many dashes to draw for the top and bottom of the box.
int count = 0; // (i)
for (int i = 1; i < argc; i++) // (ii)
count += strlen(argv[i]);
count += argc - 2; // (iii)
(a)
(b)
(c)
Solution 2: Counting characters.
The program must print a dash for each character in each name. Counting the total number of characters is a straightforward process: visit each name in argv and count each character. However, the command-line processor discards the separating spaces during parsing. The counting algorithm must account for these missing spaces.
The separate names stored in argv without spaces.
We can design a general algorithm accounting for spaces by incrementally adding names and observing the relationship between the number of names (argc-1) and the number of spaces: spaces=number_of_names-1 or spaces=argc-2.
The C++ code that counts the number of dashes the program prints to form the top and bottom of the box:
Printing separate separate names.
Although counting characters simplifies printing the top and bottom of the box, the names remain in separate command-line elements, making them difficult to print. The problem and its solution parallel the concatenation problem presented in Figure 2. However, this version counts the number of dashes independently of how it prints the names; consequently, the first two attempts draw top and bottom lines that don't match the full name's length. Attempt 3 uses one fence-post-problem solution to print the names, correctly printing the name within a box.
#include <iostream>
#include <cstring>
using namespace std;
int main(int argc, char* argv[]) // (a)
{
if (argc == 1) // (b)
return 0;
int count = 0; // (c)
for (int i = 1; i < argc; i++)
count += strlen(argv[i]);
count += argc - 2;
cout << "+"; // (d)
for (int i = 0; i < count; i++)
cout << "-";
cout << "+" << endl;
cout << "|"; // (e)
for (int i = 1; i < argc - 1; i++)
cout << argv[i] << " ";
cout << argv[argc - 1] << "|" << endl;
cout << "+"; // (f)
for (int i = 0; i < count; i++)
cout << "-";
cout << "+" << endl;
return 0;
}
name_box2.cpp (version 2).
The operating system passes the names into the program in argv, creating two sub-problems: determining the number of dashes to draw and printing the names correctly spaced. Statements (a) through (e) solve the first, while statements (i) through (k) solve the second.
Adds the command-line arguments to main.
Ends the program if there are no command-line elements beyond the program's name, ensuring the program doesn't index argv out of bounds.
Implements the Figure 4 counting algorithm.
Draws the top of the box using the count calculated in statements (c).
Prints the names and sides of the box following the final Figure 5 algorithm.