Skip to content

Games programming from the ground up with C: Command line arguments and decisions

You can see an index of all the posts in this series: go to index.

If you want to review where I got to by this point, here are the starter files from the end of the previous post: functions and random numbers.

In the previous post I generated random numbers and used these to display a randomly generated string. I also left you with a couple of tricky challenges.

The first was to think of a situation in which I might want my random numbers to always start at the same point in the sequence by using srand with an integer literal rather than with the time function. The answer is when I’m testing my code. If I am trying to remove errors from my code, it’s often useful to be able to test it several times using exactly the same conditions. This way I can be sure that the changes I’ve made have actually fixed the problem, and not that the problem has just been temporarily masked because some values have changed.

The second challenge was to think about why my method of getting random numbers in a particular range will often be slightly biased towards one part of the range. Here’s why. On my machine, RAND_MAX is set to 2,147,483,647. If I divide this by 13 I get 165,191,049.76923076923077. In other words, RAND_MAX is not an exact multiple of our range. The highest multiple of 13 that is less than or equal to RAND_MAX is 2,147,483,637, which leaves a remainder of 10. This means that there is a very tiny bias in favour of numbers 0 to 9 in my range, because those numbers have a 1 in 165,191,050th greater chance of appearing. The larger my range gets, the more bias there is likely to be towards one part of it. For example, if I were choosing random numbers from a range of 100,000, the numbers 0 to 83,646 would have a 1 in 21,475th greater chance of appearing. If you were developing a scientific application that required a high degree of accuracy, you might want to find a solution to this. For the purposes of a game however, and with a range of this magnitude, this bias is so small as to make no appreciable difference.

In the previous post, I also introduced functions. So far I’ve called various functions from my program, but I haven’t showed you what a function looks like. Well actually I have. All the code I’ve written so far has been within a special function called main.  It’s special because it is the only function that is required in every single C program. When a C program starts it always starts by calling the main function. Let’s have a look at my program again to see how it’s structured:

/*******
 * Buzzword
 *
 * A program to create amusing fake jargon
 *******/

 #include <stdio.h>
 #include <stdlib.h>
 #include <time.h>

 int main(int argc, const char * argv[])
 {
   printf("Buzzword Generator\nBased on a program from Creative Computing, Morristown, New Jersey\n\n\nThis program prints highly acceptable phrases in 'educator-speak' that you can work into reports and speeches. Whenever a question mark is printed, type a 'Y' for another phrase or 'N' to quit.\n\n\nHere's the first phrase:\n");

   const char * adjectives1[] = {"ability", "basal", "behavioral", "child-centered", "differentiated", "discovery", "flexible", "heterogeneous", "homogenous", "manipulative", "modular", "tavistock", "individualized" };
   const char * adjectives2[] = {"learning", "evaluative", "objective", "cognitive", "enrichment", "scheduling", "humanistic", "integrated", "non-graded", "training", "vertical age", "motivational", "creative"};
   const char * nouns[] = {"grouping", "modification", "accountability", "process", "core curriculum", "algorithm", "performance", "reinforcement", "open classroom", "resource", "structure", "facility", "environment"};

   srand((unsigned int)time(NULL));
   printf("\n%s %s %s\n\n", adjectives1[rand() % 13], adjectives2[rand() % 13], nouns[rand() % 13]);

   return 0;
 }

Lines 1 to 5 of the file are comments. Comments are a way of annotating my code so it’s easier for me or others to understand. Comments are ignored by the compiler and have no effect on how my program runs.

There are two ways of including comments in my code. The type you can see in this program is a block comment. Block comments can span several lines but the first line must begin with /* and the final line must end with */.

There is another form of comment which begins with two forward slashes, //. This tells C, “everything on this line from this point on is a comment for my benefit, so please ignore it.” Comments are a useful way of reminding myself, or other people who view my code, what each part of it does, so I should use them liberally. I can use them anywhere in my file and I’ll inlcude further examples of them in later posts.

Beneath the comments, on lines 7-9 are the lines that include the function libraries I’m using.

The main function begins on line 11. You’ll recall from the previous post that some functions return a value and the value must always be of one type. When I define a function, I start with a keyword to show what type it returns, in the same way as I start variable declarations with a type. You can see from line 11 that the main function returns an integer.

The next part of the function definition is the name of the function, in this case main.

You’ll also recall that all function definitions end with a pair of parentheses and, for some functions, we included comma-separated arguments within those parentheses. When I define a function I must specify what arguments I expect and what their types are. You can see that the main function accepts two arguments: an integer called argc, and a string array called argv. Note that the string array also has a const keyword. You may remember from a previous post that this means the contents of the array may not be changed. If I try to change them from within the function, I’ll get an error. If a function has no arguments, I will still include the parentheses, but they will have nothing within them.

Next I have the body of the function – this is the code that the function executes when it is called. The body of the function is included within a pair of braces. You can see these in lines 12 and 23.

Lines 13 to 22 are the code that I added in earlier posts.

Line 22 is a return statement. A return statement ends the function and returns control to the code that called it. When a function returns a value, the return statement should end with the value to be returned to the calling code. In this case, I am returning the int 0. Zero, in this context, is a way of this function telling the calling code, I’ve finished running and everything is OK.

If I define a function that doesn’t return a value, then a return statement is optional, because my function will also return when it reaches the closing brace, like the one on line 23.

If you’ve been following along with the earlier posts, you’ll know I’ve been building and running this program in a terminal application, such as the Linux Bash shell. You are probably most familiar with programs that run in a graphical user interface (or GUI). These are programs that use windows, buttons, text boxes and so on. While, it’s certainly possible to use C to create programs that run in this way, I’m starting with command line programs because they are a lot simpler, so I can focus on understanding what the program is doing rather than having to worry about how it is working with the operating system to create windows, cursors etc. Command line programs might not be very visually exciting, but don’t worry – I’ll be venturing into graphical programming in later posts in the series, once I’ve covered the basics.

Now you already know that some functions take arguments, which are included in the parentheses after the function name and separated by commas if there’s more than one. You’ve already seen examples of this when I’ve used the printf, srand and time functions. You can also see that the main function in my program accepts two arguments, argc and argv. What you might be wondering is, if main is the first function to be called in every C program, where do those arguments come from? The answer is, they come from the user.

When I typed Buzzword into the terminal app to run my program, I could have also followed it with one or more arguments separated by spaces, e.g.

Buzzword argument1 argument2 argument3

What C does when it finds these arguments is count them. It then puts that number + 1 into argc (which you can probably now guess stands for argument count, and I’ll explain why it has 1 added to it later in this post). Then it takes each argument and puts them, in order, into the elements of the array argv (which stands for argument vector). When my program starts, it can then access the argument count and argument vector and use them as it needs to. This is one way of getting user input into a command line program.

To see how this works, I’ll modify my program so that it accepts a single argument. This will be an integer representing the number of phrases that I want displayed.

The first thing to do with any kind of user input to my program is to validate it. Otherwise users who are not sure what they are doing (or who are just plain malicious), could break my program with nonsensical input.

The first bit of validation I will do is to check that I have received the right number of arguments. I changed my program so it looked like this (the new or changed lines are 13-16 and 27):

/*******
 * Buzzword
 *
 * A program to create amusing fake jargon
 *******/

 #include <stdio.h>
 #include <stdlib.h>
 #include <time.h>

 int main(int argc, const char * argv[])
 {
   if(argc != 2) {
     printf("\nUsage: BUZZWORD number_of_phrases\n");
     return EXIT_FAILURE;
   }

   printf("Buzzword Generator\nBased on a program from Creative Computing, Morristown, New Jersey\n\n\nThis program prints highly acceptable phrases in 'educator-speak' that you can work into reports and speeches. Whenever a question mark is printed, type a 'Y' for another phrase or 'N' to quit.\n\n\nHere's the first phrase:\n");

   const char * adjectives1[] = {"ability", "basal", "behavioral", "child-centered", "differentiated", "discovery", "flexible", "heterogeneous", "homogenous", "manipulative", "modular", "tavistock", "individualized" };
   const char * adjectives2[] = {"learning", "evaluative", "objective", "cognitive", "enrichment", "scheduling", "humanistic", "integrated", "non-graded", "training", "vertical age", "motivational", "creative"};
   const char * nouns[] = {"grouping", "modification", "accountability", "process", "core curriculum", "algorithm", "performance", "reinforcement", "open classroom", "resource", "structure", "facility", "environment"};

   srand((unsigned int)time(NULL));
   printf("\n%s %s %s\n\n", adjectives1[rand() % 13], adjectives2[rand() % 13], nouns[rand() % 13]);

   return EXIT_SUCCESS;
 }

The if command in line 15 is a very important part of the C language. Up to now my program has executed each statement in order, starting from the top of the main function and working towards the bottom. Sometimes though I might want to execute some code only under certain conditions. This is where the if command comes in. The if command will evaluate whether an expression is true or false and then only execute some code if the expression is true.

The expression to be evaluated is placed in parentheses after the if command. This is known as the condition. So in this case, the expression I am evaluating is argc != 2. Notice that I use the argument name in exactly the same way I would use a normal variable. This is the first time I have used the != operator. It means not equal. In other words, I am checking to see if argc is not equal to 2. So this expression will evaluate as true if argc is equal to any other value than 2 and it will evaluate as false only if argc is equal to 2.

Now at this point you might be thinking, “hold on, I thought you wanted just one argument – the number of phrases that the user wants displayed – so why are you looking for two?”. Well, when C collects all the arguments, it also includes whatever I typed to start the program. So every C program will always have at least one argument, which in my case is “buzzword”. Any additional arguments will start from the second element of argv. This is why I am looking for a count of 2 and not 1.

OK what happens if the condition is true? In this case C will execute the body of the if statement, which is all of the statements within the braces that follow the if command. In my case, this will be the statements on lines 16 and 17. Note that the closing brace is not followed by a semi-colon, but the statements within the braces are.

You should be noticing a pattern here of using curly braces to define a block of code in C. You can see them on lines 12 and 28 to define the block of code that forms my main function, and on lines 13 and 16 to define the block of code that will be executed when the if statement evaluates as true.

But do you notice there’s a small difference in the way they’ve been used. On line 12 the opening curly brace is on its own line, under the function declaration it belongs to, while on line 13 the curly brace comes at the end of the line with the if statement.

Both of these are acceptable and my compiler won’t complain if I use either of these forms. Which form I choose is a matter of personal preference, although employers of professional programmers might give them a coding style guide that will tell them to use one type or another. What I should do though, is be consistent – choose the form I prefer and stick to it. I should never mix up the two styles as I’ve done here.

I’ll correct that now. I’m going to move the curly brace on line 12 so it comes at the end of the line 11.

When I’d done that, my main function looked like this:

int main(int argc, const char * argv[]) {
   if(argc != 2) {
     printf("\nUsage: BUZZWORD number_of_phrases\n");
     return EXIT_FAILURE;
   }

   printf("Buzzword Generator\nBased on a program from Creative Computing, Morristown, New Jersey\n\n\nThis program prints highly acceptable phrases in 'educator-speak' that you can work into reports and speeches. Whenever a question mark is printed, type a 'Y' for another phrase or 'N' to quit.\n\n\nHere's the first phrase:\n");

   const char * adjectives1[] = {"ability", "basal", "behavioral", "child-centered", "differentiated", "discovery", "flexible", "heterogeneous", "homogenous", "manipulative", "modular", "tavistock", "individualized" };
   const char * adjectives2[] = {"learning", "evaluative", "objective", "cognitive", "enrichment", "scheduling", "humanistic", "integrated", "non-graded", "training", "vertical age", "motivational", "creative"};
   const char * nouns[] = {"grouping", "modification", "accountability", "process", "core curriculum", "algorithm", "performance", "reinforcement", "open classroom", "resource", "structure", "facility", "environment"};

   srand((unsigned int)time(NULL));
   printf("\n%s %s %s\n\n", adjectives1[rand() % 13], adjectives2[rand() % 13], nouns[rand() % 13]);

   return EXIT_SUCCESS;
}

If I’d opted for the other style, it would look like this:

int main(int argc, const char * argv[]) 
{
   if(argc != 2) 
   {
     printf("\nUsage: BUZZWORD number_of_phrases\n");
     return EXIT_FAILURE;
   }

   printf("Buzzword Generator\nBased on a program from Creative Computing, Morristown, New Jersey\n\n\nThis program prints highly acceptable phrases in 'educator-speak' that you can work into reports and speeches. Whenever a question mark is printed, type a 'Y' for another phrase or 'N' to quit.\n\n\nHere's the first phrase:\n");

   const char * adjectives1[] = {"ability", "basal", "behavioral", "child-centered", "differentiated", "discovery", "flexible", "heterogeneous", "homogenous", "manipulative", "modular", "tavistock", "individualized" };
   const char * adjectives2[] = {"learning", "evaluative", "objective", "cognitive", "enrichment", "scheduling", "humanistic", "integrated", "non-graded", "training", "vertical age", "motivational", "creative"};
   const char * nouns[] = {"grouping", "modification", "accountability", "process", "core curriculum", "algorithm", "performance", "reinforcement", "open classroom", "resource", "structure", "facility", "environment"};

   srand((unsigned int)time(NULL));
   printf("\n%s %s %s\n\n", adjectives1[rand() % 13], adjectives2[rand() % 13], nouns[rand() % 13]);

   return EXIT_SUCCESS;
}

OK let’s get back to what my decision is doing. I’ve established that if the number of arguments is not equal to 2, the code between the curly braces will be executed. This code uses a printf function to display a useful reminder to the user of how the program should be used. This is followed by a return statement. This immediately ends the program.

Notice that I have returned the value EXIT_FAILURE and the return value at the end of the function has been changed from 0 to EXIT_SUCCESS. You’ll recall that returning a code of 0 indicates that the program ran successfully and terminated normally. In this case, because the program has detected the wrong number of arguments and is ending abnormally, I return a non-zero value. C has two special values defined, called EXIT_FAILURE and EXIT_SUCCESS. When the code is built, these will be replaced with values that are appropriate for the system the code is being built for. Using them helps to make my code more portable between systems and to future proof it to some extent.

If the condition is not true (i.e. the user entered the right number of arguments) then the body of the if, i.e. all of the code between the braces, will be ignored and execution will continue from the first statement following the closing brace.

I want to see how this works. I saved and compiled the program, then ran it without using any arguments, i.e. just with buzzword. I saw a helpful message:

Output of the Buzzword program when running the program without any command line arguments.
Output when running the program without any command line arguments

The I tried it again, but with two arguments, Buzzword 1 2 (note that arguments on the command line are separated with spaces, not commas and I don’t put parentheses around them like I would in a function call). I got the same message.

Finally I tried it with just one argument, Buzzword 3 and this time I was rewarded with a random phrase, as before. Note that all I am doing at the moment is checking for the right number of arguments. I’m not doing anything with the argument I receive. In fact it doesn’t matter at the moment whether the argument is 1, 99, or bananas, my program will quite happily give me a phrase in response to any of them, but it will only give one phrase, regardless of what number I use.

Output when running the program with a single command line argument.
Output when running the program with a single command line argument

Let me fix that. So far I have checked that the program received exactly one argument, but at the moment that argument can be anything. I need to make sure it’s a number. I changed my program to look this this (changes are highlighted):

/*******
 * Buzzword
 *
 * A program to create amusing fake jargon
 *******/

 #include <stdio.h>
 #include <stdlib.h>
 #include <time.h>
 #include <errno.h>
 #include <limits.h>

 int main(int argc, const char * argv[]) {

   // int numberOfPhrases = 0;
   
   if(argc != 2) {
     printf("\nUsage: BUZZWORD number_of_phrases\nWhere number of phrases is a whole number from 1 to %i\n", INT_MAX);
     return EXIT_FAILURE;
   } else {
     errno = 0;
     char * inputStr;
     long inputNum = (int) strtol(argv[1], &inputStr, 10);

     if(errno == ERANGE || *inputStr != '\0' || inputNum < 1 || inputNum > INT_MAX) {
       printf("\nUsage: BUZZWORD number_of_phrases\nWhere number of phrases is a whole number from 1 to %i\n", INT_MAX);
       return EXIT_FAILURE;
     }

     int numberOfPhrases = (int) inputNum;
   }

   printf("\nThe user asked for %i phrase(s)\n", numberOfPhrases);

   printf("Buzzword Generator\nBased on a program from Creative Computing, Morristown, New Jersey\n\n\nThis program prints highly acceptable phrases in 'educator-speak' that you can work into reports and speeches. Whenever a question mark is printed, type a 'Y' for another phrase or 'N' to quit.\n\n\nHere's the first phrase:\n");

   const char * adjectives1[] = {"ability", "basal", "behavioral", "child-centered", "differentiated", "discovery", "flexible", "heterogeneous", "homogenous", "manipulative", "modular", "tavistock", "individualized" };
   const char * adjectives2[] = {"learning", "evaluative", "objective", "cognitive", "enrichment", "scheduling", "humanistic", "integrated", "non-graded", "training", "vertical age", "motivational", "creative"};
   const char * nouns[] = {"grouping", "modification", "accountability", "process", "core curriculum", "algorithm", "performance", "reinforcement", "open classroom", "resource", "structure", "facility", "environment"};

   srand((unsigned int)time(NULL));
   printf("\n%s %s %s\n\n", adjectives1[rand() % 13], adjectives2[rand() % 13], nouns[rand() % 13]);

   return EXIT_SUCCESS;
 }

The first thing to notice is that my if statement has now become a bit more involved. You’ll recall that the body of an if statement is only executed if the condition is true. In my case, the code is only executed if argc does not equal 2. But I can also include some code that will only be executed if the condition is false. To do this, I add the keyword else after my first set of braces, then I add a second set of braces with the code that is to be executed if the condition is false. In my case, if the condition is false, i.e. argc does equal 2, then the code in lines 21 to 30 will be executed. This is shown in the diagram:

A diagram showing how code is executed in an if ... else statement.
Diagram showing how code is executed in an if … else statement

You’ll recall from the earlier discussion that the second argument to main is an array of string pointers and that each element points to one of the arguments supplied on the command line. I can obtain the string representing any of the arguments by specifying the relevant array index. Note, in line 23, that I get the second element of the array, [1], and remember that this is because the first element, [0], points to the text entered to start the program.

Because all the command line arguments are stored as strings, I need to convert the argument to an integer before I can do anything useful with it. I can convert a string to an integer using the strtol function. This is short for ‘string to long’. The long type is another type of integer that supports larger numbers than a standard int. The maximum number an integer variable can hold depends on how many bits (binary digits) it has. The size of these variables depends on which system my code is compiled to run on, but generally a long has more bits than an int. For example, an int might have 32 bits, which would mean it could store numbers between -2,147,483,648 and 2,147,483,647, while a long might have 64 bits, which would mean it could store numbers between -9,223,372,036,854,775,808 and 9,223,372,036,854,775,807.

Because I want an int rather than a long, on line 30 I use a typecast, (int), to treat the result as if it were an int. You might be wondering why I don’t just use a typecast directly on the string to convert it to an integer. The reason is because type casting does not actually change a value, it simply asks C to reinterpret it as another type. When I use a typecast to view a long as if it were an int I know that C is always going to be able to complete the operation because it is treating one number as another type of number. The worst thing that will happen is that some information will be lost  because the new int might not be big enough to hold the old number, but the program won’t crash. Remember that a string array doesn’t hold the actual text, it holds pointers to the text in memory, and a pointer is just a large number. So if I try to us a typecast on a string pointer, C assumes I simply want to treat the pointer as if it was an int – so I’ll end up with some sort of number, but not the one that I intended to get.

That’s where the strtol function comes in. It will try to make sense of the string as a number. If it can it will give me the number. If it can’t, it won’t crash, it will simply let me know – “Hey, I tried to make this happen, but it just didn’t work out. OK?” How does it do this?

The second argument to strtol is a reference to a char pointer. For now, don’t worry about how references work – I’ll come back to this in a later post. But simply know that when the strtol function ends, the variable inputStr, which I declare on line 22 and pass as a reference to strtol on line 23, will point to the last character of argv[1] that strtol reached when it stopped processing. In my case, if that is anything other than the null character, \0, that terminates the string, then Ie know that strtol hasn’t been successful in interpreting the whole string as a number.

The function strtol is also designed to work with a special error handling library called errno (short for error number). I imported this library on line 10. This library creates a special variable, also called errno. When strtol is called, it might change the value in errno if certain error conditions arise. If the number was outside the range of a long, errno will contain a value defined as ERANGE.

I check for both of these conditions with another if statement on line 25. This if statement looks a little different than the one you saw before, because it has several conditions each separated by two vertical bars, ||. This is a logical OR operator. I’ll be looking at logical operators in more depth in a future post, but for now know that this if statement will evaluate as true if the first condition is true OR the second condition is true OR the third condition is true, and so on, and it will evaluate as false only if none of the conditions are true.

So let’s examine what each of those conditions is doing.

errno == ERANGE

This is checking for an out of range error, as I mentioned earlier. This would only happen if the user entered a very large positive or negative number. But it’s important to trap all error conditions, however unlikely. One thing to note about errno is that it might have been set by a previous error, which means it’s important that I always set it to zero before calling the function I want to check. I do this on line 21.

*inputStr != '\0'

This is checking whether strtol stopped processing before it reached the null character at the end of the string. As I mentioned above, this indicates that strtol wasn’t able to successfully interpret the whole string as a number.

<strong>inputNum < 1</strong>

This introduces a new operator, less than <. As you might expect, this checks whether the left operand is less than the right operand. So this is checking if the user has entered a number smaller than 1. Clearly it doesn’t make sense for my program to display 0 phrases or a negative number of phrases, so I should reject any number that falls into this range.

<strong>inputNum > INT_MAX</strong>

I’m going to store the number of phrases to be shown as an int. As I mentioned earlier, the size of types can vary from one system to the next. Fortunately C has a useful library that defines a set of values that equate to these limits. I imported this library in line 11. One of the values it defines is INT_MAX, which equates to the largest number I can store in an int. You can see that this is using yet another new operator, >, which, I’m sure you’ve already guessed, means greater than. So this line is checking to see whether the user has entered a number that’s too big to store in an int. By rejecting numbers in that range I can be sure that when I convert the long to an int on line 30, I won’t be losing any information.

Because I am limiting the range of numbers that user can enter, it’s helpful to let the user know what those limits are so I’ve added a second line to the usage statement I show on lines 18 and 26. Note that I’ve reused INT_MAX here to show the upper limit.

You might be wondering about the third argument for strtol. This indicates what base the number should be interpreted as. In computing we often use a number base other than 10, most predominantly base 2 (binary), base 8 (octal) and base 16 (hexadecimal). I won’t torture my users by requiring them to enter numbers in anything other than decimal, so I’ve set the base to 10.

Notice that when I checked for ERANGE on line 25, the equality operator I use is not a single = sign, but a double ==. A single = sign means, I want the left operand to be set equal to the right operand. If I used a single = sign here, so that the condition was if(errno = ERANGE …, what do you think would happen?

C would interpret this as meaning, “I want errno to be set equal to ERANGE”. This is exactly what it would do. Not only that, but an assignment always evaluates as the value of the assignment. C treats zero as being equivalent to false and any non-zero number as being equivalent to true, so whether the code between the first set of braces would be executed, would depend on the value of ERANGE! You can see how easy it is for me to break my program by using = when I meant to use ==. Believe me when I say, programmers do this a lot while they are learning, and they continue to do it a lot, even when they are experienced, so it’s one of the first things to check for when my program doesn’t work as I expect.

On line 31 I am setting an int variable called numberOfPhrases to be equal to the number the user entered on the command line. Then on line 33 I am using printf to display the value of that variable. I’ll save the code, compile and run it to see that working.

When I tried to compile, however, I got this error:

A compiler error reporting that numberOfPhrases has not been declared.
Output showing scope error

The compiler is trying to claim that I haven’t declared the variable numberOfPhrases, but I have declared it, back on line 30! Has the compiler lost the plot?

Well actually the compiler is right! The problem I’ve run into here has to do with something called variable scope. It’s another issue that new programmers will encounter frequently as they are developing their programming skills and it’s an important concept to understand in C and many other languages.

Imagine a theme park called “Fun Days” that gives me a choice for how I buy my tickets. I can go into the theme park and I can buy a ticket for an individual ride. If I just want to go on the Big Dipper, I can buy a ticket for that ride, but the ticket will only be valid for that ride. If I try to use that ticket to go on the the Crazy Swings ride, the attendant will say “Sorry – this ticket isn’t valid here.”

A diagram showing that tickets for an individual ride are not valid on another ride.
A diagram showing that tickets for an individual ride are not valid on another ride

However, I can choose instead to buy a “Fun Days” ticket at the gate that will let me into any ride within the park. Now the attendants at both the Big Dipper and the Crazy Swings rides will quite happily accept that ticket.

A diagram showing that the Fun Days ticket is valid for any ride in the park.
A diagram showing that the Fun Days ticket is valid for any ride in the park

You could say that the ‘scope’ of the Big Dipper ticket is limited to the the Big Dipper ride, while the ‘scope’ of the Fun Days ticket encompasses the whole theme park, which includes both the Big Dipper and Crazy Swings rides.

In C, whenever I use braces, {}, the code within the braces defines a scope. So consider my main function as being somewhat like the Fun Days theme park, and consider the code within the braces following my else statement as being like the Big Dipper ride. When I declare a variable inside the braces following my else statement, that’s like buying a ticket that’s just for the Big Dipper ride. While I’m within those braces it’s valid, but try to use it outside of the braces and it won’t be valid.

A diagram showing that the scope of a variable declared within braces is constrained to that block of code.
The scope of a variable declared within curly braces is constrained to that block of code

So how do I get round this? Well, in the theme park, I wanted a ticket that would work on both rides. Similarly, I want a variable that’s valid for use within the else braces but can also be with the printf function outside the braces. The solution for the rides is to trade up to a Fun Days ticket which I buy at the gate. I’m going to adopt the same solution for the variable. By declaring it within the main body of my function, it will be available throughout the function, not just within the else braces.

On line 15 you’ll see I’ve included a declaration of the numberOfPhrases variable, but I’ve put two forward slashes at the start of the line. You’ll recall from earlier in this post that this turns the line into a comment. Previously I’ve used comments to simply add useful information to the file. Another common use of comments is to temporarily disable lines of code. In a later post I’ll explore why this is useful in testing. For now, I’ll delete the backslashes so the statement is enabled.

int numberOfPhrases = 0;

Now, when I save and compile the code, that seems to work – phew! Then I ran it with an argument of 3, and was surprised to see this:

Output from the Buzzword program showing that the number of phrases is being reported incorrectly.
tart of output when running the program with an argument of 3

What’s going on here? I clearly entered a request for three phrases, but the program is claiming I asked for 0!

Well, another aspect of scope is that I am allowed to redefine a variable, as long as the scope for the redefined variable is different from the scope of the original variable. When I do this, the new version of the variable hides the previous version while the new version is within scope. The following example shows how this works:

A diagram showing an example of variable hiding.
A diagram showing an example of variable hiding

So that’s what’s happening with my code. Because I still have the variable declaration in line 30, I am effectively defining a new version of numberOfPhrases that will only be visible within the else braces.

This is not what I want, so I’ll fix it by removing int from the start of line 30, so it reads:

numberOfPhrases = (int) inputNum;

When I now save, compile and run again with an argument of 3 I saw that the program reported it correctly:

Output of Buzzword program showing that the requested number of phrases is now being reported correctly.
Output when running the program with an argument of 3 after fixing the scope issues

Don’t worry if you’re still struggling to get your head around the concept of scope. It’s one of the trickier concepts in programming and it will come with practice as you write and analyse more code.

I want to check if my new validation code is working. I tried running my program with bananas as the argument. Again, I saw the helpful reminder of how to use the program:

Output of the Buzzword program when bananas is entered as an argument.
Output when trying to run the program with a string argument

I satisfied myself that the validation code was working by entering a variety of valid and invalid arguments. All these worked:

  • buzzword 1
  • buzzword 99
  • buzzword 1000

and all these were rejected (showing me the usage message)

  • buzzword 0
  • buzzword -1
  • buzzword 99redballoons

Great work. In the next post I’ll continue to make improvements to my program. In the meantime, here’s a couple of challenges I set myself.

It would be useful if the argument was optional and defaulted to a value of 1 if it was missing. How could I modify the code to allow for that possibility?

While I’m at it, at the moment, I’ve set the upper limit on the number of phrases the learner can enter as being the maximum number I can store in an int. That’s a pretty big number. So I might also want to set a sensible and reasonable upper limit, say 99, on the number of phrases that can be generated. I’ve already used the less than (<) and greater than (>) operators. There are others I might want to use, like <= and =>. You should be able to work out what they do just by looking at them. I’ll explore them further in the next post.

I shouldn’t forget to update my usage messages to let the user know what the new upper limit is and that the argument is optional.

For my second challenge, you might have noticed that I now have two bits of code, lines 19-20 and lines 27-28 that do the exact same thing, but at different places in my program. Is this an acceptable situation? If you don’t think it’s acceptable, what might I be able to do about it? Solutions will be shown in the next post.

Published inGamesProgramming

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *