Choosing Variable Names

It often surprises me that developers seem to choose completely useless variable names like MyVariable when creating an application. Although MyVariable could be an interesting variable name for an example in a book, it never has a place in any sort of production code. Even then, I try to create book examples with meaningful variable names, especially when getting past the initial “Hello World” example. Variable names are important because they tell others:

 

  • What sort of information the variable stores
  • When the variable is commonly used
  • Where the variable is used
  • How to use the variable correctly
  • Why the variable is important


In some cases, the variable name could even indicate who created the variable; although, this sort of information is extremely rare. If you never thought a variable name should contain all that information, then perhaps you haven’t been choosing the best variable names for your application.

Even with these restrictions in place, choosing a variable name can be uncommonly hard if you want to maximize the name’s value to both yourself and other developers. Some organizations make the selection process easier by following certain conventions. For example, a form of Hungarian Notation, where certain type prefixes, suffixes, and other naming conventions are used, is a common way to reduce the complexity of creating a variable name. In fact, Hungarian Notation (or some form of it) is often used to name objects, methods, functions, classes, and other programming elements as well. For example, NamArCustomers could be an array of customer names (Nam for names, Ar for array). The use of these two prefixes would make it instantly apparent when the variable is being used incorrectly, such as assigning a list of numbers to the array. The point is that an organizational variable naming policy can reduce complexity, make the names easy to read for anyone, and reduces the time the developer spends choosing a name.

 

Before I get a ton of e-mail on the topic, yes, I know that many people view Hungarian notation as the worst possible way to name variables. They point out that it only really works with statically typed languages and that it doesn’t work at all for languages such as JavaScript. All that I’m really pointing out is that some sort of naming convention is helpful—whether you use something specific like Hungarian Notation is up to you.


Any variable name you create should convey the meaning of that variable to anyone. If you aren’t using some sort of pattern or policy to name the variables, then create a convention that helps you create the names in a consistent manner and document it. When you create a variable name, you need to consider these kinds of questions:

 

  1. What information does the variable contain (such as a list of names)?
  2. How is the variable used (such as locally or globally, or to contain coordinates, or a special kind of object)?
  3. When appropriate, what kind of information does the variable contain (such as a string or the coordinate of a pixel on screen)?
  4. Is the variable used for a special task (such as data conversion)?
  5. What case should prefixes, suffixes, and other naming elements appear in when a language is case sensitive?

The point is that you need to choose variable names with care so that you know what they mean later. Carefully chosen variable names make it possible for you to read your code with greater ease and locate bugs a lot faster. They also make it easier for others to understand your code and for you to remember what the code does months after you’ve written it. However, most important of all, useful variable names help you see immediately that a variable is being using the wrong way, such as assigning the length of a name string to a coordinate position on screen (even though both variables are integer values). Let me know your thoughts about variable naming at John@JohnMuellerBooks.com.

 

Passing By Value Versus By Reference

A number of readers have written me about the examples that use pointers in the book. One of the most common questions comes from the example on page 149 of C++ All-In-One Desk Reference For Dummies. In this example, the application contains a function that accepts an argument by reference. The use of a reference seems to have many people confused and this post will hopefully clear the issue up.

The first thing you need to understand is that there isn’t any magic going on here. The process for working with functions remains constant in C++. When you call any function with an argument, the application creates memory to hold the argument. The memory is local to that function. When the function ends, the memory is released. What the memory holds, now that changes.

Think of the memory as a box. This box has an address that points to it. When you work with a pointer, you’re working with the address. On the other hand, when you work with the value, you’re working with the content pointed to by the pointer, or the contents of the box. Whether the box the function receives contains a pointer or the value depends on how you write the function and what you pass to it from the caller.

Now, let’s look at an example so that you can better understand what’s going on.  This example will do the same thing to the number each time, but using different techniques: by value, using a pointer, and by reference.

#include <iostream>
 
using namespace std;
 
void MessMeUpA(int myparam)
{
    myparam = myparam * 2 + 10;
}
 
void MessMeUpB(int* myparam)
{
    // Obtain the current value.
    int currentValue = *myparam;
 
    // Perform the required math.
    currentValue = currentValue * 2 + 10;
 
    // Save the result.
    *myparam = currentValue;
}
 
void MessMeUpC(int &myparam)
{
    myparam = myparam * 2 + 10;
}
 
int main()
{
    // Call by value.
    int mynumber = 30;
    MessMeUpA(mynumber);
    cout << "By Value: " << mynumber << endl;
 
    // Call using a pointer.
    mynumber = 30;
    MessMeUpB(&mynumber);
    cout << "By Pointer: " << mynumber << endl;
 
    // Call using a reference.
    mynumber = 30;
    MessMeUpC(mynumber);
    cout << "By Reference: " << mynumber << endl;
 
    return 0;
}

You may notice that part of this example comes directly from page 149. If you run this example, you’ll see this output:

PassingData01

When you pass the data by value, you’re passing the information, not a pointer to the information. As a result, MessMeUpA() receives a value of 30. It doesn’t receive a pointer to the initial variable, nor does it obtain a reference to the initial variable. As a result, when the application performs the calculation, the result is thrown away.

When the application calls MessMeUpB(), it provides a pointer to the variable. However, the pointer isn’t a value. As a result, you move the value pointed to by the pointer into a local variable, perform the required math, and then move the value back into the original pointer. As a consequence, the original value is altered to 70.

Finally, when the application calls MessMeUpC(), the function obtains a reference to the original memory location. When the function performs the math, it’s actually using the original value as pointed to by the reference, which means that this is a kind of pointer, just not passed from the caller. The changes made in MessMeUpC() are reflected in the original value because you’re using a pointer to that value and not a copy of the value in local memory.

I highly recommend that anyone trying to understand how code works use the debugger to trace through that code. It’s instructive to look at each of the functions to see how they look. Place a breakpoint in each of the functions and then start the debugger. Open the Watches window, if necessary, by choosing Debug | Debugging Windows | Watches. Here is the view of the Watches window when calling MessMeUpA().

PassingData02

What you see here is a function argument—an integer that contains the value of 30. Now, let’s look at MessMeUpB().

PassingData03

In this case, you see a local variable, currentValue, that contains the updated value and a pointer to the variable used by the caller in myparam. The pointer gives you access to the value held by the caller and allows you to change it. Finally, let’s look at MessMeUpC().

PassingData04

Look at the function argument, myparam. The debugger is giving you a valuable insight here. See the address provided with the variable? Match that address to the pointer provided to MessMeUpB() and you’ll see that they’re the same. The reference is simply another sort of pointer, but one that gives you direct access to the memory without having to go through the extra steps required by pointers. The debugger even shows you that the current value of that memory is 30 (the math hasn’t been performed as of yet).

A function argument is simply a box that holds either a value or a pointer. Whether that pointer is an actual pointer or a reference depends on how you write you code. That’s the point of the example. Pointers don’t have to be mysterious or hardthey’re simply an address for an area of memory (a box if you will) that holds a specific value. Please let me know if you have any questions about this issue at John@JohnMuellerBooks.com.