Parâmetros de funções em C, diversas formas de passá-los

Vimos neste post como é que o compilador passa os parâmetros para uma função. Ele os empilha! Só que o exemplo que dei mostra apenas um parâmetro. E se fossem vários? Você pode assumir que os parâmetros seriam passados na ordem com que foram declarados, por exemplo:

  int min(int a, int b);

  ...
  int x = min(1,2);

Será que o compilador empilhará o valor 1 e depois o valor 2 para só então chamar a função min()? Infelizmente não! Ele fará, por default, o contrário:

  mov eax,2
  push eax              /* Empilha o valor 2 */
  mov eax,1
  push eax              /* Empilha o valor 1 */
  call _min
  add esp,8
  mov dword ptr [x],eax /* EAX contém o valor retornado pela função. */

Além de passar os parâmetros na ordem invertida (e existe um motivo para isso, explico logo, logo), o padrão cdecl (o padrão default do C) também faz com que a função chamada lide com a limpeza da pilha (como expliquei naquele outro post, citado ai em cima). Essa é a convenção da linguagem C. Embora seja uma convenção usada a quase 40 anos, ambientes como o Microsoft Windows não usam esse padrão. As funções da API do Windows usam uma convenção chamada stdcall.

A “chamada padrão” (stdcall) faz exatamente o contrário do que a convenção cdecl faz… stdcall passa os parâmetros na ordem em que são declarados e instrui o compilador para que as funções chamadas limpem a pilha. Se reescrevêssemos a mesma rotina acima com o padrão stdcall, teríamos algo assim:

  /* Declare a função como:

    __stdcall int min(int a, int b);

     A chamada fica do mesmo jeito...
  */
  mov eax,1
  push eax
  mov eax,2
  push eax
  call _min
  mov dword ptr [x],eax  

Parece que ficou melhor e mais intuitivo, não é? Até posso concordar com isso, mas C passa os parâmetros de forma invertida porque, ao contrário de outras linguagens, permite a passagem de um número variável de parâmetros. Eis um exemplo de declaração de função que aceita diversos parâmetros:

  int sscanf(char *s, char *fmt, ...);

A função sscanf(), definida no header stdio.h, aceita mais de 2 parâmetros e o primeiro tem que ser um ponteiro para uma string contendo a formatação. Por causa da convenção cdecl é que você pode fazer algo assim:

  if (sscanf(s, "%d,%d", &a, &b) == 2)
  {
    printf("Os valores são: ");
    printf("a=%d, b=%d.\n", a, b);
  }

Como você pode ver, não é só o sscanf, mas o printf também aceita multiplos parâmetros…

Curiosidade: Na Win32 API existe apenas uma função declarada com a convenção cdecl: wsprintf! Não é à toa… A convenção stdcall não aceita multiplos parâmetros!

Existem outras convenções. A convenção fastcall, por exemplo, tenta passar os dois primeiros parâmetros através dos registradores ECX e EDX, ao invés de usar a pilha (os demais parâmetros vão pra pilha…). Dê uma olhada no 5 sessão 27 do manual do GCC (aqui) para ver as outras convenções.

Anúncios

Um comentário sobre “Parâmetros de funções em C, diversas formas de passá-los

Deixe um comentário

Faça o login usando um destes métodos para comentar:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s