Bits and Bytes

Variables and Operators



Basic Data Types

In the original C language there were six basic (atomic) data types. These were char (1 byte), short (2 bytes), int (2 types), long (4 bytes), float (4 bytes), and double (8 bytes). As microprocessors got faster and memory cheaper there were some modifications made. Among the integers the int was upgraded to four bytes and a new integer data type, the long long (8 bytes) was added. Among the reals the new long double (8, 10, 12, or 16 bytes - platform dependent) was added. The diagrams below illustrate the number of bytes in each and the range of values each can hold.


Enumerated Data Types

One of the main purposes of enumerated data types is to make code more readable. You define an enumerated data type by using the keyword enum followed by the name of the new data type, then a comma separated list of "values":
	enum Number{ZERO, ONE, TWO, THREE, FOUR, FIVE, TEN=10, ELEVEN};
The actual values assigned to each of the items in the braces starts with zero for the first and increments sequentially. A specific value can be assigned to one of the "values" as shown. When this is done the subsequent items are given values sequentially from that value. Thus the defined values for the enum Number will be ZERO=0, ONE=1, TWO=2, THREE=3, FOUR=4, FIVE=5, TEN=10, and ELEVEN=11.

Having defined the enumerated data type you can now declare a variable of that type:
	Number num = ZERO;
Now the variable num can be used in code. Note: that it can only be assigned one of the values defined for it.
	if(num == ZERO) NUM = ONE;

      switch(num)
      {
          case ZERO :    // Do something
              break;
          case ONE :    // Do something
              break;
          case TWO :    // Do something
              break;
          case THREE :    // Do something
              break;
          case FOUR :    // Do something
              break;
      }


Memory Allocation of Variables

There are two places where variables can be allocated in memory. These are called the heap and the stack.

Any variable declared inside of a function is created on the stack. When the function exits all those variables are destroyed. If the above example the variables X, m, and the pointer ptr are destroyed when the function exits. Any memory that has been allocated using the new operator is allocated on the heap and does not get destroyed when the function exits unless the delete operator is used. Static instances of a class, such as m, in the example will automatically have their destructor called when the function exits.

Note the two uses of static. A variable that is defined as static such as static int count = 0; will be allocated on the heap. It will not be destroyed when the function exits but will be accessable any time a call is made to the function. When the function is called the value stored in count will be the same value that was left in the variable after the last call to the function.

The other use of static refers to how memory is allocated. We would say that for the variables X, m, and the pointer ptr the memory has been allocated "statically" while the memory allocated for the MyClass instance that ptr points to has been allocated "dynamically."

This example also shows that you can declare a variable to be a register variable. That means the memory space for it will be in one of the microprocessor registers. This make it very fast to access and use. However, declaring a variable as register is only a request. If no register is available then the variable will be created on the stack just like a normal variable. Also, you cannot declare a variable as a "static register" variable. This is not allowed.

Scope

Scope refers to where a variable can be accessed in a program. Take a look at the code example below. Be aware that some of this is for illustrative purposes only and should not be implemented in your code because it violates all the principles of object oriented programming. This refers specifically to the global variables declared outside of any function.
In this example two source files are illustrated. At the top of myFile1.cpp are declared a number of global variables. These variables can be accessed by any function in the file where they are declared. They can also be accessed within any other file if they are declared as extern in that file. There are many exceptions to these rules, however, and this is where the scope of a variable comes into play. Refer to the notes and comments below on the scope of each of the variables noted in this code.

  1. Here is a third use of the word static. A global variable such as s that is declared as static is only visible within the file in which it is declared. It cannot be declared as extern in myFile2.cpp.
  2. The variables x, y, and z declared in main() make the global variables x, y, and z invisible in main(). When a variable inside a block of code has the same name as a wider scoped variable outside its' block the inner scoped variable will mask assess to the outer scoped variable.
  3. The ch variable in function1() makes the global ch variable invisible to function1(). See the notes in 2 above.
  4. The x variable in function1() makes the global x variable invisible to function1(). See the notes in 2 above. Because it has been declared as static the x variable will retain the value it holds between calls to the function.
  5. While the for loop is executing the y variable used as a loop counter will mask access to the global y variable.
  6. This cout will print the value in the for loop counter variable.
  7. After leaving the block of code in the for loop the y variable used as a loop counter will be destroyed. Scope now lets this statement access the global y variable.
  8. The static x variable declared in function1() is incremented. It will still hold this value the next time function1() is called.
  9. The char variable ch is declared as extern this means that any references to a global ch variable in myFile2.cpp will actually be accessing the global ch variable declared in myFile1.cpp.
  10. The int variables x and z are declared as extern this means that any references to a global x or z variable in myFile2.cpp will actually be accessing the global x and z variables declared in myFile1.cpp. Because of the location in the code of these declarations these variables will only be visible below this point. The x and z variables will not be visible to function2().
  11. The float variable n1 is declared as extern this means that any references to a global n1 variable in myFile2.cpp will actually be accessing the global n1 variable declared in myFile1.cpp. Because of the location in the code of this declaration this variable will only be visible below this point. The n1 variable will not be visible to function2().
  12. The ch variable in function3() makes the global ch extern variable invisible to function3(). See the notes in 2 above.
  13. The cout<<x; will output the value of the global variable declared in myFile1.cpp, but inside the block of code surrounded by the stand along braces the new int x = 5; will now make the global x variable invisible. It will become visible again when execution exits the block where the new x variable was declared.


Pointers and References

By now you should be familiar with the use of the * operator to define a pointer and to dereference a pointer as shown in the illustration below. If not then maybe you should click here and fix that serious problem NOW!


You should also be familiar with how to create a reference function as shown on the right, in which the arguments to the function are treated as aliases for the variables used as the arguments to the function.

But you can also create a reference variable. These are like pointers that are automatically dereferenced so you don't have to worry about using the address or pointer operator. For example:
     int iVar;           // Create an int variable
     int& iRef = iVar;   // Create a referencd and set it to iVar
There are, however, some rules you have to follow in creating reference variables.

Pointer references

There may be situations in which you want to enable a function to change what a pointer passed to it as an argument is actually pointing to. There are two ways of doing this. The first passes a pointer to a pointer (a handle) to the function. The second passes a pointer reference to the function. Both are illustrated below.



Type Casting

In C and C++ you can change the value stored in a variable to another variable type for use in a math formula or for copying the value into a variable of a different type as shown below:


In arithmetic opeations if a float or double appears anywhere in the formula then all integer data types are automatically cast to double. This is called implicit type coercion. C++ now adds four special types of typecasting:



static_cast

The static_cast works the same as the old C style typecasting:


reinterpret_cast

The reinterpret_cast allows you to create a value of a new data type with the same bit pattern as the expression. You can do the following but you have to be careful and know what you are doing and why you are doing it:


const_cast

The const_cast allows you to make a variable value constant if the variable is not constant or remove the constness from a variables value if the variable is constant:


In this example:
  1. Function f expects a pointer to an int.
    Variable a is constant and cannot be changed.
    Pointer b is constant so a cannot be changed through it.
    Because f cannot take an argument that has been declared as const you must use a work around: Declare a normal (volatile) int pointer, c, and use const_cast to remove the constness. Thus: int* c = const_cast(b) gives a value that can be passed to f. This removes the constness and is called casting away constness,
  2. The compiler won't allow this because b points to a const int.
  3. The compiler will let you do this but you can't be sure of the results because c is pointing to an object that can't be changed.
  4. Function g expects a constant pointer. We can call it with b which is alreach const or we can cast &a1 or pointer b1 to constant and pass either to g.

dynamic_cast

The primary purpose of this cast is to safely cast a pointer to a parent class object to a pointer to one of its’ sub-classes. Use this if you know the type of the sub-class and need a pointer explicitly to an instance of that sub-class when you only have a parent class pointer pointing to it.





Operators

You will need to know each of the operators listed below and what they are used for to be an effective C++ programmer.


It is also important to know the precedence (order of execution) of each of the operators:




Operator Overloading

Any of the C++ operators can be overloaded for a different data type. Some already have been. The standard math operators (+ - * /) have all been overloaded for integer and real data types. The string template has had all of the logical operators overloaded so you can compare two string objects with (< > == != <= >+). The example below shows how the += operator can be overloaded for a linked list class.