Chapter 6 - functions
1. Function basis
1.1 formal parameter list of function
-
The formal parameters in the formal parameter list are usually well separated. Even if the brother's formal parameters are of the same type, both types must be written out
int f3(int v1, v2) {/* ... */} // error int f4(int v1, int v2) {/* ... */} // correct
-
The return value of a function cannot be an array, but can be a pointer to an array or function
1.2 local objects
-
Life time
-
local variable: formal parameter and variable defined inside the function body
-
Automatic object: an object that exists only during fast execution. A formal parameter is an automatic object
-
local static object: it is initialized when the execution path of the program passes through the object definition statement for the first time and is destroyed until the program terminates. During this period, even if the execution of the function where the object is located ends, it will not be affected
// Count the number of times you have been called size_t count_calls() { static size_t ctr = 0; // ctr is a local static object. After the call, return ++ctr; // ctr is still valid } // Outputs numbers from 1 to 10 inclusive int main() { for (size_t i = 0; i != 10; ++i) cout << count_calls() < endl; return 0; }
1.3 function prototype
-
The declaration of a function does not include the function body, so formal parameters can be omitted
void print(vector<int>::const_iterator beg, vector<int>::const_iterator end);
-
Functions should be declared in the header file and defined in the source file
-
The header file containing the function declaration should be included in the source file that defines the function
1.4 separate compilation is omitted
2. Parameter transfer
2.1 pass reference parameters
-
By using reference parameters, functions can change the value of arguments
-
Use references to avoid copying
// Compare the length of two string objects. Since string objects may be very long, direct copy should be avoided bool &isShorter(const string &s1, const string &s2) { return s1.size() < s2.size() }
- If the function does not need to change the value of the reference parameter, it is best to declare it as a constant reference
2.2 const formal parameters and arguments
-
It is allowed to define several functions with the same name, provided that the formal parameter lists of different functions are obviously different
void fcn(const int i) {/* .. */} // fcn can read i, but cannot write value to i void fcn(int i) {/* ... */} // Error: duplicate definition of fcn(int)
-
You can initialize a low-level const object with a non constant, but you cannot initialize a non constant with a low-level const object
2.3 array parameters
-
Two properties of arrays:
- Copy is not allowed;
- Using an array converts it to a pointer
-
The array is passed to the function as a pointer, so the function does not know the exact size of the array at first, and the caller should provide some additional information for this
-
Use tag to specify array length: applicable to the case where there is an obvious end tag and the tag will not be confused with ordinary data
-
Use the standard library specification: pass pointers to the first and last elements of the array
void print(const int *beg, const int *end) { while(beg != end) cout << *beg++ << endl; } int j[2] = {0, 1}; print(begin(j), end(j));
-
Explicitly pass a formal parameter expressing the size of the array
void print(const int ia[], size_t size) { for (size_t i = 0; i != size; ++i) cout << ia[i] << endl; } int j[] = {0, 1}; print(j, end(j) - begin(j));
-
-
Array reference parameter
void print (int (&arr)[10]) { for (auto elem : arr) cout << elem << endl; }
-
&The parentheses at both ends of arr are indispensable
f(int &arr[10]) // arr is an array of references f(int (&arr)[10]) // arr is a reference to an integer array with 10 integers
-
The size of the array is part of the process array type, which limits the availability of the print function
-
-
Multidimensional array
void print(int (*matrix)[10], int rowSize) {/* ... */} // int (*matrix)[10]; Pointer to an array of 10 integers
- The first element of a multidimensional array itself is an array, and the pointer is a pointer to the array;
- The size of the second bit (and all subsequent dimensions) of a multidimensional array is part of the array type and cannot be omitted
2.5 main processing command function options
// Assuming that the main function is located in the pointing file prog, pass the following options to the program prog -d -o ofile data0 int main(int argc, char *argv[]) { ... } argv[0] = "prog"; // Or argv[0] can point to an empty string argv[1] = "-d"; argv[2] = "-o"; argv[3] = "ofile"; argv[4] = "data0"; argv[5] = 0;
- The second parameter argv is an array whose elements are pointers to C-style strings; The first parameter argc represents the number of strings in the array
- When using arguments in argv, the optional arguments start with argv[1], and argv[0] saves the name of the program instead of user input
2.6 functions with variable parameters
3. Return type and return statement
3.1 functions with return values
-
Reference return lvalue
char &get_val(string &str, string::size_type ix) { return str[ix]; } int main() { string s("a value"); cout << s << endl; get_val(s, 0) = 'A'; // Change the value of s[0] to A cout << s << endl; return 0; }
- If the function return type is a constant reference, the result of the call cannot be assigned a value
-
Function returns a list of values enclosed in curly braces
-
Recursive function: the function calls itself
// Calculate the factorial of val int factorial(int val) { if (val > 1) return factorial(val - 1) * val; return 1; }
- The main function cannot call itself
3.2 return array pointer
-
Use type alias
typedef int arrT[10]; // arrT is a type alias that represents an array of 10 integers using arrT = int[10]; // Equivalent to previous statement // Declares a func function that returns a pointer to an array containing 10 integers arrT* func(int i);
-
Do not use type aliases
int arr[10]; // arr is an array of 10 integers int *p1[10]; // p1 is an array of 10 pointers that point to integers int (*p2)[10]; // p2 is a pointer to an array containing 10 integers // Like the above declaration, when defining a function that returns an array pointer, the dimension of the array must follow the function name, such as int (*func(int i))[10];
- func(int i) means that an argument of type int is required when calling func function
- (* func(int i)) indicates that you can dereference the result of a function call
- (* func(int i))[10] indicates that the dereference results in an array of size 10
- int (*func(int i))[10] indicates that the elements in the array are of type int
-
Use trailing return type
// func accepts an int type parameter and returns a pointer to an array containing 10 integers auto func(int i) -> int(*) [10]
-
Use decltype
int odd[] = {1, 3, 5, 7, 9}; int even[] = {0, 2, 4, 6, 8}; // Returns a pointer to an array containing 5 integers decltype(odd) *arrPtr(int i) { // The result of the function call is dereferenced to get the odd type // Array of return (i % 2) ? &odd : &even; // Returns a pointer to an array }
4. Function overloading
-
Overloaded function: several functions in the same scope have the same name but different formal parameter lists
-
Two functions are not allowed. All other elements are the same except the return type
-
The top-level const does not affect the objects passed into the function: a parameter with a top-level const cannot be distinguished from another parameter without a top-level const
-
If the formal parameter of a function is a pointer or reference of some type, the function overload can be realized by distinguishing whether the object it points to is a constant. At this time, const is the bottom layer
-
const_cast and overload
string::size_type reset(const string &s) { auto ret = s.size(); for (decltype(ret) i = 0; i != s.size(); ++i) { if (s[i] == '.') ret = i; } return ret; } // Overloaded function shorterString, the return value is not from const string &, but from string& const string &shorterString(const string &s1, const string &s2) { return s1.size() <= s2.size() ? s1 : s2; } string &shorterString(string &s1, string &s2) { auto &r = shorterString(const_cast<const string&>(s1), const_cast<const string&>(s2)); return const_cast<string&>(r); }
-
If a name is declared in an inner scope, it hides entities with the same name declared in an outer scope
5. Special purpose language features
5.1 default argument
typedef string::size_type sz; string screen(sz ht = 24, sz = wid = 80, char backgrnd = ' ');
- Once a parameter is given a default value, all parameters following it must have a default value
- A formal parameter can only be given a default argument once in a given scope,
- Usually, you should specify the default argument in the function declaration instead and put the declaration in the appropriate header file
- Local variables cannot be used as default arguments
5.2 inline function and constexpr function
-
Inline function can avoid the cost of function call. Adding the keyword inline in front of the return type of the function can declare the function as an inline function
inline const string & shortString(const string &s1, const string &s2) { return s1.size() <= s2.size() ? s1 : s2; }
- Inline description knowledge is like a request issued by the compiler, and the compiler can choose to ignore this request
- Generally speaking, the introvert mechanism is used to optimize functions with small scale, direct process and frequent calls
-
constexpr function: a function that can be used for constant expressions
- The return type of a function and the types of all formal parameters must be literal types
- There must be only one return statement in the function body
- In order to expand at any time during compilation, the constexpr function is implicitly defined as an inline function
- Allows the return value of the constexpr function to be not a constant
-
Debugging help
-
assert preprocessing macro
assert (expr); // When the expression expr is false, it outputs information and terminates the execution of the program // When the expression expr is true, nothing is done
-
NDEBUG preprocessing variables
-
If NDEBUG is defined, assert does nothing. NDEBUG is not defined by default
// Write #define NDEBUG at the beginning of the main.c file $ CC -D NDEBUG mian.C # use /D with the Microsoft compiler
-
If NDEBUG is not defined, execute the code between #ifndef and #endif; If NDEBUG is defined, these codes will be ignored
-
-
Names that may be used in debugging
name effect type Definer __func__ Store function name A static array of const char compiler __FILE__ Storage file name string literal Preprocessor __LINE__ Store current line number Integer literal Preprocessor __TIME__ Compile time of stored files string literal Preprocessor __DATA__ Compile date of stored file string literal Preprocessor
-
6. Function pointer
-
The function pointer points to a function rather than an object, pointing to a specific type. The type of a function is determined by its return type and formal parameter type, regardless of the function name
// Compare the length of two string objects bool lengthCompare(const string &, const string &); // pf points to a function whose parameters are references to two const string s, and the return value is of type bool bool (*pf)(const string &, const string &); // uninitialized
-
*pf means pf is a pointer
-
The following formal parameter list indicates that pf points to a function
-
Bool on the left indicates that the return value of the function is of bool type
-
*The parentheses on both sides of pf cannot be omitted. When the parentheses are not written
// Declare a function named pf whose return value type is bool* bool *pf(const string &, const string &);
-
-
Using function pointers
-
When a function name is used as a value, the function is automatically converted to a pointer
pf = lengthCompare; // pf points to a function named lengthCompare pf = &lengthCompare; // Equivalent to previous statement
-
You can call the function directly with a pointer to the function without dereferencing the pointer in advance
// The following three statements are equivalent bool b1 = pf("hello", "goodbye"); bool b2 = (*pf)("hello", "goodbye"); bool b3 = lengthCompare("hello", "goodbye");
-
typedef
// Func is equivalent to Func2 and is a function type typedef bool Func(const string &, const string &); typedef decltype(lengthCompare) Func2; // FuncP is equivalent to FuncP2, which is a pointer to a function typedef bool (*FuncP)(const string &, const string &); typedef decltype(lengthCompare) *FuncP2;
-
using
using F = int(int*, int); // F is a function type, not a pointer using PF = int(*)(int *, int); // PF is a pointer type
-