Defining and Using Variables

Defining and Using Variables

Introduction

A variable in C/C++ is similar to a variable in algebra—essentially a variable is a name we associate with an underlying quantity or value. However, the use of variables in C/C++ is governed by various rules that we discuss here. Of special note is the fact that, in addition to having a value, variables have a type (that specifies what they are) and a scope (that specifies where they are defined or “visible”). Type, and some of the ways in which data are stored, are further explained by clicking on the box to the right.

Valid Identifiers

When you create a function or a variable, you must select an identifier for it (identifier is really just another word for name). You are free to choose whatever name you like provided it adheres to the following rules.

  1. Identifiers can only contain letters (upper and lower case), digits, and the underscore character.
    • Valid identifiers:
      • Foo1
      • blarg
      • F_oo57_b
    • Invalid identifiers:
      • F oo1 (cannot include a space)
      • bl@rg (at sign: punctuation other than underscore not allowed)
      • F_oo1-b (hyphen not allowed)
  2. Identifiers must start with a letter or an underscore.
    • Valid identifiers: one_two, __Foo, foo, _bar_.
    • Invalid identifiers: 1foo (cannot start with a digit).
  3. Identifiers cannot be the same as a keyword. Within MPIDE, several keywords like void, int, and if are highlighted in red or orange. So in a sketch if you write a “name” that appears highlighted, then it is a good indication that it would be best to choose another name. (However, many function names, such as pinMode(), appear highlighted but are not keywords. Thus, you could potentially define your own variables using these non-keyword names. However, that is not a good idea! If you do that, you will no longer be able to use the function.) A complete list of keywords in C++ is available via the box to the right.
  4. Identifiers are case sensitive, so pay attention to the use of capitalization. For example, the identifier name Foo is different from foo, which is different from fOO.
  5. In our projects, we will use the convention known as lowerCamelCase for the names of variables and functions. In this convention the first letter is lowercase and any subsequent “words” have their first letter capitalized.
    • Examples of lower camel case: pinMode(), digitalRead (), nameFirst, nameLast.
    • Examples of identifiers that do not adhere to lower camel case (but that are still valid): PinMode, led_pin, Lastname.
  6. Identifiers can be as short as a single letter (or a single underscore), but generally identifiers should be long enough to convey some additional information about what the associated variable or function is and how it should be used. However, variables that are used as integer counters are often a single letter. By convention, these single-character variables are either i, j, k, m, or n. These letters are in the range of i through n, which are the first two letters of the word integer. Note that one should avoid using the single-letter identifiers l and o because they can easily be confused for the digits one and zero.

Variable Declaration and Variable Type

Before using a variable, the computer must be told the variable's type. Data can be stored using various formats. For example, an integer variable can be stored in different ways depending on what its anticipated maximum value is and whether or not the variable can be negative (as well as positive). Also, real numbers (i.e., numbers that can have a fractional part) are stored differently from integer values.

To declare variables of a particular type we write a statement that specifies the type followed by a comma-separated list of names. Three common types are int, float, and double. The int data type is used for integers, i.e., whole numbers that do not have a fractional part. On the other hand, both the float and double data types are used to store real numbers (numbers that can contain a fractional part). floats are sometimes called “single precision” real numbers while doubles are sometimes called “double precision” real numbers. However, the amount of precision associated with these types is actually dependent on the hardware used, so there is no guarantee that precision is different between them—you have to read the documentation for the hardware to know for sure. (This is discussed further at the “Type and Storage” page available via the box at the top right of this page.)

As an example of the declaration of various variables, the following statements declare that the variables count1 and count2 are integers; temperature and humidity are single precision real numbers, and z is a double precision number.

                  int count1, count2;
                  float temperature, humidity;
                  double z;
                

It is also possible to initialize the value of a variable when it is declared (or, more technically, when it is defined). To do this, you merely use the assignment operator (in other words, the equal sign) to assign a value to the variable. For example, we can assign initial values to the variables described above with statements such as:

                  int count1 = 0, count2 = 100;
                  float temperature = 98.7, humidity = 54.321;
                  double z = -97.012;
                

As a matter of programming style, many programmers find it better to declare no more than one variable per line. Thus, the statements above would be written as:

                  int count1 = 0;
                  int count2 = 100;
                  float temperature = 98.7;
                  float humidity = 54.321;
                  double z = -97.012;
                

You should keep in mind that a finite number of bits are used to store variables so that values cannot be arbitrarily large. For example, float values are typically stored using 32 bits whereas double values are typically (but not always) stored using 64 bits.

Variable Scope

It is important to pay attention to where you declare a variable, i.e., pay attention to the location of the statement that tells the sketch of the existence of a variable. Depending on where this statement appears within your sketch, you may or may not be able to make use of the variable at other points in your sketch. The place where the variable is defined is known as the variable's scope. If the statement that declares a variable is outside of any function, that variable can be “seen,” used, and changed within all functions that follow the variable's declaration. Variables declared outside of a function are known as global variables. Figure 1 shows a sketch where the variable ledPin is a global variable because it is declared outside of any function. The square bracket to the right of the code is used to indicate that ledPin is “visible” throughout all these lines of code.

Figure 1. The variable “ledPin” is defined outside the functions. Its scope is thus that of a global variable.

Again, an important thing to note is that the variable is defined at all points in the code following the definition/declaration. Thus, if the setup() function in Fig. 1 was written before the declaration of the variable ledPin, an error would occur.

In contrast to a global variable, if you declare a variable inside of a function, that variable cannot be “seen” anywhere outside of that particular function. Said another way, the variable can only be used and changed inside of the body of the function in which it is declared. A variable declared in this way is known as a local variable. Figure 2 illustrates the creation of a local variable called ledPin within the body of the setup() function. Again, the square bracket to the right of the code indicates where the variable is “visible” (i.e., defined). Unfortunately, in the code in Fig. 2, the programmer tried to reference the ledPin variable within the loop() function, but such a reference falls outside the variable's scope. Thus, this code is broken and cannot be compiled—a fatal error occurs if you attempt to compile it and the sketch cannot be uploaded to a chipKIT™ board (fatal errors are ones that prevent compilation from completing).

Figure 2. The variable ledPin is local to the setup() function and thus cannot be used inside the loop() function. Therefore, this code cannot be compiled.

The sketch in Fig. 3 suffers from the same underlying problem that exists in the sketch of Fig. 2: the programmer is attempting to access a variable outside of its scope. In this case the variable ledPin is declared within the loop() function, but the programmer tries to reference it from within the setup() function (where it is not visible/defined). Therefore this sketch cannot be compiled.

Figure 3. The variable ledPin is local to loop() and cannot be used in the setup() function. Said another way, the body of the setup() function is outside of the scope of ledPin.

Qualifiers

As mentioned above, when a variable is declared, one must specify the variable's type (for example, int for an integer variable or float for a real number that can have a fractional part). Optionally, when a variable is declared, one can also provide qualifiers that specify additional aspects about the behavior of that variable. The const qualifier specifies that the variable should be treated as a constant. In other words, after its value is initially set, the value cannot be changed. Strictly speaking, this qualifier is never truly necessary—if a variable shouldn't change, then the programmer simply shouldn't change it—but the const qualifier helps document the code and it prevents programmers from accidentally changing a variable that shouldn't be changed. (When a variable is declared, you can write the const qualifier either before or after the variable's type, but it is most common to write const before the type and that is the style we advocate and use.)

As an example of the use of the const qualifier, the following sets the integer variable my_variable to 56:

                  const int my_variable = 56;
                

Note that if a variable is declared to be constant, it must be initialized to a value when it is declared. Thus, the following code is broken and will not compile:

            const int my_variable;
            my_variable = 56;       // ERROR! my_variable is constant and cannot be changed.
            

Variable Do's and Don'ts

Here are some other important rules about declaring and using variables:

  1. Two variables with the same name cannot be declared in the same scope. For example, whether within the body of a function or outside of any function, you could not have the following code:
    int ledPinA = 13;  // First declaration of ledPinA.
    int ledPinA = 8;   // ERROR!  Cannot declare the same variable twice in the same scope.
                            

    In this case the programmer probably made an error. Perhaps the second statement should have assigned a value to ledPinB instead of ledPinA. Whatever the case, the compiler will not allow this.

    There may be times that you want to experiment with different values of a variable, but want to maintain the information about the original value of the variable for future reference. In this case, simply maintain that “original” information in a comment. For example, the following is acceptable (even if a bit confusing):

    //int ledPinA = 13;  // Statement previously used to set ledPinA.
    int ledPinA = 8;   // Experiment with new value for ledPinA.
                            
  2. You can define two variables with the same name if they are in different scopes. This is not a problem if the variables with the same name are local variables (i.e., they are declared within the bodies of different functions).

    You can declare variables with the same name in different, but overlapping, scopes. However, just because you can do something doesn't mean you should! In fact, it is considered very bad programming style if a global variable and a local variable share the same name. Using global and local variables with the same name can cause quite a bit of confusion and often leads to errors. If a local variable has the same name as a global variable, the value of the local variable is used in the local scope. Thus, the local variable masks the value of the global variable. This is illustrated in Fig. 4. Note that the sketch in Fig. 4 can be compiled—there is no syntactic error in the code and it can be uploaded to a chipKIT board. (By syntactic error, we mean an error in the “grammar” of the language.) However, this sketch does contain a semantic error, i.e., an error in “meaning.” Thus, this sketch may not behave in the desired way because, although the programmer undoubtedly wanted to use the same pin throughout the sketch, pin 6 is initialized in the setup() function but pin 13 is used in the loop() function!

    Figure 4. Naming conflicts arise when both global and local variables are defined.

    The screenshots above are of MPIDE running on Microsoft Windows 7.

    Because of the confusion that can be caused by using both global and local variables, it is usually considered best to avoid the use of global variables whenever possible. When using a global variable is really the best option for implementing something, some programmers with use a variable name that explicitly indicates the variable is a global one. For example, one might use the identifier ledPinGlb to indicate that this is global variable corresponding to an LED pin (i.e., the Glb “suffix” indicates this is a global variable).

  3. C++ allows you to declare variables at almost any point in your code. However, it is common when defining local variables to declare all of them at the start of the function. With few exceptions (which we won't discuss here), we advocate and use that style.

  • Other product and company names mentioned herein are trademarks or trade names of their respective companies. © 2014 Digilent Inc. All rights reserved.