Curiouser and curiouser!
Lewis Carroll, Alice in Wonderland
When you call a function and pass into the function values for the function’s input parameters, the C/C++ compiler creates a copy of all the input parameters’ values and only then calls a function to process these values. For example, if you have a function defined as
void DrawCircle(COORD p, int r);
And you perform a call
COORD x = {10, 15};
DrawCircle(x, 23);
Then the computer will perform the following steps:
- Allocate memory for input parameters x and r.
- Copy values p => x, 23 => r.
- Execute the function DrawCircle().
This method of passing input parameters to a function is called passing by value because only values are passed into the function. It works great for built-in types since these types are small but it quickly becomes expensive for more complex struct types. It is not unusual to have a struct with several dozens fields. Allocating memory for a large struct and copying all the fields is a costly process. Thus, C/C++ language provides another methods for passing parameters to a function: passing by reference and passing by pointer.
To declare an input parameter as passed by reference, you need to put an ampersand ‘&’ in front of the variable name in the function declaration:
void DrawCircle(COORD & p, int r);
When you call the function in the usual way:
COORD x = {10, 15};
DrawCircle(x, 23);
The computer will no longer allocate memory for the input parameter p and will simply create a reference (alias) that maps variable p to the function’s input parameter x. Note that parameter r is still passed by value: it is just an integer and passing it by value is not slower than passing it by reference.
When you pass a parameter by reference, the function has access to the original variable and can modify it. This comes handy when you need to return multiple values from a function. For example, it is very common to have a function return a status code that indicates if there are any errors during function execution while the actual data is returned from a function through parameters passed by reference. For example, the following function will return status code from the function and the screen size will be returned in sz input parameter:
STATUS_CODE GetScreenSize(SIZE & sz);
...
SIZE screenSize;
if(GetScreenSize(screenSize) == 0) {
cout << “Screen size: “ << screenSize.w << “ x ” << screenSize.h << endl;
} else {
cout << “Error getting screen size” << endl;
}
If the function doesn’t modify input parameters, it can declare this by using the const keyword. For example, the DrawCircle function doesn’t need to modify its parameters and we can declare it as follows (note that there is no point in using const keyword for parameters passed by value because function can’t modify the variables regardless):
void DrawCircle(const COORD & p, int r);
It is usually a good idea to always mark with const the input parameters that the function doesn’t modify. This gives a extra clue to the compiler and the developer of how this function should be used and it helps to avoid stupid mistakes.
The last but not the least option for passing input parameters is passing by pointer. It is very similar to passing by reference: no value copying happens, the function gets access to the variable passed into the function and can potentially modify it. The syntax for passing by pointer is a little bit more complex than syntax for passing by reference and requires changes for function declaration and function call. The star ‘*’ is used in the function declaration to indicate that this parameter is passed by pointer and ampersand ‘&’ is used during the function call to get a pointer from a variable:
STATUS_CODE GetScreenSize(SIZE *sz);
...
SIZE screenSize;
if(GetScreenSize(&screenSize) == 0) {
cout << “Screen size: “ << screenSize.w << “ x ” << screenSize.h << endl;
} else {
cout << “Error getting screen size” << endl;
}
This is probably somewhat confusing but we are going to discuss pointers in great details later. Passing by reference is the preferred C++ way to avoid input parameters copying or return extra data from a function . Passing by pointers is the “old” C way used in C libraries and APIs (for example, Windows API is a C API).
Exercises
1) Look at the following functions and describe how input parameters are passed in and which parameters can be modified by the function:
bool GetScreenInfo(HANDLE hScreen, SCREEN_INFO & info); bool SetScreenInfo(HANDLE hScreen, const SCREEN_INFO & info); bool GetScreenInfo(HANDLE hScreen, SCREEN_INFO * info); bool SetScreenInfo(HANDLE hScreen, SCREEN_INFO const * info);
2) On a piece of paper, write example of how you would call each of the functions from 1).
3) Describe the meaning of the parameters for the “main” function (note that “char *” type is a string and that “char **” is simply a pointer to a string):
int main(int argc, char const ** argv);
4) Modify the Lunar Lander program and use passing by reference where appropriate. Don’t forget to use const keyword when function doesn’t modify input parameters passed by reference.
Pingback: Practical programming | Random Notes