Deep Exploration of variable parameter table in C/C ++

Source: Internet
Author: User
Tags drawtext

Http://dev.yesky.com/424/2110924.shtml

  

Introduction

C/C ++ has a feature different from other languages, that is, it supports variable parameters. Typical functions such as printf and scanf can accept a certain number of parameters. For example:

Printf ("I Love You ");
Printf ("% d", );
Printf ("% d, % d", a, B );

The first, second, and third printf parameters are 1, 2, and 3 respectively. Let's look at the prototype of the printf function:

Int printf (const char * format ,...);

From the function prototype, we can see that apart from receiving a fixed parameter format, the following parameter uses "... . In C/C ++ ,"... "Indicates that an indefinite number of parameters can be accepted. Theoretically, it can be zero or more than N parameters.

This article will explore the use of variable parameters in C/C ++ and the deep mechanism that C/C ++ supports variable parameters.

  Variable parameter table usage

1. Related macros

The standard C/C ++ contains the header file stdarg. H, which defines the following three macros:

Void va_start (va_list arg_ptr, prev_param);/* ANSI version */
Type va_arg (va_list arg_ptr, type );
Void va_end (va_list arg_ptr );

Among these macros, VA refers to Variable Argument (Variable Parameter); arg_ptr refers to the pointer to the variable parameter table; prev_param refers to the previous fixed parameter of the variable parameter table; type is the type of a variable parameter. Va_list is also a macro defined as typedef char * va_list, which is essentially a char pointer. The Char pointer is characterized by ++ and -- the result of the operation on it is increased by 1 and decreased by 1 (because sizeof (char) is 1 ), the difference is that the ++ and -- Operations of other types of pointers such as int are used to increase or decrease sizeof (type), and sizeof (type) greater than 1.

Through the va_start macro, we can obtain the first pointer of the variable parameter table. The macro is defined:

# Define va_start (AP, V) (AP = (va_list) & V + _ intsizeof (v ))

Obviously, it means to assign the address of the last fixed parameter to the AP after adding the offset of the variable parameter, so that the AP is the first address of the variable parameter table. The _ intsizeof macro is defined:

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

The va_arg macro refers to the variable parameter referred to by the current arg_ptr and points the AP pointer to the next variable parameter. Its prototype is:

# Define va_arg (list, mode) (mode *) (List =/
(Char *) (INT) List + (_ builtin_alignof (mode) <= 4? 3: 7 ))&/
(_ Builtin_alignof (mode) <= 4? -4:-8) + sizeof (mode) [-1]

The specific meaning of this macro will be discussed in detail later.

The va_end macro is used to end variable parameter acquisition. It is defined:

# Define va_end (list)

It can be seen that va_end (list) is actually defined as null, and there is no actual corresponding code for code symmetry, which corresponds to va_start. In addition, it may also play the role of "self-annotation" of the Code. The so-called "self-annotation" of code means that the code can annotate itself.

The following example describes how to use the preceding three macros.

2. A simple example

# Include <stdarg. h>
/* Function name: Max
* Function: returns the maximum value of N integers.
* Parameter: num: Number of integers...: num integer
* Return value: the obtained maximum integer.
*/
Int max (INT num ,...)
{
Int M =-0x7fffffff;/* the smallest integer in 32 Systems */
Va_list AP;
Va_start (AP, num );
For (INT I = 0; I <num; I ++)
{
Int T = va_arg (AP, INT );
If (T> m)
{
M = T;
}
}
Va_end (AP );
Return m;
}
/* Main function call Max */
Int main (INT argc, char * argv [])
{
Int n = max (5, 5, 6, 3, 8, 5);/* calculate the maximum value of the five integers */
Cout <N;
Return 0;
}

The variable parameter table pointer AP is defined in function max, and then the first address (assigned to AP) of the parameter table is obtained through va_start (AP, num ), the for loop is used to traverse Variable Parameter tables. This Traversal method is similar to the Traversal method we often see in Data Structure textbooks.

The function Max looks concise and clear, but in fact the implementation of printf is far more complex than this. The max function looks simple because:

(1) The length of the variable parameter table of the Max function is known and passed in through the num parameter;

(2) In the variable parameter table of the Max function, the parameter types are known and all are int type.

The printf function is not so lucky. First, the number of variable parameters in the printf function cannot be obtained easily, and the type of variable parameters is not fixed, must be identified by the format string (determined by % F, % d, % s, etc.), so it involves more complex applications of Variable Parameter tables.

Next we use examples to analyze the advanced applications of Variable Parameter tables.

Advanced Applications

The following program is the drawtext function that shows the format string on the screen written for an embedded system (which has 16 characters in the CPU, its usage is similar to int printf (const char * format ,...) function, but the output target is the LCD screen (LED) of the embedded system ).

//////////////////////////////////////// ///////////////////////////////////////
// Function name: drawtext
// Function Description: Draw text on the display screen
// Parameter description: xpos --- X coordinate position [0 .. 30]
// Ypos --- ordinate position [0 .. 64]
//... Can be displayed together with numbers. You need to set the flag (% d, % L, % x, % s)
//////////////////////////////////////// ///////////////////////////////////////
Extern void drawtext (byte xpos, byte ypos, lpbyte lpstr ,...)
{
Byte lpdata [100]; // Buffer
Byte byindex;
Byte bylen;
DWORD dwtemp;
Word wtemp;
Int I;
Va_list lpparam;

Apsaradb for memset (lpdata, 0,100 );
Bylen = strlen (lpstr );
Byindex = 0;
Va_start (lpparam, lpstr );

For (I = 0; I <bylen; I ++)
{
If (lpstr [I]! = '%') // It is not the start of a format character
{
Lpdata [byindex ++] = lpstr [I];
}
Else
{
Switch (lpstr [I + 1])
{
// Integer
Case 'D ':
Case 'D ':
Wtemp = va_arg (lpparam, INT );
Byindex + = inttostr (lpdata + byindex, (DWORD) wtemp );
I ++;
Break;
// Long integer
Case 'l ':
Case 'l ':
Dwtemp = va_arg (lpparam, long );
Byindex + = inttostr (lpdata + byindex, (DWORD) dwtemp );
I ++;
Break;
// Hexadecimal (long integer)
Case 'X ':
Case 'X ':
Dwtemp = va_arg (lpparam, long );
Byindex + = hextostr (lpdata + byindex, (DWORD) dwtemp );
I ++;
Break;
Default:
Lpdata [byindex ++] = lpstr [I];
Break;
}
}
}
Va_end (lpparam );
Lpdata [byindex] = '/0 ';
Displaystring (xpos, ypos, lpdata, true); // display the string lpdata on the screen
}

In this function, you must identify the input format string (the first address is lpstr) to learn the number of variable parameters and the types of variable parameters. The specific implementation is embodied in the for loop. For example, if it is identified as % d, va_arg (lpparam, INT) is used. If it is known as % L and % x, va_arg (lpparam, long) is used ). After the format string is identified, variable parameters are processed.

At the beginning of the project, we had been struggling to find a good way to mix output strings and numbers. We used the method of displaying numbers and strings separately, and specified coordinates respectively, program organization is damaged. In addition, we feel a headache when we need to manually calculate coordinates for various types of data during hybrid display. The previous functions are:

// Display string
Showstring (byte xpos, byte ypos, lpbyte lpstr)
// Display numbers
Shownum (byte xpos, byte ypos, int num)
// Display numbers in hexadecimal notation
Showhexnum (byte xpos, byte ypos, int num)

In the end, we use the drawtext (byte xpos, byte ypos, lpbyte lpstr,...) function to replace all the original output functions, and the program is simplified. In this way, the boys are so easy to use.

Exploration of Operating Mechanism

In section 2nd, we learned how to use variable parameter tables. I believe that the readers who prefer to leave the root question are unwilling to answer the following questions:

(1) Why Can variable parameters be obtained and operated according to the practice in section 2nd?

(2) What does C/C ++ rely on at the underlying layer to support this syntax? Why can't other languages provide variable parameter tables?

We will explore these questions step by step.

3.1 call mechanism Disassembly

Disassembly is the ultimate good way to study the deep features of syntax. Let's take a look at the disassembly of the main function for max (5, 5, 6, 3, 8, 5) calls in section 2.2:

1. 004010c8 Push 5
2. 004010ca push 8
3. 004010cc Push 3
4. 004010ce push 6
5. 004010d0 Push 5
6. 004010d2 Push 5
7. 004010d4 call @ ILT + 5 (max) (0040100a)

From the disassembly code above, we can see that the C/C ++ function call process is:

Step 1: Import parameters from right to left (1st ~ 6 rows );

Step 2: Call the call command to redirect (line 1 ).

These two steps have profound meanings. They indicate that the default calling method of C/C ++ is based on the caller's management parameter operation in the stack, and the order of the incoming stack is from right to left, this call method is called _ cdecl. The Inbound direction of the x86 system is from high address to low address, so the 1st to N parameters are placed in the stack with increasing addresses. Within the called function, read the stack content to obtain the value of each parameter. Let's disassemble it into the interior of the Max function:

Int max (INT num ,...)
{
1. 00401020 push EBP
2. 00401021 mov EBP, ESP
3. 00401023 sub ESP, 50 h
4. 00401026 push EBX
00401027 push ESI
6. 00401028 push EDI
00401029 Lea EDI, [ebp-50h]
8. 0040102c mov ECx, 14 h
9. 00401031 mov eax, 0 cccccccch
10. 00401036 rep STOs dword ptr [EDI]
Va_list AP;
Int M =-0x7fffffff;/* the smallest integer in 32 Systems */
11. 00401038 mov dword ptr [ebp-8], 80000001 H
Va_start (AP, num );
12. 0040103f Lea eax, [EBP + 0ch]
13. 00401042 mov dword ptr [ebp-4], eax
For (INT I = 0; I <num; I ++)
14. 00401045 mov dword ptr [ebp-0Ch], 0
15. 0040104c jmp max + 37 h (00401057)
16. 0040104e mov ECx, dword ptr [ebp-0Ch]
17. 00401051 add ECx, 1
18. 00401054 mov dword ptr [ebp-0Ch], ECx
19. 00401057 mov edX, dword ptr [ebp-0Ch]
20. 0040105a CMP edX, dword ptr [EBP + 8]
21. 0040105d jge MAX + 61 H (00401081)
{
Int T = va_arg (AP, INT );
22. 0040105f mov eax, dword ptr [ebp-4]
23. 00401062 add eax, 4
24. 00401065 mov dword ptr [ebp-4], eax
25. 00401068 mov ECx, dword ptr [ebp-4]
26. 0040106b mov edX, dword ptr [ecx-4]
27. 0040da-e mov dword ptr [T], EDX
If (T> m)
28. 00401071 mov eax, dword ptr [T]
29. 00401074 CMP eax, dword ptr [ebp-8]
30. 00401077 jle MAX + 5fh (0040366f)
M = T;
31. 00401079 mov ECx, dword ptr [T]
32. 00401_c mov dword ptr [ebp-8], ECx
}
33. 0040366f jmp max + 2EH (0040104e)
Va_end (AP );
34. 00401081 mov dword ptr [ebp-4], 0
Return m;
35. 00401088 mov eax, dword ptr [ebp-8]
}
36. 0040da-b pop EDI
37. 00401_c pop ESI
38. 0040108d pop EBX
39. 0040da-e mov ESP, EBP
40. 00401090 pop EBP
41. 00401091 RET

The analysis of the above disassembly code will be a great pleasure for a real programmer, and it will also benefit a lot for beginners. So be sure to study it with your scalp and never be intimidated!

Line 1 ~ 10. Prepare and save the code in the execution function. Row 3 moves the stack; Row 3 means that the stack space prepared by the Max function for its internal local variables is 50 h bytes; the row 11th indicates that the memory space of Variable N is arranged at the position of 8 Minus the local stack in the function (4 bytes occupied ).

12th ~ Line 13 is critical and corresponds to va_start (AP, num). The addresses of the first variable parameters in the two rows are assigned to the AP pointer. In addition, we can see from row 3 that the num address is EBP + 0ch; from Row 3, we can see that the AP is allocated to the position where the local stack of the function is reduced by 4 (occupying 4 bytes ).

22nd ~ Line 27 is the most critical, corresponding to va_arg (AP, INT ). Among them, 22 ~ Row 24 directs the AP to the next variable parameter (the address interval of the variable parameter is 4 bytes, as can be seen from add eax, 4); 25 ~ The value of the current variable parameter is assigned to the variable t in line 27. This disassembly is very strange. It first moves the variable parameter pointer, and then returns the previous parameter value in the value assignment command to T (from mov edX, dword ptr [ecx-4] statement can be seen ). Visual c ++ has fun. I don't know how to deal with Visual Basic and other students in the same situation?

36th ~ Line 41 restores the site and stack address, and executes the function return operation.

The painful disassembly journey is almost over. After reading this disassembly, we finally figured out the storage location of variable parameters and the way they are read!

2. Special Call conventions

In addition, we need to understand some special conventions about the space occupied by parameters in C/C ++ function calls, because in the _ cdecl call protocol, some variable types are imported into the stack according to the size of other variables.

For example, the memory variable will be automatically extended into a word space, because the inbound operation is for a word.

The actual space occupied by parameter n is (sizeof (n) + sizeof (INT)-1 )&~ (Sizeof (INT)-1), this is the history of Section 2.1 _ intsizeof (v) macro!

In this case, the va_arg (list, mode) macro in the previous section is very clear about what the plane is like. This issue will be analyzed by a reader.

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.