How to Read C++ Variable Declarations


C++ variable declarations can be quite complex and the order of the keywords and symbols matters. There is a specific process to follow in order to read them correctly:

Declaring complicated data types in C++ is an adventure. Complex type expressions are arguably one of the most convoluted things to decipher in C++. For example, the declaration "p is a pointer to a 6 by 3 array of pointers to arrays of 20 pointers to constant integers" appears as:

    const int *(*(* p)[6][3])[20];

Easy, no? Well, it will be after a few basic rules are explained.

Components and Placement

There are several components that can possibly make up a data type. Each one has a specific location with respect to the name of the data type. The name is usually a variable name or the name of a function parameter. The name is the pivot point around which all type declaration is centered. Below is a list of some of the components that go into a type declaration and where they are found with respect to the variable name.

Item Appears How to Read It
Data type such as int, unsigned int, double, Date, etc. to the left of the variable name exactly as it appears
const to the left of the variable name as "constant"
* to the left of the variable name as "pointer to"
& to the left of the variable name as "reference to"
( to the left of the variable name not read, skip to next step
[] To the right of the variable name "array of"
[6] To the right of the variable name as "array of 6" (or whatever number appears inside the brackets)
[][10] To the right of the variable name as "two-dimensional array of an unknown number of rows with 10 columns of" (or whatever number appears inside the brackets)
[4][10] To the right of the variable name as "two-dimensional array of 4 rows with 10 columns of" (or whatever numbers appear inside the brackets)
) to the right of the variable name not read, skip to next step

Declaration Order

The key to C++ type declaration is the order in which the basic components listed above are read. Unfortunately, it's not from left to right as we humans like to do. Rather, its from the middle out and jumps back and forth from left to right (or right to left). Fortunately, it's not too bad if you instead think of it as the following algorithm:

  1. Start at the variable name. Remember this location.
  2. Read to the right as far as possible, stopping if a right parenthesis ')' is encountered. Remember this location.
  3. Read to the left as far as possible, stopping if a left parenthesis '(' is encountered. Remember this location as needed.
  4. Move out by one level of parentheses as needed and go to step 2.

Examples

Here are some examples to help illustrate the correct process:

Declaration Meaning
int x

Start at the name, x, and move right. There is nothing there. Move to the left, starting from x, "is an int". "x is an int." Easy.
int* x

Start at the name, x, and move right. There is nothing there. Move to the left, starting from x, "is a pointer to". Keep going, "an int." Fairly straightforward. "x is pointer to an int."
int& x

"x is a reference to an int."
char ch

"ch is a char."
char* str

"str is a pointer to a char."
const char* str

"str is a pointer to a char constant."
char const* str

"str is a pointer to a constant char." How is this different than the previous example? It isn't. In both cases, it is the character that is constant. There are two equally valid ways to write this data type in C++.
char* const p

"p is a constant pointer to a char." Note that here it is the pointer that is constant, not the character to which it points.
string& str

"str is a reference to a string."
const string& str

"str is a reference to a string constant."
char text[31]

This is read starting from the name, "text is". Move right, "an array of 31" 31 what? Move right. Nothing there. Go back and move left, "char". "text is an array of 31 char."
int numbers[]

"numbers is an array of int."
int (*numbers)[]

This is read starting from the name, "numbers is". Go right. There is nothing after the name before the closing parenthesis is encountered. So change direction and go left. "a pointer to". Nothing more to the left before the parenthesis again. So this set of parentheses is finished. Step out of them and move to the right. "an array of". Keep going right, nothing there. Move back to the left where we left off. "int". So, "numbers is a pointer to an array of int."
char* argv[]

"argv is an array of pointers to char."
const char* const (&x)[12]

This is read starting from the name, "x is". Go right. There is nothing after the name before the closing parenthesis is encountered. So change direction and go left. "a reference to". Nothing more to the left before the parenthesis again. So this set of parentheses is finished. Step out of them and move to the right. "an array of 12". Keep going right, nothing there. Move back to the left where we left off. "constant". Keep going. "pointers to". Keep going. "char". Keep going. "constants". So, "x is a reference to an array of 12 constant pointers to char constants."