C language: variable parameter list

insert image description here


1. What is a variable parameter list

The variable parameter list sounds unfamiliar, but in fact we often use it in the printf and scanf functions.
As follows:
insert image description here
insert image description here
Among them... is the representation of the variable parameter list in the formal parameter.

Second, the use of variable parameter list

The use of the variable parameter list is related to the use of four macros, which are va_list , va_start , va_arg , va_end .

va_list is a (char*) renamed type that defines variables that can access variable parameters.
va_start(ap, v) ap is a defined variable parameter variable, v is the first parameter name before the variable parameter in the formal parameter, and its function is to make ap point to the variable parameter part.
va_arg(ap, t) ap is the defined variable parameter variable, t is the type of the variable parameter, and accesses the data in the variable parameter list according to the type.
va_end(ap) ap is a defined variable parameter variable, make the ap variable empty, and use it as the end.

In the second time, I used the problem of finding the maximum value among 5 numbers to demonstrate the use of variable parameter lists.
code show as below:

//VS2022中使用
int FindMax(int num, ...)
{
    
    
	va_list arg;
	__crt_va_start(arg, num);
	int max = __crt_va_arg(arg, int);

	for (int i = 0; i < num-1; i++)
	{
    
    
		int cur = __crt_va_arg(arg, int);
		if (max < cur)
		{
    
    
			max = cur;
		}
	}
	__crt_va_end(arg);

	return max;
}

int main()
{
    
    
	int max = FindMax(5, 10, 20, 30, 40, 50);
	
	printf("%d\n", max);
	return 0;
}

Third, understand the variable parameter list

The implementation of its macro is as follows:
insert image description here
it is very easy to understand for va_list and __crt_va_end. va_list is the renaming of (char*), __crt_va_end is to set the ap variable parameter variable to 0, and it is relatively difficult to understand __crt_va_start_a and __crt_va_arg.

1. Understand the role of __crt_va_start_a and __crt_va_arg from the perspective of assembly

We all know that the instantiation of function parameters is performed directly on the top of the stack, and it is performed from right to left.
As follows:
The thick line frame part is the part that will be pushed on the stack, 32h, 28h, 1Eh... are the hexadecimal numbers of 50, 40, 30 respectively
insert image description here

insert image description here
The schematic diagram is as follows:

insert image description here
That is to say, the relative positions of the temporary copies of the formal parameters of the function are fixed. Then as shown in the figure, when I know the address of num, then I can access the data in the variable parameter list.

  • __crt_va_start_a

0x0113fc10 is the current position of esp, 0x0113fc08 is the position of ebp-8, which is the space where the variable arg is located.
0x0113fc1c is the position of ebp+0ch, that is, the position of the first element (10) in the variable parameter list data.

At this time __crt_va_start(arg, num) is not executed.
insert image description here

At this point __crt_va_start(arg, num) is executed.
The address stored in 0x0113fc08 (arg variable) at this time is the address of the first element in the variable parameter list.

insert image description here
Schematic diagram:
insert image description here
insert image description here
as shown in the figure, __crt_va_start_a is equivalent to (char*)(&(num))+_INTSIZEOF(int), which is to find the first element (10) of the variable parameter list by adding _INTSIZEOF(v) to the address of num The address of is stored in the variable arg.

  • __crt_va_arg

At this time __crt_va_arg is not implemented.
arg stores the address of the first element (10) in the variable parameter list.

insert image description here
At this point __crt_va_arg is executed.
The space pointed to by the address in arg is the address of the second element (20) of the variable parameter list, and the content of the first element in the variable parameter list is placed in the max variable.
So how?
First put the address in arg into eax, then let eax increase by 4, and then put it back into arg. At this time, the address in arg is the address of the second element (20) of the variable parameter list.
Then read the address in arg and put it in ecx, and then look for the address at ecx-4, which is the address of the first element of the variable parameter list, put it in edx, and then put the address in edx to the space pointed to The content is placed at ebp-14h, that is, the first element (10) of the variable parameter list is placed in the space of max.
insert image description here

In the figure above, the 00D117BB, 00D117BE, and 00D117C1 instructions complete the operation (ap+=_INTSIZEOF(t)) [the first is the expression X], and the 00D117C4 and 00DC117C7 complete the X-_INTSIZEOF(t) operation.
insert image description here
Schematic diagram:

insert image description here
At this point, we have a basic understanding of **__crt_va_start_a** and __crt_va_arg , so what is _INTSIZEOF(n)?

2. Understanding of _INTSIZEOF(n)

_INTSIZEOF(n) is actually seeking the alignment number of 4. So why do you want to do it, is it not good to directly add the size of the variable parameter type?

  • Why?
    Let's look at the following code first:
int FindMax(int num, ...)
{
    
    
	va_list arg;
	__crt_va_start(arg, num);
	int max = __crt_va_arg(arg, int);

	for (int i = 0; i < num - 1; i++)
	{
    
    
		int cur = __crt_va_arg(arg, int);
		if (max < cur)
		{
    
    
			max = cur;
		}
	}
	__crt_va_end(arg);

	return max;
}

int main()
{
    
    
	//int max = FindMax(5, 10, 20, 30, 40, 50);

	//对于短整型而言,在形参实例化中,会发生整形提升
	char e = 'e';
	char d = 'd';
	char c = 'c';
	char b = 'b';
	char a = 'a';

	int max = FindMax(5, a, b, c, d, e);


	printf("%d\n", max);
	return 0;
}

Note that in the above code, the variable parameter list I pass is char type, and when I use __crt_va_arg, I use int type. So can I get the right answer?
As follows:
insert image description here
The ASCII value corresponding to 101 is e.

In fact, when we pass a short integer, the formal parameter will undergo plastic promotion.
as follows:
insert image description here
insert image description here

In fact, movsx will convert the short integer parameter to plastic improvement. In this case, if we add or subtract the size of the type in __crt_va_arg, it will cause an error, so _INTSIZEOF(n) is required.

  • How to realize
    Firstly, the alignment number of 4 can be understood as the smallest integer multiple of 4, so how to solve it?
    As shown in the picture:
    insert image description here

Summarize

The above is my understanding of variable parameter lists. thanks for watching! ! !
insert image description here

Guess you like

Origin blog.csdn.net/li209779/article/details/130914923