Section 7.2. Argument Passing


7.2. Argument Passing

Each parameter is created anew on each call to the function. The value used to initialize a parameter is the corresponding argument passed in the call.

Parameters are initialized the same way that variables are. If the parameter has a nonreference type, then the argument is copied. If the parameter is a reference (Section 2.5, p. 58), then the parameter is just another name for the argument.



Exercises Section 7.1.2

Exercise 7.1:

What is the difference between a parameter and an argument?

Exercise 7.2:

Indicate which of the following functions are in error and why. Suggest how you might correct the problems.

      (a) int f() {              string s;              // ...              return s;          }      (b) f2(int i) { /* ... */ }      (c) int calc(int v1, int v1) /* ... */ }      (d) double square(double x) return x * x; 

Exercise 7.3:

Write a program to take two int parameters and generate the result of raising the first parameter to the power of the second. Write a program to call your function passing it two ints. Verify the result.

Exercise 7.4:

Write a program to return the absolute value of its parameter.


7.2.1. Nonreference Parameters

Parameters that are plain, nonreference types are initialized by copying the corresponding argument. When a parameter is initialized with a copy, the function has no access to the actual arguments of the call. It cannot change the arguments. Let's look again at the definition of gcd:

      // return the greatest common divisor      int gcd(int v1, int v2)      {          while (v2) {              int temp = v2;              v2 = v1 % v2;              v1 = temp;          }          return v1;      } 

Inside the body of the while, we change the values of both v1 and v2. However, these changes are made to the local parameters and are not reflected in the arguments used to call gcd. Thus, when we call

      gcd(i, j) 

the values i and j are unaffected by the assignments performed inside gcd.

Nonreference parameters represent local copies of the corresponding argument. Changes made to the parameter are made to the local copy. Once the function terminates, these local values are gone.



Pointer Parameters

A parameter can be a pointer (Section 4.2, p. 114), in which case the argument pointer is copied. As with any nonreference type parameter, changes made to the parameter are made to the local copy. If the function assigns a new pointer value to the parameter, the calling pointer value is unchanged.

Recalling the discussion in Section 4.2.3 (p. 121), the fact that the pointer is copied affects only assignments to the pointer. If the function takes a pointer to a nonconst type, then the function can assign through the pointer and change the value of the object to which the pointer points:

      void reset(int *ip)      {          *ip = 0; // changes the value of the object to which ip points          ip = 0;   // changes only the local value of ip; the argument is unchanged      } 

After a call to reset, the argument is unchanged but the object to which the argument points will be 0:

      int i = 42;      int *p = &i;      cout << "i: " << *p << '\n';   // prints i: 42      reset(p);                      // changes *p but not p      cout << "i: " << *p << endl;   // ok: prints i: 0 

If we want to prevent changes to the value to which the pointer points, then the parameter should be defined as a pointer to const:

      void use_ptr(const int *p)      {           // use_ptr may read but not write to *p      } 

Whether a pointer parameter points to a const or nonconst type affects the arguments that we can use to call the function. We can call use_ptr on either an int* or a const int*; we can pass only on an int* to reset. This distinction follows from the initialization rules for pointers (Section 4.2.5, p. 126). We may initialize a pointer to const to point to a nonconst object but may not use a pointer to nonconst to point to a const object.

const Parameters

We can call a function that takes a nonreference, nonconst parameter passing either a const or nonconst argument. For example, we could pass two const ints to our gcd function:

      const int i = 3, j = 6;      int k = rgcd(3, 6);   // ok: k initialized to 3 

This behavior follows from the normal initialization rules for const objects (Section 2.4, p. 56). Because the initialization copies the value of the initializer, we can initialize a nonconst object from a const object, or vice versa.

If we make the parameter a const nonreference type:

      void fcn(const int i) { /* fcn can read but not write to i */ } 

then the function cannot change its local copy of the argument. The argument is still passed as a copy so we can pass fcn either a const or nonconst object.

What may be surprising, is that although the parameter is a const inside the function, the compiler otherwise treats the definition of fcn as if we had defined the parameter as a plain int:

      void fcn(const int i) { /* fcn can read but not write to i */ }      void fcn(int i) { /* ... */ }            // error: redefines fcn(int) 

This usage exists to support compatibility with the C language, which makes no distinction between functions taking const or nonconst parameters.

Limitations of Copying Arguments

Copying an argument is not suitable for every situation. Cases where copying doesn't work include:

  • When we want the function to be able to change the value of an argument.

  • When we want to pass a large object as an argument. The time and space costs to copy the object are often too high for real-world applications.

  • When there is no way to copy the object.

In these cases we can instead define the parameters as references or pointers.

Exercises Section 7.2.1

Exercise 7.5:

Write a function that takes an int and a pointer to an int and returns the larger of the int value of the value to which the pointer points. What type should you use for the pointer?

Exercise 7.6:

Write a function to swap the values pointed to by two pointers to int. Test the function by calling it and printing the swapped values.


7.2.2. Reference Parameters

As an example of a situation where copying the argument doesn't work, consider a function to swap the values of its two arguments:

       // incorrect version of swap: The arguments are not changed!      void swap(int v1, int v2)      {          int tmp = v2;          v2 = v1;    // assigns new value to local copy of the argument          v1 = tmp;      }               // local objects v1 and v2 no longer exist 

In this case, we want to change the arguments themselves. As defined, though, swap cannot affect those arguments. When it executes, swap exchanges the local copies of its arguments. The arguments passed to swap are unchanged:

      int main()      {          int i = 10;          int j = 20;          cout << "Before swap():\ti: "               << i << "\tj: " << j << endl;          swap(i, j);          cout << "After swap():\ti: "               << i << "\tj: " << j << endl;          return 0;      } 

Compiling and executing this program results in the following output:

      Before swap(): i: 10 j: 20      After  swap(): i: 10 j: 20 

For swap to work as intended and swap the values of its arguments, we need to make the parameters references:

      // ok: swap acts on references to its arguments      void swap(int &v1, int &v2)      {          int tmp = v2;          v2 = v1;          v1 = tmp;      } 

Like all references, reference parameters refer directly to the objects to which they are bound rather than to copies of those objects. When we define a reference, we must initialize it with the object to which the reference will be bound. Reference parameters work exactly the same way. Each time the function is called, the reference parameter is created and bound to its corresponding argument. Now, when we call swap

      swap(i, j); 

the parameter v1 is just another name for the object i and v2 is another name for j. Any change to v1 is actually a change to the argument i. Similarly, changes to v2 are actually made to j. If we recompile main using this revised version of swap, we can see that the output is now correct:

      Before swap(): i: 10 j: 20      After  swap(): i: 20 j: 10 

Programmers who come to C++ from a C background are used to passing pointers to obtain access to the argument. In C++ it is safer and more natural to use reference parameters.



Using Reference Parameters to Return Additional Information

We've seen one example, swap, in which reference parameters were used to allow the function to change the value of its arguments. Another use of reference parameters is to return an additional result to the calling function.

Functions can return only a single value, but sometimes a function has more than one thing to return. For example, let's define a function named find_val that searches for a particular value in the elements of a vector of integers. It returns an iterator that refers to the element, if the element was found, or to the end value if the element isn't found. We'd also like the function to return an occurrence count if the value occurs more than once. In this case the iterator returned should be to the first element that has the value for which we're looking.

How can we define a function that returns both an iterator and an occurrence count? We could define a new type that contains an iterator and a count. An easier solution is to pass an additional reference argument that find_val can use to return a count of the number of occurrences:

      // returns an iterator that refers to the first occurrence of value      // the reference parameter occurs contains a second return value      vector<int>::const_iterator find_val(          vector<int>::const_iterator beg,             // first element          vector<int>::const_iterator end,             // one past last element          int value,                                    // the value we want          vector<int>::size_type &occurs)              // number of times it occurs      {          // res_iter will hold first occurrence, if any          vector<int>::const_iterator res_iter = end;          occurs = 0; // set occurrence count parameter          for ( ; beg != end; ++beg)              if (*beg == value) {                  // remember first occurrence of value                  if (res_iter == end)                     res_iter = beg;                  ++occurs; // increment occurrence count              }          return res_iter;  // count returned implicitly in occurs      } 

When we call find_val, we have to pass four arguments: a pair of iterators that denote the range of elements (Section 9.2.1, p. 314) in the vector in which to look, the value to look for, and a size_type (Section 3.2.3, p. 84) object to hold the occurrence count. Assuming ivec is a vector<int>, it is an iterator of the right type, and ctr is a size_type, we could call find_val as follows:

      it = find_val(ivec.begin(), ivec.end(), 42, ctr); 

After the call, the value of ctr will be the number of times 42 occurs, and it will refer to the first occurrence if there is one. Otherwise, it will be equal to ivec.end() and ctr will be zero.

Using (const) References to Avoid Copies

The other circumstance in which reference parameters are useful is when passing a large object to a function. Although copying an argument is okay for objects of built-in data types and for objects of class types that are small in size, it is (often) too inefficient for objects of most class types or large arrays. Moreover, as we'll learn in Chapter 13, some class types cannot be copied. By using a reference parameter, the function can access the object directly without copying it.

As an example, we'll write a function that compares the length of two strings. Such a function needs to access the size of each string but has no need to write to the strings. Because strings can be long, we'd like to avoid copying them. Using const references we can avoid the copy:

      // compare the length of two strings      bool isShorter(const string &s1, const string &s2)      {          return s1.size() < s2.size();      } 

Each parameter is a reference to const string. Because the parameters are references the arguments are not copied. Because the parameters are const references, is Shorter may not use the references to change the arguments.

When the only reason to make a parameter a reference is to avoid copying the argument, the parameter should be const reference.



References to const Are More Flexible

It should be obvious that a function that takes a plain, nonconst reference may not be called on behalf of a const object. After all, the function might change the object it is passed and thus violate the constness of the argument.

What may be less obvisous is that we also cannot call such a function with an rvalue (Section 2.3.1, p. 45) or with an object of a type that requires a conversion:

      // function takes a non-const reference parameter      int incr(int &val)      {          return ++val;      }      int main()      {          short v1 = 0;          const int v2 = 42;          int v3 = incr(v1);   // error: v1 is not an int          v3 = incr(v2);       // error: v2 is const          v3 = incr(0);        // error: literals are not lvalues          v3 = incr(v1 + v2);  // error: addition doesn't yield an lvalue          int v4 = incr(v3);   // ok: v3 is a non const object type int      } 

The problem is that a nonconst reference (Section 2.5, p. 59) may be bound only to nonconst object of exactly the same type.

Parameters that do not change the value of the corresponding argument should be const references. Defining such parameters as nonconst references needlessly restricts the usefulness of a function. As an example, we might write a program to find a given character in a string:

      // returns index of first occurrence of c in s or s.size() if c isn't in s      // Note: s doesn't change, so it should be a reference to const      string::size_type find_char(string &s, char c)      {          string::size_type i = 0;          while (i != s.size() && s[i] != c)              ++i;                   // not found, look at next character          return i;      } 

This function takes its string argument as a plain (nonconst) reference, even though it doesn't modify that parameter. One problem with this definition is that we cannot call it on a character string literal:

      if (find_char("Hello World", 'o')) // ... 

This call fails at compile time, even though we can convert the literal to a string.

Such problems can be surprisingly pervasive. Even if our program has no const objects and we only call find_char on behalf of string objects (as opposed to on a string literal or an expression that yields a string), we can encounter compile-time problems. For example, we might have another function is_sentence that wants to use find_char to determine whether a string represents a sentence:

      bool is_sentence (const string &s)      {           // if there's a period and it's the last character in s           // then s is a sentence           return (find_char(s, '.') == s.size() - 1);      } 

As written, the call to find_char from inside is_sentence is a compile-time error. The parameter to is_sentence is a reference to const string and cannot be passed to find_char, which expects a reference to a nonconst string.

Reference parameters that are not changed should be references to const. Plain, nonconst reference parameters are less flexible. Such parameters may not be initialized by const objects, or by arguments that are literals or expressions that yield rvalues.



Passing a Reference to a Pointer

Suppose we want to write a function that swaps two pointers, similar to the program we wrote earlier that swaps two integers. We know that we use * to define a pointer and & to define a reference. The question here is how to combine these operators to obtain a reference to a pointer. Here is an example:

      // swap values of two pointers to int      void ptrswap(int *&v1, int *&v2)      {          int *tmp = v2;          v2 = v1;          v1 = tmp;      } 

The parameter

      int *&v1 

should be read from right to left: v1 is a reference to a pointer to an object of type int. That is, v1 is just another name for whatever pointer is passed to ptrswap.

We could rewrite the main function from page 233 to use ptrswap and swap pointers to the values 10 and 20:

      int main()      {          int i = 10;          int j = 20;          int *pi = &i;  // pi points to i          int *pj = &j; // pj points to j          cout << "Before ptrswap():\t*pi: "               << *pi << "\t*pj: " << *pj << endl;          ptrswap(pi, pj); // now pi points to j; pj points to i          cout << "After ptrswap():\t*pi: "               << *pi << "\t*pj: " << *pj << endl;          return 0;      } 

When compiled and executed, the program generates the following output:

      Before ptrswap(): *pi: 10 *pj: 20      After ptrswap():  *pi: 20 *pj: 10 

What happens is that the pointer values are swapped. When we call ptrswap, pi points to i and pj points to j. Inside ptrswap the pointers are swapped so that after ptrswap, pi points to the object pj had addressed. In other words, pi now points to j. Similarly, pj points to i.

7.2.3. vector and Other Container Parameters

Ordinarily, functions should not have vector or other library container parameters. Calling a function that has a plain, nonreference vector parameter will copy every element of the vector.



In order to avoid copying the vector, we might think that we'd make the parameter a reference. However, for reasons that will be clearer after reading Chapter 11, in practice, C++ programmers tend to pass containers by passing iterators to the elements we want to process:

Exercises Section 7.2.2

Exercise 7.7:

Explain the difference in the following two parameter declarations:

      void f(T);      void f(T&); 

Exercise 7.8:

Give an example of when a parameter should be a reference type. Give an example of when a parameter should not be a reference.

Exercise 7.9:

Change the declaration of occurs in the parameter list of find_val (defined on page 234) to be a nonreference argument type and rerun the program. How does the behavior of the program change?

Exercise 7.10:

The following program, although legal, is less useful than it might be. Identify and correct the limitation on this program:

      bool test(string& s) { return s.empty(); } 

Exercise 7.11:

When should reference parameters be const? What problems might arise if we make a parameter a plain reference when it could be a const reference?


      // pass iterators to the first and one past the last element to print      void print(vector<int>::const_iterator beg,                 vector<int>::const_iterator end)      {          while (beg != end) {              cout << *beg++;              if (beg != end) cout << " "; // no space after last element          }          cout << endl;      } 

This function prints the elements starting with one referred to by beg up to but not including the one referred to by end. We print a space after each element but the last.

7.2.4. Array Parameters

Arrays have two special properties that affect how we define and use functions that operate on arrays: We cannot copy an array (Section 4.1.1, p. 112) and when we use the name of an array it is automatically converted to a pointer to the first element (Section 4.2.4, p. 122). Because we cannot copy an array, we cannot write a function that takes an array type parameter. Because arrays are automatically converted to pointers, functions that deal with arrays usually do so indirectly by manipulating pointers to elements in the array.

Defining an Array Parameter

Let's assume that we want to write a function that will print the contents of an array of ints. We could specify the array parameter in one of three ways:

      // three equivalent definitions of printValues      void printValues(int*) { /* ... */ }      void printValues(int[]) { /* ... */ }      void printValues(int[10]) { /* ... */ } 

Even though we cannot pass an array directly, we can write a function parameter that looks like an array. Despite appearances, a parameter that uses array syntax is treated as if we had written a pointer to the array element type. These three definitions are equivalent; each is interpreted as taking a parameter of type int*.

It is usually a good idea to define array parameters as pointers, rather than using the array syntax. Doing so makes it clear that what is being operated on is a pointer to an array element, not the array itself. Because an array dimension is ignored, including a dimension in a parameter definition is particularly misleading.



Parameter Dimensions Can Be Misleading

The compiler ignores any dimension we might specify for an array parameter. Relying, incorrectly, on the dimension, we might write printValues as

      // parameter treated as const int*, size of array is ignored      void printValues(const int ia[10])      {           // this code assumes array has 10 elements;           // disaster if argument has fewer than 10 elements!           for (size_t i = 0; i != 10; ++i)           {               cout << ia[i] << endl;           }      } 

Although this code assumes that the array it is passed has at least 10 elements, nothing in the language enforces that assumption. The following calls are all legal:

      int main()      {          int i = 0, j[2] = {0, 1};          printValues(&i);      // ok: &i is int*; probable run-time error          printValues(j);      // ok: j is converted to pointer to 0th                               // element; argument has type int*;                               // probable run-time error          return 0;      } 

Even though the compiler issues no complaints, both calls are in error, and probably will fail at run time. In each case, memory beyond the array will be accessed because printValues assumes that the array it is passed has at least 10 elements. Depending on the values that happen to be in that memory, the program will either produce spurious output or crash.

When the compiler checks an argument to an array parameter, it checks only that the argument is a pointer and that the types of the pointer and the array elements match. The size of the array is not checked.



Array Arguments

As with any other type, we can define an array parameter as a reference or nonreference type. Most commonly, arrays are passed as plain, nonreference types, which are quietly converted to pointers. As usual, a nonreference type parameter is initialized as a copy of its corresponding argument. When we pass an array, the argument is a pointer to the first element in the array. That pointer value is copied; the array elements themselves are not copied. The function operates on a copy of the pointer, so it cannot change the value of the argument pointer. The function can, however, use that pointer to change the element values to which the pointer points. Any changes through the pointer parameter are made to the array elements themselves.

Functions that do not change the elements of their array parameter should make the parameter a pointer to const:


      // f won't change the elements in the array      void f(const int*) { /* ... */ } 


Passing an Array by Reference

As with any type, we can define an array parameter as a reference to the array. If the parameter is a reference to the array, then the compiler does not convert an array argument into a pointer. Instead, a reference to the array itself is passed. In this case, the array size is part of the parameter and argument types. The compiler will check that the size of the array argument matches the size of the parameter:

      // ok: parameter is a reference to an array; size of array is fixed      void printValues(int (&arr)[10]) { /* ... */ }      int main()      {          int i = 0, j[2] = {0, 1};          int k[10] = {0,1,2,3,4,5,6,7,8,9};          printValues(&i); // error: argument is not an array of 10 ints          printValues(j);  // error: argument is not an array of 10 ints          printValues(k);  // ok: argument is an array of 10 ints          return 0;      } 

This version of printValues may be called only for arrays of exactly 10 ints, limiting which arrays can be passed. However, because the parameter is a reference, it is safe to rely on the size in the body of the function:

      // ok: parameter is a reference to an array; size of array is fixed      void printValues(int (&arr)[10])      {          for (size_t i = 0; i != 10; ++i) {              cout << arr[i] << endl;          }      } 

The parentheses around &arr are necessary because of the higher precedence of the subscript operator:


      f(int &arr[10])     // error: arr is an array of references      f(int (&arr)[10]) // ok: arr is a reference to an array of 10 ints 


We'll see in Section 16.1.5 (p. 632) how we might write this function in a way that would allow us to pass a reference parameter to an array of any size.

Passing a Multidimensioned Array

Recall that there are no multidimensioned arrays in C++ (Section 4.4, p. 141). Instead, what appears to be a multidimensioned array is an array of arrays.

As with any array, a multidimensioned array is passed as a pointer to its zeroth element. An element in a multidimenioned array is an array. The size of the second (and any subsequent dimensions) is part of the element type and must be specified:

      // first parameter is an array whose elements are arrays of 10 ints      void printValues(int (matrix*)[10], int rowSize); 

declares matrix as a pointer to an array of ten ints.

Again, the parentheses around *matrix are necessary:


      int *matrix[10];   // array of 10 pointers      int (*matrix)[10]; // pointer to an array of 10 ints 


We could also declare a multidimensioned array using array syntax. As with a single-dimensioned array, the compiler ignores the first dimension and so it is best not to include it:

      // first parameter is an array whose elements are arrays of 10 ints      void printValues(int matrix[][10], int rowSize); 

declares matrix to be what looks like a two-dimensioned array. In fact, the parameter is a pointer to an element in an array of arrays. Each element in the array is itself an array of ten ints.

7.2.5. Managing Arrays Passed to Functions

As we've just seen, type checking for a nonreference array parameter confirms only that the argument is a pointer of the same type as the elements in the array. Type checking does not verify that the argument actually points to an array of a specified size.

It is up to any program dealing with an array to ensure that the program stays within the bounds of the array.



There are three common programming techniques to ensure that a function stays within the bounds of its array argument(s). The first places a marker in the array itself that can be used to detect the end of the array. C-style character strings are an example of this approach. C-style strings are arrays of characters that encode their termination point with a null character. Programs that deal with C-style strings use this marker to stop processing elements in the array.

Using the Standard Library Conventions

A second approach is to pass pointers to the first and one past the last element in the array. This style of programming is inspired by techniques used in the standard library. We'll learn more about this style of programming in Part II.

Using this approach, we could rewrite printValues and call the new version as follows:

      void printValues(const int *beg, const int *end)      {          while (beg != end) {              cout << *beg++ << endl;           }      }      int main()      {          int j[2] = {0, 1};          // ok: j is converted to pointer to 0th element in j          //     j + 2 refers one past the end of j          printValues(j, j + 2);          return 0;      } 

The loop inside printValues looks like other programs we've written that used vector iterators. We march the beg pointer one element at a time through the array. We stop the loop when beg is equal to the end marker, which was passed as the second parameter to the function.

When we call this version, we pass two pointers: one to the first element we want to print and one just past the last element. The program is safe, as long as we correctly calculate the pointers so that they denote a range of elements.

Explicitly Passing a Size Parameter

A third approach, which is common in C programs and pre-Standard C++ programs, is to define a second parameter that indicates the size of the array.

Using this approach, we could rewrite printValues one more time. The new version and a call to it looks like:

      // const int ia[] is equivalent to const int* ia      // size is passed explicitly and used to control access to elements of ia      void printValues(const int ia[], size_t size)      {           for (size_t i = 0; i != size; ++i) {               cout << ia[i] << endl;           }      }      int main()      {          int j[] = { 0, 1 }; // int array of size 2          printValues(j, sizeof(j)/sizeof(*j));          return 0;      } 

This version uses the size parameter to determine how many elements there are to print. When we call printValues, we must pass an additional parameter. The program executes safely as long as the size passed is no greater than the actual size of the array.

Exercises Section 7.2.5

Exercise 7.12:

When would you use a parameter that is a pointer? When would you use a parameter that is a reference? Explain the advantages and disadvantages of each.

Exercise 7.13:

Write a program to calculate the sum of the elements in an array. Write the function three times, each one using a different approach to managing the array bounds.

Exercise 7.14:

Write a program to sum the elements in a vector<double>.


7.2.6. main: Handling Command-Line Options

It turns out that main is a good example of how C programs pass arrays to functions. Up to now, we have defined main with an empty parameter list:

      int main() { ... } 

However, we often need to pass arguments to main. TRaditionally, such arguments are options that determine the operation of the program. For example, assuming our main program was in an executable file named prog, we might pass options to the program as follows:

      prog -d -o ofile data0 

The way this usage is handled is that main actually defines two parameters:

      int main(int argc, char *argv[]) { ... } 

The second parameter, argv, is an array of C-style character strings. The first parameter, argc, passes the number of strings in that array. Because the second parameter is an array, we might alternatively define main as

      int main(int argc, char **argv) { ... } 

indicating that argv points to a char*.

When arguments are passed to main, the first string in argv, if any, is always the name of the program. Subsequent elements pass additional optional strings to main. Given the previous command line, argc would be set to 5, and argv would hold the following C-style character strings:

      argv[0] = "prog";      argv[1] = "-d";      argv[2] = "-o";      argv[3] = "ofile";      argv[4] = "data0"; 

Exercises Section 7.2.6

Exercise 7.15:

Write a main function that takes two values as arguments and print their sum.

Exercise 7.16:

Write a program that could accept the options presented in this section. Print the values of the arguments passed to main.


7.2.7. Functions with Varying Parameters

Ellipsis parameters are in C++ in order to compile C programs that use varargs. See your C compiler documentation for how to use varargs. Only simple data types from the C++ program should be passed to functions with ellipses parameters. In particular, objects of most class types are not copied properly when passed to ellipses parameters.



Ellipses parameters are used when it is impossible to list the type and number of all the arguments that might be passed to a function. Ellipses suspend type checking. Their presence tells the compiler that when the function is called, zero or more arguments may follow and that the types of the arguments are unknown. Ellipses may take either of two forms:

      void foo(parm_list, ...);      void foo(...); 

The first form provides declarations for a certain number of parameters. In this case, type checking is performed when the function is called for the arguments that correspond to the parameters that are explicitly declared, whereas type checking is suspended for the arguments that correspond to the ellipsis. In this first form, the comma following the parameter declarations is optional.

Most functions with an ellipsis use some information from a parameter that is explicitly declared to obtain the type and number of optional arguments provided in a function call. The first form of function declaration with ellipsis is therefore most commonly used.



C++ Primer
C Primer Plus (5th Edition)
ISBN: 0672326965
EAN: 2147483647
Year: 2006
Pages: 223
Authors: Stephen Prata

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net