Home

Variable Scope and Casting

 

Variables Local Scope

 

Introduction

The variables used in a program are always declared. The location you declare a variable controls its “visibility” and role in a program. The area where a variable is declared and can be accessed is referred to as its scope. This area usually has delimiters. The most used delimiter of scope is the curly bracket. If a variable is declared after an opening curly bracket “}”, that variable is available and accessible until the first and next closing bracket “}”. Such a variable is referred to as local. If you declare a variable outside of any function, such a variable is qualified as global. If a variable, mostly a constant is part of the operating system or the compiler, such a variable has the widest scope; it can be “seen” and accessible by any function and any program, provided the right header file is included.

Condition Scope

If you declare a variable inside of a conditional statement, that variable is available only inside of the statement. Here is an example:

using namespace System;

int main()
{
    int PropertyType;

    Console::WriteLine(L"What Type of House Would you Like to Purchase?");
    Console::WriteLine(L"0 - Any");
    Console::WriteLine(L"1 - Single Family");
    Console::WriteLine(L"2 - Townhouse");
    Console::WriteLine(L"3 - Condominium");
    Console::Write(L"Your Choice? ");
    PropertyType = int::Parse(Console::ReadLine());

    if( (PropertyType >= 0) && (PropertyType <= 3) )
    {
	String ^ Type;

	switch(PropertyType)
	{
	case 1:
		Type = L"Single Family";
		break;
	case 2:
		Type = L"Townhouse";
		break;
	case 3:
		Type = L"Condominium";
		break;
	default:
		Type = L"Type Unspecified";
	};

	Console::WriteLine(L"\nDesired Property Type: {0}", Type);
    }

    Console::WriteLine();

	return 0;
}

Here is an example of running the program:

What Type of House Would you Like to Purchase?
0 - Any
1 - Single Family
2 - Townhouse
3 - Condominium
Your Choice? 1

Desired Property Type: Single Family

Press any key to continue . . .

If you try to use the same variable outside of the conditional statement, you would receive an error because its "visibility" doesn't extend beyond the conditional statement. Consider this:

using namespace System;

int main()
{
	int PropertyType;

	Console::WriteLine(L"What Type of House Would you Like to Purchase?");
	Console::WriteLine(L"0 - Any");
	Console::WriteLine(L"1 - Single Family");
	Console::WriteLine(L"2 - Townhouse");
	Console::WriteLine(L"3 - Condominium");
	Console::Write(L"Your Choice? ");
	PropertyType = int::Parse(Console::ReadLine());

	if( (PropertyType >= 0) && (PropertyType <= 3) )
	{
		String ^ Type;

		switch(PropertyType)
		{
		case 1:
			Type = L"Single Family";
			break;
		case 2:
			Type = L"Townhouse";
			break;
		case 3:
			Type = L"Condominium";
			break;
		default:
			Type = L"Type Unspecified";
		};
	}

	Console::WriteLine(L"\nDesired Property Type: {0}", Type);

	Console::WriteLine();

	return 0;
}

When compiled, this program would produce the following error:

error C2275: 'System::Type' : illegal use of this type as an expression

Whenever you want a variable to be available or to be accessible by more than one area of influence, declare it outside of its scope.

Block Scope

You can also create and define your own scope. Such a scope starts with an opening curly bracket and ends with a closing curly bracket. This scope is called a block. The following program will produce an error because the side of the shape is confined to one scope and being accessed from outside:

using namespace System;

int main()
{
    {
        float Side, Area;

        Console::WriteLine("Enter the side of the square: ");
		Side = float::Parse(Console::ReadLine());
        Area = Side * Side;
		Console::WriteLine("\nThe area of the square is {0}", Area);
    }
    {
        Area = 6 * Side * Side;
	Console::WriteLine("\nThe area of the cube is {0}", Area);
    }

    Console::WriteLine();
    return 0;
}

A variable declared inside of a conditional or a block is said to have a block scope.

Function Scope

If you declare a variable inside of a function or a method but outside of any block, such a variable has a function or a method scope because it is accessible by any other variable that is inside of the same function or method. Consider the following example:

using namespace System;

int main()
{
    int Shape;
    double Radius;

    Console::WriteLine(L"Select the shape whose are you want to calculate.");
    Console::Write(L"1. Circle\n2. Sphere\nYour Choice: ");
    Shape = int::Parse(Console::ReadLine());
    Console::Write(L"Enter the radius: ");
    Radius = double::Parse(Console::ReadLine());

    if( Shape == 1 )
    {
        Console::WriteLine(L"\n - Circle -");
	Console::WriteLine(L"The area is: {0}", Radius * Radius * Math::PI);
    }
    else if( Shape == 2 )
    {
        Console::WriteLine(L"\n - Sphere -");
	Console::WriteLine(L"The area is: {0}", 4 * Radius * Radius * Math::PI);
    }

    Console::WriteLine();
    return 0;
}

Automatic Variables

If you declare a variable inside of a function or method, such a variable cannot be accessed outside of that function of method. If you try accessing it outside, you would receive an error.

A variable declared in a function or method is referred to as local. To indicate this, you can precede the data type of the variable with the auto keyword. Here is an example:

using namespace System;

int main()
{
	auto double Number = 2012535.088;

	Console::WriteLine(Number);

	Console::WriteLine();
	return 0;
}

Register Variables

When you declare a variable of a primitive type such as int, double, or char, the variable is stored in the stack as we mentioned earlier. The compiler is then in charge or managing it. As an alternative, you can suggest to the compiler to use a register to store the variable. To do this, start the declaration of the variable with the register keyword. Here is an example:

using namespace System;

int main()
{
	register int n = 202;

	Console::WriteLine(L"n = {0}", n);

	Console::WriteLine();
	return 0;
}

This would produce:

n = 202

Press any key to continue . . .

Global Scope

 

Global Variables 

Imagine you want a function to request a radius from the user and make that radius available to other functions. Such a function would look like this:

void GetRadius()
{
    double Radius;

    Console::Write("Enter the radius: ");
    Radius = double::Parse(Console::ReadLine());
}

The following two functions would need the value of that radius:

void ShowCircle()
{
    Console::WriteLine("\n - Circle -");
    Console::WriteLine("The area is: {0}", Radius * Radius * Math::PI);
}

void ShowSphere()
{
    Console::WriteLine("\n - Sphere -");
    Console::WriteLine("The area is: {0}", 4 * Radius * Radius * Math::PI);
}

If you compile the program with the functions defined like that, you would receive an error. As a first alternative, you could declare a radius variable in each function; the drawback is that each function would need and use its own variable. If you want different functions to share the same variable, in this case the same radius, declare it outside of any function; that way it would become available to all functions of the same file.

A variable declared outside of any function in the same header or source file is called a global variable. Such a variable can be accessed by any function or even class that exists in the same file. Here is an example of declaring variable:

using namespace System;

double Radius;

void GetRadius()
{
    Console::Write("Enter the radius: ");
    Radius = double::Parse(Console::ReadLine());
}

void ShowCircle()
{
    Console::WriteLine("\n - Circle -");
    Console::WriteLine("The area is: {0}", Radius * Radius * Math::PI);
}

void ShowSphere()
{
    Console::WriteLine("\n - Sphere -");
    Console::WriteLine("The area is: {0}", 4 * Radius * Radius * Math::PI);
}

int main()
{
    int Shape;

    Console::WriteLine("Select the shape whose are you want to calculate.");
    Console::Write("1. Circle\n2. Sphere\nYour Choice: ");
    Shape = int::Parse(Console::ReadLine());

    GetRadius();

    if( Shape == 1 )
    {
	ShowCircle();
    }
    else if( Shape == 2 )
    {
	ShowSphere();
    }

    Console::WriteLine();
    return 0;
}

This characteristic of C/C++ allows you to declare global variables that can be accessed by all functions of the same file.
Some languages, such as C, C++, Pascal, and Visual Basic have the concept of global variables.
Some other languages, such as C# and Java, don't allow global variables.
 

External Variables

By now, we know that you can use both header and source files in the same program. This feature of C++ allows you to declare a variable in a header file.  Here is an example of such a variable in a header file:
Header File: Exercise.h
using namespace System;

double Number;

After declaring a file in a header file, you can use that variable in a source file by including its header file in the source file. Here is an example:

Source  File: Exercise.cpp
#include "Exercise.h"
using namespace System;

int main()
{
	Number = 94.12;

	Console::WriteLine(L"Number: {0}", Number);

	Console::WriteLine();
	return 0;
}

This would produce:

Number: 94.12

Press any key to continue . . .

We have also learned that, in a source file, you could declare a global variable outside of any function. Imagine that, for some reason, you declare a global variable that holds the same name as a variable already in a header file. Here is an example:

Header File: Exercise.h
using namespace System;

double Number;
Source  File: Exercise.cpp
#include "Exercise.h"
using namespace System;

double Number;

int main()
{
	Number = 94.12;

	Console::WriteLine(L"Number: {0}", Number);

	Console::WriteLine();
	return 0;
}

If you compile this program, you would get the following error:

.\Exercise.cpp(4) : error C2086: 'double Number' : redefinition

When the compiler executes this application, it first reads the header file, finds a variable named Number and puts it in its table of already declared variables. When the compiler gets to the source file, it finds another variable named Number. Now, it realizes that it has two variables with the same name, which is not allowed in C++. The only this would work is if both names represent the same variable. To indicate this, when declaring the variable in the source file, you must indicate that you are representing the same variable that was already declared in the header file. To show this, you can precede the data type of the variable in the source file with with the extern keyword. This would be done as follows:

Header File: Exercise.h
using namespace System;

double Number;
Source  File: Exercise.cpp
#include "Exercise.h"
using namespace System;

extern double Number;

int main()
{
	Number = 94.12;

	Console::WriteLine(L"Number: {0}", Number);

	Console::WriteLine();
	return 0;
}

This time, the program would compile fine. Remember that, if you declare an extern variable in a source file and that variable already exists in the header file, the variable you are now using in the source file is actually the one you declared in the header file. This rule doesn't apply if the file is locally declared in a function in the source file. Such a variable would override the variable in the header file. Consider the following program:

Header File: Exercise.h
using namespace System;

double Number = 2148.52;
Source  File: Exercise.cpp
#include "Exercise.h"
using namespace System;

int main()
{
	double Number;

	Console::WriteLine(L"Number: {0}", Number);

	Console::WriteLine();
	return 0;
}

This would produce:

Number: 0

Press any key to continue . . .

And you would receive the following warning:

warning C4700: uninitialized local variable 'Number' used

Notice that, this time, the program compiles but with a warning. Since you should always treat a warning as an error, you can correct this by declaring the local variable as extern. Here is an example:

Header File: Exercise.h
using namespace System;

double Number = 2148.52;
Source  File: Exercise.cpp
#include "Exercise.h"
using namespace System;

int main()
{
	extern double Number;

	Console::WriteLine(L"Number: {0}", Number);

	Console::WriteLine();
	return 0;
}

This time, the program would compile fine and would produce:

Number: 2148.52

Press any key to continue . . .

Like a variable, a function can also be declared in a header file and implemented in a source file as extern.

Value Casting

 

Introduction

A compiler has the duty of rendering good results, but sometimes it cannot; it might figure out what is going on in your program. This means that, sometimes, you have to be explicit. When performing operations on variables of different types, you need to tell the compiler what form of result you are expecting. For example, you can ask the compiler to convert a variable or result from one type to another, otherwise the compiler might just "make up its own mind". And most of the time you will not like it.

Boxing

In Lesson 7, we saw that any value of ref class in C++/CLI is derived from the universal Object class. As the parent of all classes, an Object object can hold almost any value. Consider the following examples:

using namespace System;

int main()
{
	Object ^ objFullName = L"Gertrude Monayong";
	Object ^ objNumber   = 475540;

	Console::WriteLine(L"Name:   {0}", objFullName);
	Console::WriteLine(L"Number: {0}", objNumber);

	Console::WriteLine();

	return 0;
}

This would produce:

Name:   Gertrude Monayong
Number: 475540

Press any key to continue . . .

C Casting

Value casting consists of converting the type of data that a value is holding, into another type. There are three main techniques available in C++/CLI: C++/CLI boxing, the C fashion, and the C++ alternatives.

To cast one value into another, the C language provides the following formula:

(DataType)Expression

Consider the following example:

using namespace System;

int main()
{
    double number = 248.12;
    int    value  = (int)number;

    Console::WriteLine(L"Number: {0}", number);
    Console::WriteLine(L"Value:  {0}\n", value);
    
    return 0;
}

This would produce:

Number: 248.12
Value:  248

Press any key to continue . . .

In this example, the number variable, which is a decimal value, is converted into an integer. The C language also supports the following formula:

DataType(Expression)

In this example, you use the constructor of a data type to convert a value into it. Here is an example:

using namespace System;

int main()
{
    double number = 248.12;
    int    value  = int(number);

    Console::WriteLine(L"Number: {0}", number);
    Console::WriteLine(L"Value:  {0}\n", value);
    
    return 0;
}

C++ Casting

 

Static Casting

The C++ language provides a new operator to cast the value of a certain data type into another desired and more appropriate data type. This technique can be used to perform the same casting as above. This operator is called static_cast. Its syntax is:

static_cast<DesiredType>(Expression)

The static_cast keyword is required to let the compiler know that you want to perform a variable casting.

The DesiredType parameter must be a known type such as the data types we have used so far: integers, char, float, or double. It can also be a reference or a pointer. It can even be an enumeration. The DesiredType must be enclosed between < and >. The Expression parameter can be a value to cast. It can also be an operation or a complex expression.

Here is example:

using namespace System;

int main()
{
    double number = 248.12;
    int    value  = static_cast<int>(number);

    Console::WriteLine(L"Number: {0}", number);
    Console::WriteLine(L"Value:  {0}\n", value);
    
    return 0;
}

You Can void Anything, Only in C++

 

Introduction

In the previous lessons, we have always used the void keyword applied to functions that did not return a value. This is the most classic way of using void. This is also because void is not a data type. In C/C++, you can use void as a pointer and then make it assume any value you want. This means that, void as a pointer can point to anything, including values of primitive types or objects of classes. This concept of void is not available in C++/CLI. This means that you can (and probably should) skip the following two sections that are only available as a reference in C++.

voiding a Primitive Type

In C/C++, you cannot declare void as a variable. For example, the following program will not work:

#include <iostream>
using namespace std;

int main()
{
    void Number;
    
    return 0;
}

When compiled, this would produce an error. As stated above, void is not a data type and therefore cannot be used to declare a variable. On the other hand, you can declare a pointer to void. Once you have done this, void can then point to any type. Of course, as reviewed when studying pointers, before using a pointer to void, you must initialize it. You can do this by assigning it a variable previously declared from a primitive type. Here is an example:

#include <iostream>
using namespace std;

int main()
{
    void *pNumber;
    int   iNumber = 1558;

    pNumber = &iNumber;
    
    return 0;
}

You can also declare a pointer to a known type and assign it to a pointer to void, making the later usable. Here is an example:

#include <iostream>
using namespace std;

int main()
{
    void *pNumber;
    int  *iNumber = new int(1558);

    pNumber = iNumber;
    
    return 0;
}

Even if a pointer to void points to a known type, it is still void and, based on the characteristics of void, you cannot display its value using cout.

Passing void

One of the most regular uses of void as a pointer consists of passing it as argument. Here is an example:

void  Function(void *pNbr)
{
}

In this case, the argument specifies that void points (or can point) to anything. As mentioned for a pointer to void declared as a variable, before using this argument, you may need to initialize it. For example, if you want the argument to point to a primitive type, you can cast it to the appropriate type to retrieve its value. After doing this, you can then use its value. You can display it using the cout class. Here is an example:

#include <iostream>
using namespace std;

void  Function(void *pNbr)
{
    int *iNbr = static_cast<int *>(pNbr);

    cout << "*iNbr = " << *iNbr;
}

int main()
{
    int  iNumber = 1558;

    cout << "&iNumber = " << iNumber;
    cout << endl;

    iNumber = 980;
    Function(&iNumber);
    
    return 0;
}

This would produce:

&iNumber = 1558
*iNbr    = 980
Press any key to continue...

This would produce:

Variable Casting
3.14159 converted to an integer is 3
The float value 2.06 converted to an integer is 2
The double value 122.448 converted to an integer is 122
The integer 65 + the character r converted to an integer is 179
The integer 65 * the character r converted to a double is 7410
The double 122.448 / the character r converted to an integer is 1
sgHermaphrodite of the TAnimalGender enumerator being cast to an integer is 2

Press any key to continue...

Operator Precedence and Direction

When combining operations in C++, there are two aspects involved: an operator's precedence and its direction.

If you ask a program to add two numbers, for example 240 + 65, the program will execute the operation by adding 240 to 65; in other words, it would read 240, then +, then 65, and evaluate the result. This is considered as operating from left to right: 240 -> + -> 65.

On the other hand, if you ask the program to find the size of a variable, you would write sizeof(FirstName). In this case, the program would first consider the FirstName variable, then it would evaluate the size of that variable; in other words, it first finds out the variable it is operating on, in this case FirstName. Once it knows the variable that is involved, it would execute the operation. This is considered that the operation is executed from right to left.

This process is referred to as the direction of the operation.

As you will regularly combine operators on your various calculations, each operation is known for the amount "weighs" as compared to other operators. This is known as its precedence. This means that when a certain operator is combined with another, such as a + b * c, or x / y - z, some operators would execute before others, almost regardless of how you write the operation. That's why an operator could be categorized by its level of precedence.

Refer to Appendix A for a summary list of C++ operators, their precedence and direction


Previous Copyright © 2006 FunctionX, Inc. Next