Linguagem C: lista de parâmetros variáveis

insira a descrição da imagem aqui


1. O que é uma lista de parâmetros variáveis

A lista de parâmetros variáveis ​​não parece familiar, mas na verdade a usamos com frequência nas funções printf e scanf.
Como segue:
insira a descrição da imagem aqui
insira a descrição da imagem aqui
Entre eles... está a representação da lista de parâmetros variáveis ​​no parâmetro formal.

Em segundo lugar, o uso da lista de parâmetros variáveis

O uso da lista de parâmetros variáveis ​​está relacionado ao uso de quatro macros, que são va_list , va_start , va_arg , va_end .

va_list é um tipo renomeado (char*) que define variáveis ​​que podem acessar parâmetros variáveis.
va_start(ap, v) ap é uma variável de parâmetro variável definida, v é o primeiro nome do parâmetro antes do parâmetro variável no parâmetro formal, e sua função é fazer com que ap aponte para a parte do parâmetro variável.
va_arg(ap, t) ap é a variável de parâmetro variável definida, t é o tipo do parâmetro da variável e acessa os dados na lista de parâmetros da variável de acordo com o tipo.
va_end(ap) ap é uma variável de parâmetro variável definida, deixe a variável ap vazia e use-a como final.

Na segunda vez, utilizei o problema de encontrar o valor máximo entre 5 números para demonstrar o uso de listas de parâmetros variáveis.
código mostrado abaixo:

//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;
}

Terceiro, entenda a lista de parâmetros variáveis

A implementação de sua macro é a seguinte:
insira a descrição da imagem aqui
é muito fácil de entender para va_list e __crt_va_end. va_list é a renomeação de (char *), __crt_va_end é definir a variável de parâmetro da variável ap como 0 e é relativamente difícil de entender __crt_va_start_a e __crt_va_arg.

1. Compreenda a função de __crt_va_start_a e __crt_va_arg da perspectiva da montagem

Todos sabemos que a instanciação dos parâmetros da função é realizada diretamente no topo da pilha e da direita para a esquerda.
Da seguinte forma:
A parte do quadro de linha grossa é a parte que será colocada na pilha, 32h, 28h, 1Eh... são os números hexadecimais de 50, 40, 30 respectivamente
insira a descrição da imagem aqui

insira a descrição da imagem aqui
O diagrama esquemático é o seguinte:

insira a descrição da imagem aqui
Ou seja, as posições relativas das cópias temporárias dos parâmetros formais da função são fixas. Então, conforme mostrado na figura, quando eu souber o endereço de num, poderei acessar os dados na lista de parâmetros variáveis.

  • __crt_va_start_a

0x0113fc10 é a posição atual de esp, 0x0113fc08 é a posição de ebp-8, que é o espaço onde a variável arg está localizada.
0x0113fc1c é a posição de ebp+0ch, ou seja, a posição do primeiro elemento (10) nos dados da lista de parâmetros variáveis.

Neste momento __crt_va_start(arg, num) não é executado.
insira a descrição da imagem aqui

Neste ponto __crt_va_start(arg, num) é executado.
O endereço armazenado em 0x0113fc08 (variável arg) neste momento é o endereço do primeiro elemento na lista de parâmetros da variável.

insira a descrição da imagem aqui
Diagrama esquemático:
insira a descrição da imagem aqui
insira a descrição da imagem aqui
conforme mostrado na figura, __crt_va_start_a é equivalente a (char*)(&(num))+_INTSIZEOF(int), que é encontrar o primeiro elemento (10) da lista de parâmetros variáveis ​​​​adicionando _INTSIZEOF(v) para o endereço de num O endereço de é armazenado na variável arg.

  • __crt_va_arg

Neste momento, __crt_va_arg não está implementado.
arg armazena o endereço do primeiro elemento (10) na lista de parâmetros variáveis.

insira a descrição da imagem aqui
Neste ponto __crt_va_arg é executado.
O espaço apontado pelo endereço em arg é o endereço do segundo elemento (20) da lista de parâmetros variáveis, e o conteúdo do primeiro elemento na lista de parâmetros variáveis ​​é colocado na variável max.
Então, como?
Primeiro coloque o endereço em arg em eax, depois deixe eax aumentar em 4 e, em seguida, coloque-o novamente em arg. Neste momento, o endereço em arg é o endereço do segundo elemento (20) da lista de parâmetros variáveis.
Em seguida, leia o endereço em arg e coloque-o em ecx, e a seguir procure o endereço em ecx-4, que é o endereço do primeiro elemento da lista de parâmetros variáveis, coloque-o em edx e depois coloque o endereço em edx para o espaço apontado para O conteúdo é colocado em ebp-14h, ou seja, o primeiro elemento (10) da lista de parâmetros variáveis ​​​​é colocado no espaço de max.
insira a descrição da imagem aqui

Na figura acima, as instruções 00D117BB, 00D117BE e 00D117C1 completam a operação de (ap+=_INTSIZEOF(t)) [a primeira é a expressão X], e as instruções 00D117C4 e 00DC117C7 completam a operação de X-_INTSIZEOF(t).
insira a descrição da imagem aqui
Diagrama esquemático:

insira a descrição da imagem aqui
Neste ponto, temos um entendimento básico de **__crt_va_start_a** e __crt_va_arg , então o que é _INTSIZEOF(n)?

2. Compreensão de _INTSIZEOF(n)

_INTSIZEOF(n) está na verdade buscando o número de alinhamento 4. Então, por que você quer fazer isso, não é bom adicionar diretamente o tamanho do tipo de parâmetro variável?

  • Por que?
    Vejamos primeiro o seguinte código:
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;
}

Observe que no código acima, a lista de parâmetros de variáveis ​​​​que passo é do tipo char e, quando uso __crt_va_arg, uso o tipo int. Então, posso obter a resposta certa?
Como segue:
insira a descrição da imagem aqui
O valor ASCII correspondente a 101 é e.

Na verdade, quando passamos um número inteiro curto, o parâmetro formal sofrerá promoção plástica.
do seguinte modo:
insira a descrição da imagem aqui
insira a descrição da imagem aqui

Na verdade, movsx converterá o parâmetro inteiro curto em melhoria plástica. Neste caso, se adicionarmos ou subtrairmos o tamanho do tipo em __crt_va_arg, causará um erro, portanto _INTSIZEOF(n) é necessário.

  • Como realizar
    Em primeiro lugar, o número de alinhamento 4 pode ser entendido como o menor múltiplo inteiro de 4, então como resolvê-lo?
    Como mostrado na imagem:
    insira a descrição da imagem aqui

Resumir

O texto acima é meu entendimento das listas de parâmetros variáveis. obrigado por assistir! ! !
insira a descrição da imagem aqui

Acho que você gosta

Origin blog.csdn.net/li209779/article/details/130914923
Recomendado
Clasificación