[In-depth analysis of C language] variable parameter list

foreword

If you don't know the function stack frame, it is recommended to supplement the knowledge of the stack frame first. This article involves this part of knowledge.
Function stack frame
This article uses the conclusion of the function stack frame

  • The function passes parameters and pushes the stack from right to left, and pushes the stack.
  • When passing parameters, the function is not called .
  • When a data type smaller than an integer is pushed onto the stack, it will be promoted to an integer

1. Variable parameter list

1. Basic knowledge

Symbol: ... (three decimal points)
Prerequisite: The variable parameter list is used as a function parameter, and there must be at least one actual parameter before it.

Find the function declaration and definition of the maximum value:

int Max(int nums, ...);//可变参数列表的前面至少有一个实参
int Max(int nums, ...)
{
    
    
	va_list arg;
	__crt_va_start(arg, nums);
	int max = __crt_va_arg(arg, int);

	for (int i = 1; i < nums; i++)
	{
    
    
		int ret = __crt_va_arg(arg, int);
		if (max < ret)
		{
    
    
			max = ret;
		}
	}
	__crt_va_end(arg);
	return max;
}

2. Basic use

Four quantities are involved - a type redefinition and three macros

1. A type redefinition

va_list arg;
  • va_list is actually a type redefinition of char *
    as follows:
typedef char* va_list;

2. Three macros

premise

  • Understanding of _INTSIZEOF macro

Simple understanding:
align the type size to 4

Example: _INTSIZEOF(char), the size of the char type is less than 4, and the result is 4.
Example: _INTSIZEOF(int), the size of the int type is 4, and the result is 4.

Implementation principle:
In simple terms, it can be divided into two situations:

  1. It is a multiple of 4 - int, long long int.
    On par with 4:
  • equal to data type size
  1. Less than 4 - char, short.
    Be on par with 4:
  • Equal to (data type size/4+1)*4

Merging the two cases together:

  • Aligned multiples of 4 * 4

Multiples aligned to 4
For the first case:
  equal to the data type size/4
For the second case:
 equal to the data type size/4+1

The problem is to convert: combine the multiples of 4 alignment:
the size of the data type can be regarded
as the part divisible by 4 (denoted as n) + the part not divisible by 4 (denoted as r):
the problem continues to convert: will not The part divisible by 4 is converted into a part divisible by 4.
Since r is greater than or equal to 1 and less than or equal to 3,
the maximum value of r plus 3 can be converted into a part divisible by 4. Therefore,
the size of the data type can be regarded as:
n+r+ 3
therefore: a multiple of alignment to 4 - (n+r+3)/4Because
: n+r is the size of the data type
Therefore: it can be written as: (data type size+3)/4
Therefore: alignment to 4 That is: (data type size+3)/4*4
and because the integer division by 4 and multiplication by 4 in the computer means clearing the last two digits to 0
, so it can be written as: ((data type size+3)>>2)< <2
In this case, we can take advantage of the nature of **&: the same as 1 is 1**,
invert 3 bit by bit , and then logically AND the data type size + 3.
Isn’t 3 here sizeof(int)-1 ? , the size of the data type is not sizeof (data type)
, so it can be written again:(sizeof(data type)+sizeof(int)-1)&(~(sizeof(int)-1))
and because the priority of ~ is higher than &
, it can be written: (sizeof(data type)+sizeof(int )-1)& ~(sizeof(int)-1)
so you can understand what is done in the definition:
insert image description here

1.__crt_va_start

__crt_va_start
//定义: #define __crt_va_start_a(ap, v) ((void)(ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v)))
//_ADDRESSOF(v) (&(v))
//代换可得:(void)(ap=(char *)(&(v))+ _INTSIZEOF(v))
//代入上面找最大值的代码__crt_va_start(arg, nums);
//(void)(arg=(char *)(&(nums))+ _INTSIZEOF(nums))
//也就是将arg赋值为nums地址+nums的数据类型向4的对齐数
//换句话说:arg赋值为可变参数的第一个参数的地址

Function: Find the address of the first parameter of the variable parameter through the penultimate parameter of the variable parameter of the function
Principle: function stack frame
Parameter 1: variable of va_list
Parameter 2: positive first parameter of the variable parameter list

2. __crt_va_arg

Parameter 1: variable of va_list
Parameter 2: data type

#define __crt_va_arg(ap, t)     (*(t*)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))
//这里面的括号有点多拆开来看
//从里往外分析:(ap += _INTSIZEOF(t)) - _INTSIZEOF(t)
//第一个式子:(ap += _INTSIZEOF(t))将ap加等上t的4字节对齐数
//注意:此时改变了ap的值
//第二个式子:- _INTSIZEOF(t),对第一个式子的结果减这个值
//注意:表达式的结果并没有发生变化,但是ap却发生变化了,指向了下一个元素
//第三个式子*(t*),对表达式的结果强转,再解引用访问到t类型大小的的值

Function: Get the value of the variable in the variable parameter, and point to the next variable of the variable parameter.

3.__crt_va_end

Parameter: variable of va_list

//定义:
#define __crt_va_end(ap)        ((void)(ap = (va_list)0))

Function: Set the variable as empty to prevent it from being a wild pointer.

Guess you like

Origin blog.csdn.net/Shun_Hua/article/details/129941416