The C ++ Programming Language Chapter VII Note

xiaoxiao2021-03-06  15

THE C Programming Language Chapter VII Note James Chen050311

********************* 7.1 Function declaration *********************

In a function declaration, you need to give the name of the function, the type of the value returned by this function (if any), and the number and type of the parameters that must be provided when calling this function. In the function declaration, you can include the name of the parameter, which may help the reader, but the compiler will simply ignore such a name.

7.1.1 Function Definition

A function called in the program must be defined in a place, just once. A function definition is to give a function declaration of a function body.

Void swap (int * p, int * q); // declaration

Void Swap (int * p, int * q) {INT T = * P; * p = * q; * q = t;}

The definition of a function and all of its claims must describe the same type. However, the parameter name is not part of the type, so the parameter name does not have to be consistent. Such as: void swap (int * p, int * q); // Declare Void Swap (int * a, int * b) {} // function defined parameter name can be different from the declaration, but the type must be the same. Void swap (int *, int *); // Declaration can also have no parameters

Inline (Inline, Online) function is compiled to expand the function to the direct embedding, run, skip the process of calling the function. Definition: Inline int max (int * a, int * b) {RETURN A> B? A: B;} example: int a = 22, b = 11, c; c = max (a, b); // This line will become: c = a> b? A: b;

7.1.2 Static variable

The local variable will initialize when the running thread reaches its definition. According to the default, this matter occurs in each call of the function, and each call of the function has a copy of its local variable. If a part variable is declared as static, only the only static allocated object is used, which is used to represent this variable in all calls of the function, which will only reach its definition for the thread. initialization.

Void f (int a) {while (a -) static int n = 0; // initialization when the function is called, and then call it again, INT x = 0; // When the function is called each time the function is called. Initialization COUT << "n ==" << n << ", x ==" << x << Endl;} void main () {f (3);} The result is: n == 0, x == 0N == 1, x == 0 N == 2, x == 0 // The operating value of the static variable N has been held, and the conventional X variable is not

Static variables provide a "memory" for functions so that we do not have to introduce global variables that may be accessed or damaged by other functions.

********************* 7.2 Function Pass **********************

When a function is called, the storage required to arrange the formal parameter is arranged, and each form parameter will initialize the corresponding actual parameters. The semantics of the parameter transmission are identical to the semantics of the initialization. In particular, it is necessary to check all standards and user-defined type conversions for the type of the actual parameters of each form parameter. There are some special rules to handle the passage of array parameters, a mechanism that communicates with parameters that does not check and a mechanism for portraying the default parameters. consider:

Void F (int A, int & b) {a ; b ;} When f is called, A adds a copy of the first actual parameter, and B is increasing is the second actual parameter itself, because B is A reference to the second actual parameter. The large object is transmitted by the reference method, which is higher than the speed passing, in which case, the parameters can be declared as const to specify that this parameter is only used for efficiency, not the function modifies the actual parameters. Value. If there is no const in a declaration of a reference parameter, it should be considered that this is to explain that the parameter will be modified. Similar to this, declare the pointer parameters as consts, which is to inform the reader, the function will not modify the object referred to by this parameter, such as: int Strlen (const char *); // Request CHAR * STRCPR (char * to, const char * from); // Copy the C-style string int strcmp (const char *, const char *); // Compare the importance of two C style string const parameters will be with the size of the program Increase and further increase.

The parameters, constants, and parameters that need to be converted can be passed to const & parameters, but cannot be passed to non-Const's reference parameters. Allow conversion of the const t & parameters, ensuring the set of values ​​that can be provided for such parameters, just the same set as the T-parameter passing through a temporary amount.

FLOAT F (Const float &); Void g (Double D) {float r = f (2.0f); // passed is a reference R = f (r) of the provisional amount of 2.0F; // transmits R Quote r = f (d); // passed D's reference, Double-> Float conversion} when incoming

For non-Const reference parameters do not allow type conversion, this rules can help us avoid a ridiculous mistake generated due to temporary, such as: Void Update (FLOAT & I); Void F (Double D, Float R) {Update (2.0f); // Error, 2.0F is a temporary amount, can not be passed to T & Update (R); // Transfer R Update (D); // Error, Guanji is float, And the argument is Double, no TYPE conversion

7.2.1 array parameters

If the array is used as a function of the function, it is passed to the pointer to the first element of the array. Such as: int Strlen (const char *) void f () {char v [] = "how are you"; int i = strlen (v); int J = Strlen ("Hahaha"); // is equivalent to a temporary array}

That is, the type T [] is transmitted as a parameter transmission to a T *. This means that the assignment of an element of an array parameter will change the value of the element in the actual parameter array. In other words, the array is different from other types, and the array does not pass the value of the value. That is to say, from T [] to T *, it is delivered.

For arrays, the size of the array parameters is unavailable. The C style string is ended in 0, and their size is easy to calculate. For other arrays, the second parameter can be passed, and it can identify the size of the array, such as 3:

Void f (int * c_ptr, int c_size);

For a way, we can also use the vector and other types without arrays.

The condition of the multi-dimensional array is more strange, but can usually use the arrays of the pointer, do not require special processing, such as: char * day [] = {"MON", "TUE", "WED", "THU", "fri", "sat", "su"}; again, the VECTOR and other types can be used instead of any such, low-level arrays and pointers.

********************* 7.3 Return value ********************************

If a function is a non-Void type, you must return a value. Of course, the function of the Void type cannot return a value. INT f () {} // error, no return value void f () {} // okint f () {return 1;} // okvoid f () {Return 1;} // Wrong, no return value INT f () {return;} // is wrong, no return value void f () {return;} // ok, although there is Return, but no return value

The return value can also be described by returning words, such as int FAC (INT N) {RETURN (N> 1)? N * FAC (N-1): 1;} // A call that calls its own function is called recursive

Like the semantics of the parameter transmission, the semantics of the function return value are also the same as the semantics of the initialization, and it can be considered that the return statement is to initialize an anonymous variable with return type. At this time, check the return type of the control function to the type of expression and perform the standard or user-defined conversion. Such as: double f () {return 2;} // 2 is implicitly converted to double (2)

Whenever a function is called, a copy of all of its parameters and local (automatic) variables will be established. After the function returns, these storage will be used by him. Therefore, it must not return a pointer to the local variable because the change in the content in the position is unpredictable: int * f () {int logal = 1; returnis & local;} //

This error generally does not have a similar error caused by reference: int & f () {int logal = 1; returni;} // is wrong, but also, most compilers can make a warning

A void function can use another VOID function as an expression in its returnite statement. Such as: Void G (INT * P); Void H (INT * P) {RETURN G (P);} // OK, returnless does not value it is equivalent to Void H (INT * P) {g (p);}

This return form has its importance. If the return type of the template function to be written is a template parameter, it is likely to use this thing.

********************* 7.4 Overload function name *********************

In general, different functions should have different names. However, it is convenient to give it the same name when some functions perform the same concept on different types of objects. The case where the same name is used for functions of different types of functions. Such as

Void Print (int); // Print intvoid print (const char *); // Print char *

Their functions are printed in the incoming parameters, but the parameter type is not the same, so the function name can be used, that is, the function is overloaded. The heavy-duty function name is fundamentally convenient to a notation, which is important for those commonly used functions, such as Open, Add, Print, etc., this convenience is important. When an overload function f () is called, the compiler must figure out which function should be called, in order to complete this work, it needs the function of the actual parameters with all the functions of F. The type of parameter is compared until it is found to find F () that matches the argument, this F (), if there is no matching function, give a compilation error. Such as: Void Print (Double); Void PRINT (LONG); Void f () {Print (1L); //print (LONG )PRINT (1.0); //print (double) print (1); // error, Dispowered, printed may be print (long (1)) or print (double)}

To find the correct version that should be actually called from a number of overload functions, you need to find the best match between the type of parameter expression and the type of function of the function. In order to close our most reasonable ideas, you need to check the following series of judgment guidelines in order:

[1] Accurately match, that is, there is no need to transform or just do just ordinary conversion (such as the array name to pointer, function name to function pointer, T to Const T, etc.). [2] Utilize the matching, that is, including integers (BOOL to INT, CHAR to INT, SHORT to INT, and its unsigned versions; and float to double). [3] Using standard conversion, (int to Double, Double, T * to Void *, int to unsigned int), using user-defined transformations. [5] Matching the number of omitted numbers in the function declaration.

If two matches are found at the same time at some highest level that can be found, this call will be rejected as ambiguity, such as:

Void Print (const char *); Void Print (double); Void Print (long); Void Print (Char); Void F (Char R, INT I, SHORT S, FLOAT F) {Print c); // ok, print (char) print (i); // ok, print (int) print (s); // ok, integer lifting, Print (int (s)) print (f); //////// OK, Float to Double, Print (Double (f)) Print ('a'); // OK, Print (25); // OK, Print (int) print (0); // OK, Print (int) Print ("addf"); // ok, print (char *)}

The overload analysis is independent of the order of the function declared. Relatively, the set of rules that rely on the overload is more complicated, but the programmer is very rarely surprised which functions are called. So why? Consider the replacement method of overload. We often need to perform similar operations for several types of objects. If there is no overload, we must use several different names to define several functions:

Void print_int (int); void print_char (char); void print_string (const char *); Void F (int I, char C, const char * p, double d) {print_int (i); // okprint_char (c); // okprint_string (p); // okprint_int (c); // can be used, but call print_int (int (c)) Print_Char (i); // can be used, but call print_char (char (i)) Print_String (I ); // error Print_INT (D); // ok}

Compared with overloaded Print (), we must remember several names at the same time, and remember how to use them correctly. This will be tired, but also to defeat universal type of programming, and prompt the programmer to pay attention to the relatively low type of problem. Because there is no overload function, all standard conversions will be used for parameters of these functions. This may also cause an error. In the above example, it can be seen that in the four "error" parameters here, the compiler can only capture one. From this, the overload can also increase the chance of the compiler to reject the inappropriate parameters.

7.4.1 Overload and return type

The return type will not be considered in the overload analysis. The reason why the specified reason is to keep the resolution of the overload only for a separate operator or function call, regardless of the calling environment. Consider: float f (float); Double F (Double); Void F (Double D, Float F) {float f = f (d); // Call F (Double) Double D = F (D); // Call f (double) f = f (f); // call f (float) d = f (f); // call f (float)}

If you take into account the return type, we can't continue to see a F () call isolated, and this is determined which function should be called.

7.4.2 Overload and scope of action Not overloaded in different non-name spatial scope. Such as: void f (int); void g () {void f (double); f (1); // call f (double)}

f (int) should be the best match for F (1), but only f (double) in the scope. For this type, the required behavior can be obtained by joining or removing the local declaration. As with other places, conscious masking can become a very useful technology, but unconscious shadow is an astonishing source. If you want to overrunate the use of declarations or use instructions, you can use a declaration or use instruction.

7.4.3 Handmade Ambiguity Analysis To a function, the overloaded version of the declaration is too small or too many can result in ambissions, such as: Void F1 (Char); Void F2 (char *); Void F2 (INT *);

Void F (INT I) {F1 (I); // Ambiguity, can't find F1 (int), maybe F1 (char) or F1 (long) F2 (0); // Ambiguity, F2 (char *) Or F2 (int *)}

As long as it is possible, in this case, it should be considered as a whole, see if they make sense to see if the semantics of functions. Problems are often solved by adding a release of a disliminating ablety. For example, add Inline Void F1 (INT A);} Inline Void F2 (INT A) {F2 (& A);} can decompose all similar to larger type long int. The ambiguity of F1 (i). You can also solve a particular call by adding an explicit type conversion. For example: F2 (static_cast (0)); however, this is only a kind of power, so soon it may encounter another similar call that must be processed. 7.4.4 Parametric parsing After the above-mentioned overload analysis rules, we can guarantee that when the different types involved are significantly different in calculating efficiency or accuracy, they will be the easiest algorithm. For example: int Pow (int, int); Double Pow (double, double);

Complex Pow (Double, Complex); Complex Pow (Complex, Int); Complex Pow; Complex Pow (Complex, Complex);

Void F (Complex Z) {INT i = POW (2, 2); // Pow (int, int) Double D = POW (2.0, 2.0); // Pow (Double, Double) Complex Z2 = POW (2, Z); // Pow (double, complex) Complex z3 = POW (z, 2); // Pow (complex) Complex z4 = POW (Z, Z); // Pow (complex, complex)}

If the selection process involves two or more parameters, the best match will be found for each parameter according to the rules of Section 7.4. If a function has the best match on a certain parameter, it is better than or equal to other functions that may be called, then it will be called. If there is no such function, this call will be seen as ambiguous and rejected. For example: void f () {double d = POW (2.0, 2); // error, POW (INT (2.0), 2) or POW (2.0, double (2))} This call has ambiguity because of 2.0 The best match is POW (Double, Double), while the best match for 2 is POW (int, int).

******************** 7.5 Default Parameters *********************

The parameters required for a general function are often more than the parameters required to handle the simple condition. In particular, those functions used for object constructs usually provide some options for flexibility. Consider a function of printing an integer, give the user a option to print it with a base, but in most programs, integers are printed in decimal form. For example: void print (int value, int base = 10); // The default base is 10, that is, decimal Void f () {print (31); // Print Print (31, 10) by default decimal; // Specify a decimal Print Print (31, 16); // Specify a hexadecimal print Print (31, 2); // Specify binary print} 31 31 1F 11111

It is also possible to achieve this default parameter by overloading: void print (int value); Inline Void Print (int value) {print (value, 10);} // default print decimal system, using overload Readers are not easy to see the original intent: use a function to add a function's shorthand form.

The type of default parameter will be checked when the function declaration is checked at the time of call. It is only possible to provide default parameters on those parameters arranged in the last, for example: INT F (int, int = 0, char * = 0); // Okint G (int = 0, int = 0, char *); // Error INT G (int = 0, int, char * = 0); // Error Please note that the space between * and = is important, otherwise it becomes * = operator. In the subsequent statement in the same scope, the default parameters cannot be repeated or changed. For example: Void f (int x = 7); Void F (int = 7); // error, the default parameter cannot repeat VOID F (int = 8); // error, the default parameter cannot be changed

Void g () {void f (int x = 9); // OK, different scopes, the outer layer declaration} Declare a name in the nested scope, let it shield the same name in the outer scope This approach is easy to make mistakes.

********************* 7.6 Undened number of parameters ******************* For some For functions, we have no way to determine the number and type of all parameters expected in each call. Declare this function is to end with the end of the parameter table (...), and the omitted number indicates that "there may be some other parameters". For example: int spotf (const char * ...); mom, it is difficult to understand, skip the first. . . .

**************************** *********** For a function Can only do two things: call it, or get its address. By taking the pointer to the address of a function, it can be used later to call this function. For example: void error (string s) {} void (* EFCT) (String); // Pointer Void f () {EFCT = & error; // gives an Error function address to EFCT ("Error! "); // call error function} via EFCT}

The compiler knows that the EFCT is a pointer and will call the fingered function, which means that the indirect operation of the function can be obtained from the pointer *. Similar to this, the "VOID (* F1) (String) = & error; // okvoid (* f2) = error; // ok, it is also like & Error Void g () {f1 ("AFJLSFD"); // OK (* f1) ("SDFSFSDF"); // ok} That is, the object to obtain the pointer is originally used, but if it is a function pointer, you want to take The function of the fingers can be used *, or it can be saved.

The parameter type is also required in the declaration of a pointer to the function, just like a function declaration. When the pointer is assigned, the function type of the full-matter must be fully matched. For example: void (* pf) (String); // pointing to VoID (String) Void F1 (String); // Void (String) INT F2 (String); // Int (String) Void F3 (INT *); / / void (int *)

Void f () {pf = & f1; // okpf = & f2; // error, Void (String) cannot point to int (string) PF = & f3; // error, void (String) does not point to Void (int *) PF ( "SDFSDF"); // okpf (1); // wrong, parameter type is wrong, need to be string instead of intint i = pf ("sdfsdf"); // is wrong, PF is Void, no return value} people often For convenience, a name is defined for the pointer type of the function, which avoids the meaning of the meaning of nothing everywhere. You can define a function pointer as a type: typedef void (int); typedef void (); sig_typ signal (int, sig_arg_typ);

The array of pointers that point to the function is very useful. The following is an example of a set of menu uses a function pointer array: typedef void (* pf) (); pf edit_ops = {& cut, & pause, & copy, & search}; pf file_ops = {& Open, & Append, & Close, & Write}; then we can define and initialize some pointers, to control various operations, select those operations by association with the mouse button: PF * Button2 = Edit_Ops; PF * Button3 = file_ops; button2 [2] (); // Call the COPY function button3 [1] (); // call the append function

For the parameter type matching of the overload function, the parameter type of the overload function should match: Void F (int); Void f (char); void (* f1) (int) = & f; // void f (int) void (* F2) (char) = & f; // void f (in * f3) (int) = & f; // error, no int F (int);

7.8 Macro C suggest the inline function instead of macro, try to use a macro. . . . For time, temporary. . Need to review it.

********************* 7.9 Advice ******************** [1] question those non- CONST reference parameters; if you want a function to modify its parameters, use the pointer or return value. [2] When you need to minimize parameter replication, you should use the const reference parameters. [3] Widely consistent use of const. [4] Avoid macro. [5] Avoid uncertain number of parameters. [6] Do not return a pointer or reference to a local variable. [7] Use the overload when some functions perform the same concept of different types of objects. [8] When overloading on various integers, provide a function to eliminate common ambiguity. [9] When considering the pointer to the function, consider the virtual function and template is not the best choice. [10] If you have to use a macro, use the name with uppercase letters.

转载请注明原文地址:https://www.9cbs.com/read-46229.html

New Post(0)