Sintaxe da function C, tipos de parâmetros declarados após a lista de parâmetros

Eu sou relativamente novo em C. Eu me deparei com uma forma de syntax de function que eu nunca vi antes, onde os tipos de parâmetro são definidos após essa lista de parâmetros. Alguém pode me explicar como é diferente da syntax típica da function C?

Exemplo:

int main (argc, argv) int argc; char *argv[]; { return(0); } 

Essa é a syntax antiga das listas de parâmetros, que ainda é suportada. Em K & R C, você também poderia deixar as declarações de tipo e elas seriam padrão para int. ou seja

 main(argc, argv) char *argv[]; { return 0; } 

seria a mesma function.

O que também é interessante é a diferença entre funções da convenção de chamada e funções sem um protótipo. Considere uma definição de estilo antigo:

 void f(a) float a; { /* ... */ } 

Nesse caso, a convenção de chamada é que todos os argumentos são promovidos antes de serem passados ​​para a function. Portanto, se f receber um double mas o parâmetro tiver type float (que é perfeitamente válido), o compilador deve emitir um código que converta o double em float antes de executar o corpo da function.

Se você include um protótipo, o compilador não fará mais essas promoções automáticas e todos os dados transmitidos serão convertidos para os tipos de parâmetros do protótipo como se fossem atribuídos por atribuição. Portanto, o seguinte não é legal e resulta em comportamento indefinido:

 void f(float a); void f(a) float a; { } 

Nesse caso, a definição da function converteria o parâmetro enviado de double (o formulário promovido) para float porque a definição é de estilo antigo. Mas o parâmetro foi submetido como um float, porque a function tem um protótipo. Suas opções de resolver as contradições são as seguintes:

 // option 1 void f(double a); void f(a) float a; { } // option 2 // this declaration can be put in a header, but is redundant in this case, // since the definition exposes a prototype already if both appear in a // translation unit prior to the call. void f(float a); void f(float a) { } 

Opção 2 deve ser preferida se você tiver a escolha, pois se livrar da definição de estilo antigo na frente. Se tais tipos de funções contraditórias para uma function aparecerem na mesma unidade de tradução, o compilador geralmente lhe dirá (mas não é obrigatório). Se tais contradições aparecerem em várias unidades de tradução, o erro possivelmente passará despercebido e poderá resultar em erros difíceis de prever. É melhor evitar essas definições de estilo antigo.

Este é o estilo K & R do chamador ou a declaração de estilo antigo .

Note que esta declaração é significativamente diferente da declaração moderna. A declaração K & R não introduz um protótipo para a function, o que significa que não expõe os tipos de parâmetros ao código externo.

Enquanto a antiga syntax para definição de function ainda funciona (com avisos, se você perguntar ao seu compilador), usá-los não fornece protótipos de function.
Sem protótipos de funções, o compilador não verificará se as funções são chamadas corretamente.

 #include  int foo(c) int c; { return printf("%d\n", c); } int bar(x) double x; { return printf("%f\n", x); } int main(void) { foo(42); /* ok */ bar(42); /* oops ... 42 here is an `int`, but `bar()` "expects" a `double` */ return 0; } 

Quando o programa é executado, a saída da minha máquina é

 $ gcc proto.c $ gcc -Wstrict-prototypes proto.c proto.c:4: warning: function declaration isn't a prototype proto.c:10: warning: function declaration isn't a prototype $ ./a.out 42 0.000000 

Não há diferença, é apenas que essa é a syntax antiga para declarações de function em C – ela foi usada antes do ANSI. Nunca escreva tal código a menos que você planeje dar a seus amigos dos anos 80 . Além disso, nunca dependa de suposições de tipo implícitas (como outra resposta parece sugerir)

É apenas o mesmo, mas a moda antiga. Você provavelmente descobriu que é algum código legado antigo.