First, the program passes the int value of 2 to cube(), which expects type double. The compiler, noting that the cube() prototype specifies a type double argument, converts 2 to 2.0, a type double value. Then cube() returns a type double value (8.0) to be used as an argument to cheers(). Again, the compiler checks the prototypes and notes that cheers() requires an int. It converts the return value to the integer 8. In general, prototyping produces automatic type casts to the expected types. (Function overloading, discussed in Chapter 8, can create ambiguous situations, however, that prevent some automatic type casts.)

Automatic type conversion doesn’t head off all possible errors. For example, if you pass a value of 8.33E27 to a function that expects an int, such a large value cannot be converted correctly to a mere int. Some compilers warn you of possible data loss when there is an automatic conversion from a larger type to a smaller.

Also prototyping results in type conversion only when it makes sense. It won’t, for example, convert an integer to a structure or pointer.

Prototyping takes place during compile time and is termed static type checking. Static type checking, as you’ve just seen, catches many errors that are much more difficult to catch during runtime.

Function Arguments and Passing by Value

It’s time to take a closer look at function arguments. C++ normally passes arguments by value. That means the numeric value of the argument is passed to the function, where it is assigned to a new variable. For example, Listing 7.2 has this function call:

double volume = cube(side);

Here side is a variable that, in the sample run, had the value 5. The function header for cube(), recall, was this:

double cube(double x)

When this function is called, it creates a new type double variable called x and initializes it with the value 5. This insulates data in main() from actions that take place in cube() because cube() works with a copy of side rather than with the original data. You’ll see an example of this protection soon. A variable that’s used to receive passed values is called a formal argument or formal parameter. The value passed to the function is called the actual argument or actual parameter. To simplify matters a bit, the C++ Standard uses the word argument by itself to denote an actual argument or parameter and the word parameter by itself to denote a formal argument or parameter. Using this terminology, argument passing initializes the parameter to the argument (see Figure 7.2).

Figure 7.2. Passing by value.

Variables, including parameters, declared within a function are private to the function. When a function is called, the computer allocates the memory needed for these variables. When the function terminates, the computer frees the memory that was used for those variables. (Some C++ literature refers to this allocating and freeing of memory as creating and destroying variables. That does make it sound much more exciting.) Such variables are called local variables because they are localized to the function. As mentioned previously, this helps preserve data integrity. It also means that if you declare a variable called x in main() and another variable called x in some other function, these are two distinct, unrelated variables, much as the Albany in California is distinct from the Albany in New York (see Figure 7.3). Such variables are also termed automatic variables because they are allocated and deallocated automatically during program execution.

Figure 7.3. Local variables.

Multiple Arguments

A function can have more than one argument. In the function call, you just separate the arguments with commas:

n_chars('R', 25);

This passes two arguments to the function n_chars(), which will be defined shortly.

Similarly, when you define the function, you use a comma-separated list of parameter declarations in the function header:

void n_chars(char c, int n)  // two arguments

This function header states that the function n_chars() takes one type char argument and one type int argument. The parameters c and n are initialized with the values passed to the function. If a function has two parameters of the same type, you have to give the type of each parameter separately. You can’t combine declarations the way you can when you declare regular variables:

void fifi(float a, float b)  // declare each variable separately

void fufu(float a, b)        // NOT acceptable

As with other functions, you just add a semicolon to get a prototype:

void n_chars(char c, int n); // prototype, style 1

As with single arguments, you don’t have to use the same variable names in the prototype as in the definition, and you can omit the variable names in the prototype:

Перейти на страницу:

Все книги серии Developer's Library

Похожие книги