A simple description of the VA function)

Source: Internet
Author: User

This article mainly introduces the use of variable parameter functions, analyzes its principles, how programmers implement and encapsulate them, and finally possible problems and preventive measures.
Variable Argument function, a variable number of parameters, also known as a variable parameter function. In C/C ++ programming, the system provides few VA functions to programmers. * Printf ()/* scanf () functions are used to format strings during Input and Output. Exec * () functions are used to execute external files (main (INT argc, char * argv [] is not counted. Rather than using main () as a variable parameter function, it is better to say that it is a function with special functions and meanings after exec * () is encapsulated, at least there are many similarities at the principle level ). Because of the uncertainty of the number of parameters, the VA function is flexible and easy to use. It is tempting for programmers who have not used variable parameter functions. How can they compile their own va functions, how to Use va functions and compile them. The author will share with me some of his views on VA functions.

I. Starting with printf ()

The variable parameter functions are introduced from the familiar formatting string functions.

Prototype: int printf (const char * format ,...);

The format parameter indicates how to format string commands ,...

Optional parameter. The parameter passed to "..." during the call is optional and depends on the actual situation.

The system provides vprintf series string formatting functions for programmers to encapsulate their own I/O functions.

Int vprintf/vscanf (const char * format, va_list AP); // formatted string from standard input/output
Int vfprintf/vfsacanf (File * stream, const char * format, va_list AP); // stream from File
Int vsprintf/vsscanf (char * s, const char * format, va_list AP); // from the string

// Example 1: format a file stream for log files.

File * logfile;
Int writelog (const char * format ,...)
{
Va_list arg_ptr;

Va_start (arg_ptr, format );
Int nwrittenbytes = vfprintf (logfile, format, arg_ptr );
Va_end (arg_ptr );

Return nwrittenbytes;
}
...
// There is no difference between the call and the use of printf.
Writelog ("% 04d-% 02d-% 02d % 02d: % 02d: % 02d % S/% 04d logged out .",
Nyear, nmonth, nday, nhour, nminute, szusername, nuserid );
 

Similarly, you can also format the input from a file or format the standard input and output strings.

In example 1 above, the writelog () function can accept input with a variable number of parameters. In essence, its implementation requires the support of vprintf. How to implement your own variable parameter functions, including controlling each input optional parameter.

 

Back to Top
 

 

Ii. Va Function Definition and VA macro

C supports the VA function. c ++ supports the VA function as an extension of C. It is not recommended in C ++, the polymorphism introduced by C ++ can also implement functions with variable parameter numbers. However, C ++'s overload function can only be a limited number of foreseeable parameters. In comparison, the VA function in C can define an infinite number of overload functions equivalent to C ++. In this regard, C ++ is powerless. The advantages of VA functions are convenience and ease of use, which can simplify the code. In order to unify the implementation on different hardware architectures and hardware platforms and increase code portability, the C compiler provides a series of macros to shield the differences caused by different hardware environments.

In ansi c, VA macros are defined in stdarg. H. They include va_list, va_start (), va_arg (), and va_end ().

// Example 2: Calculate the sum of squares of any natural number:

Int sqsum (INT N1 ,...)
{
Va_list arg_ptr;
Int nsqsum = 0, n = N1;

Va_start (arg_ptr, N1 );
While (n> 0)
{
Nsqsum + = (N * n );
N = va_arg (arg_ptr, INT );
}
Va_end (arg_ptr );

Return nsqsum;
}

// Call time
Int nsqsum = sqsum (7, 2, 7, 11,-1 );
 

The prototype declaration format of variable parameter functions is:

Type vafunction (type arg1, type arg2 ,... );

Parameters can be divided into two parts: fixed parameters with fixed numbers and optional parameters with variable numbers. A function requires at least one fixed parameter. The fixed parameter must be declared in the same way as a common function. The number of optional parameters is unknown "... . The fixed parameters and optional parameters are the same as the parameter list of a function.

Let's take a look at the functions of each va_xxx using the simple example 2 above.
Va_list arg_ptr: defines a pointer to a variable number of parameter lists;

Va_start (arg_ptr, argn): sets the parameter list pointer arg_ptr to the first optional parameter in the function parameter list. Description: argn is a fixed parameter located before the first optional parameter (or, the last fixed parameter ;... The order of parameters in the function parameter list in the memory is the same as that in the function declaration. If the declaration of a va function is void va_test (char a, char B, char C ,...), Then its fixed parameters are a, B, c in sequence, and the last fixed parameter argn is C, so it is va_start (arg_ptr, c ).

Va_arg (arg_ptr, type): return the parameter indicated by the pointer arg_ptr In the parameter list. The return type is type, and the pointer arg_ptr points to the next parameter in the parameter list.

Va_copy (DEST, Src): the types of DeST and SRC are va_list. va_copy () is used to copy the parameter list pointer and initialize DEST as SRC.

Va_end (arg_ptr): clears the parameter list. The parallel parameter pointer arg_ptr is invalid. Note: After the pointer arg_ptr is set to invalid, you can restore arg_ptr by calling va_start () and va_copy. Each time va_start ()/va_copy () is called, a corresponding va_end () must match it. The parameter pointer can be freely moved back and forth in the parameter list, but must be in va_start ()... Within va_end.

 

Back to Top
 

 

Iii. How does the compiler implement va?

In Example 2, call sqsum (7, 2, 7, 11,-1) to calculate the sum of squares of 7, 2, 7, and 11.-1 indicates the end sign.

Simply put, the implementation of the VA function is the use and control of parameter pointers.

Typedef char * va_list; // va_list definition on the x86 Platform
 

The fixed parameter section of the function can be obtained directly from the parameter name when the function is defined. For the optional parameter section, first point the pointer to the first optional parameter, and then move the pointer backward, determine whether all parameters have been obtained based on the comparison with the end mark. Therefore, the ending mark in the VA function must be agreed in advance. Otherwise, the pointer will point to an invalid memory address, resulting in an error.

Here, the moving Pointer Points to the next parameter, so what is the offset when the pointer is moved? There is no specific answer, because the memory alignment problem is involved here, memory alignment is closely related to the specific hardware platform used. For example, the well-known 32-bit x86 Platform requires that all variable addresses must be multiples of 4 (sizeof (INT) = 4 ). In the VA mechanism, macro _ intsizeof (n) is used to solve this problem. Without these macros, VA portability is impossible.

First, we will introduce macro _ intsizeof (n), which is used to determine the size of the memory occupied by the variable, which is the basis for implementing va.

# DEFINE _ intsizeof (N) (sizeof (n) + sizeof (INT)-1 )&~ (Sizeof (INT)-1 ))
 

# Define va_start (AP, V) (AP = (va_list) & V + _ intsizeof (V) // The first optional parameter address
# Define va_arg (AP, t) (* (T *) (AP + = _ intsizeof (t)-_ intsizeof (t) // The next parameter address
# Define va_end (AP) (AP = (va_list) 0) // set the pointer to invalid
 

The following table is for the int testfunc (INT N1, int N2, int N3,…) function ,...) Memory stack when parameters are passed. (The default parameter passing method of the c compiler is _ cdecl .)

The call to this function is int result = testfunc (A, B, C, D. E), where E is the end mark.

 

 

We can clearly see the reason for writing the va_xxx macro.

1. va_start. To obtain the address of the first optional parameter, we have three methods:

A) = & N3 + _ intsizeof (N3)
// Address of the last fixed parameter + Memory occupied by this parameter

B) = & N2 + _ intsizeof (N3) + _ intsizeof (N2)
// The address of a fixed parameter in the middle + the sum of memory occupied by all fixed parameters after this parameter

C) = & N1 + _ intsizeof (N3) + _ intsizeof (N2) + _ intsizeof (N1)
// The address of the first fixed parameter + the sum of memory occupied by all fixed parameters

From the perspective of compiler implementation, Method B), method C) in order to find the address, the compiler still needs to know how many fixed parameters and their size, without breaking down the problem to the simplest, therefore, it is not a very clever way to choose not to adopt it. Comparatively, the two values calculated in method A can be completely determined. Va_start () uses the) method to accept the last fixed parameter. The result of calling va_start () always points the pointer to the address of the next parameter and uses it as the first optional parameter. When calling va_start () in a function with multiple fixed parameters, if the last fixed parameter is not used, the number of optional parameters has increased for the compiler, it will bring unexpected errors to the program. (Of course, if you think you have a thorough understanding of the pointer, you can use it to complete some excellent (efficient) Code. However, this greatly reduces the readability of the Code .)

Note: The macro va_start is used to operate on the parameter address. The parameter address must be valid. Some invalid addresses cannot be treated as fixed parameter types. For example, for the register type, its address is not a valid memory address value; arrays and functions are not allowed, and their length is a problem. Therefore, these types cannot be used as parameters of the VA function.

2. va_arg has two roles: return the current parameter and point the parameter pointer to the next parameter.

The definition of the va_arg macro is awkward. If we split it into two statements, we can clearly see the two responsibilities it has accomplished.

# Define va_arg (AP, t) (* (T *) (AP + = _ intsizeof (t)-_ intsizeof (t) // The next parameter address
// Split (* (T *) (AP + = _ intsizeof (t)-_ intsizeof (t):
/* Pointer AP points to the address of the next parameter */
1. AP + = _ intsizeof (t); // currently, the AP is directed to the next parameter.
/* AP obtains the address of the current parameter after subtracting the size of the current parameter, and then returns its value after forced type conversion */
2. Return * (T *) (ap-_ intsizeof (t ))
 

Looking back to formatting commands such as % d % s of the printf/scanf series functions, we can easily understand their usage-explicitly specify the type of mandatory conversion of parameters.

(Note: printf/scanf is not implemented using va_xxx, but the principle is consistent .)

3. va_end is simple, just to void the pointer.

# Define va_end (AP) (AP = (va_list) 0) // x86 Platform

 

4. conciseness, flexibility, and danger

From the implementation of VA, we can see that the rational use of pointers can fully express the concise and flexible features of the C language, which makes it hard to admire the power and efficiency of C. It is undeniable that too much free space for programmers will inevitably reduce program security. In va, va_arg must be used to traverse all parameters passed to the function in sequence. There are two risks:

1) determine the parameter type. Va_arg is not so flexible as to check the type, because it is forced type conversion, va_arg forcibly converts the content pointed to by the current pointer to the specified type;

2) End mark. If there is no end sign, VA will return the content in the memory in sequence based on the default type until the access to illegal memory and exit with an error. In Example 2, sqsum () is used to calculate the sum of the squares of natural numbers, so I use negative numbers and 0 as the end sign. For example, scanf regards the received carriage return as the ending mark. The well-known printf () uses '/0' as the ending mark for string processing, I cannot imagine how the code would look like if the string in C is not '/0'. It is estimated that the most popular character array or malloc/free would be at that time.

Allowing Random Access to memory leaves the possibility of attacks to malicious users. After processing a string carefully designed by the cracker, the program will jump to some malicious code areas for execution, so that the cracker can achieve its attack purpose. (Common Exploit attacks) Therefore, you must disable Random Access to the memory and strictly control the access boundary of the memory.

 

V. Va Declaration for UNIX System V compatibility

The declaration of variable parameter functions adopts ANSI standard. The declaration of UNIX System V compatibility is slightly different. It adds two macros: va_alist and va_dcl. And they are not defined in stdarg. h, but in varargs. h. Stdarg. H is ANSI standard; varargs. h only appears to be compatible with previous programs. It is not recommended in current programming.

Va_alist: It appears in the function header when the function declaration/definition is used to accept the parameter list.

Va_dcl: Specifies the va_alist statement, which does not need to be followed by a semicolon ";"

The va_start definition is also different. Because the system V Variable Parameter Function declaration does not distinguish between fixed parameters and optional parameters, you can directly operate on the parameter list. Therefore, va_start () is simplified to va_start (AP) instead of va_start (AP, V ). AP is a parameter pointer of the va_list type.

Function declaration in UNIX System V compatibility mode:

Type vafunction (va_alist)
Va_dcl // a semicolon is not required here
{
// The function body is the same as the ANSI standard
} // Example 3: execl implementation (UNIX System V compatibility mode), from sus v2

# Include <varargs. h>

# Deprecision maxargs 100
/* Execl (file, arg1, arg2,..., (char *) 0 );*/

Execl (va_alist)
Va_dcl
{
Va_list AP;
Char * file;
Char * ARGs [maxargs];
Int argno = 0;

Va_start (AP );
File = va_arg (AP, char *);
While (ARGs [argno ++] = va_arg (AP, char *))! = (Char *) 0)
;
Va_end (AP );
Return execv (file, argS );
}
 

 

Vi. Expansion and consideration

Variable parameters only need to be declared as "...". However, when we accept these parameters, we cannot "...". The key to implementing the VA function is how to obtain the optional parameters in the parameter list, including the parameter values and types. All the above implementations are based on the macro definition of va_xxx from stdarg. h. <Thinking> can we achieve va by ourselves with the help of va_xxx ?, The method I think of is assembly. In C, we certainly use C's Embedded Assembly for implementation, which should be possible. As for how much, stability, and efficiency can be achieved, it mainly depends on your control of the memory and pointer.

 

References

Single UNIX specification ver3 jointly developed by IEEE and opengroup,
Linux man manual,
X86 compilation, as well as some security coding materials.

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.