Diretório de artigos
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:
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:
é 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
O diagrama esquemático é o seguinte:
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.
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.
Diagrama esquemático:
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.
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.
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).
Diagrama esquemático:
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:
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:
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:
Resumir
O texto acima é meu entendimento das listas de parâmetros variáveis. obrigado por assistir! ! !