Obtendo “tipos conflitantes para function” em C, por quê?

Eu estou usando o código abaixo:

char dest[5]; char src[5] = "test"; printf("String: %s\n", do_something(dest, src)); char *do_something(char *dest, const char *src) { return dest; } 

A implementação do do_something não é importante aqui. Quando tento compilar o acima, recebo estas duas exceções:

erro: tipos conflitantes para ‘do_something’ (na chamada printf)
erro: declaração implícita anterior de ‘do_something’ estava aqui (na linha do protótipo)

Por quê?

Você está tentando chamar do_something antes de declará-lo. Você precisa adicionar um protótipo de function antes de sua linha printf:

 char* do_something(char*, const char*); 

Ou você precisa mover a definição de function acima da linha printf. Você não pode usar uma function antes de ser declarada.

Na linguagem C “clássica” (C89 / 90) quando você chama uma function não declarada, C assume que retorna um int e também tenta derivar os tipos de seus parâmetros a partir dos tipos dos argumentos reais (não, não assume que não tem parâmetros, como alguém sugeriu antes).

Em seu exemplo específico, o compilador examinaria a do_something(dest, src) e derivaria implicitamente uma declaração para do_something . Este último seria o seguinte

 int do_something(char *, char *) 

No entanto, mais tarde, no código, você declara explicitamente do_something como

 char *do_something(char *, const char *) 

Como você pode ver, essas declarações são diferentes umas das outras. Isto é o que o compilador não gosta.

Você tem que declarar a function antes de usá-la. Se o nome da function aparecer antes de sua declaração, o compilador C seguirá certas regras e fará a declaração em si. Se estiver errado, você receberá esse erro.

Você tem duas opções: (1) defini-lo antes de usá-lo ou (2) usar a declaração de encaminhamento sem implementação. Por exemplo:

 char *do_something(char *dest, const char *src); 

Observe o ponto e vírgula no final.

Você não o declarou antes de usá-lo.

Você precisa de algo como

 char *do_something(char *, const char *); 

antes do printf.

Exemplo:

 #include  char *do_something(char *, const char *); char dest[5]; char src[5] = "test"; int main () { printf("String: %s\n", do_something(dest, src)); return 0; } char *do_something(char *dest, const char *src) { return dest; } 

Alternativamente, você pode colocar toda a function do_something antes do printf.

Capítulo 3:

 K&R #3 Thou shalt always prototype your functions or else the C compiler will extract vengence. 

http://www.ee.ryerson.ca:8080/~elf/hack/God.vs.K+R.html

Assista novamente:

 char dest[5]; char src[5] = "test"; printf("String: %s\n", do_something(dest, src)); 

Concentre-se nesta linha:

 printf("String: %s\n", do_something(dest, src)); 

Você pode ver claramente que a function do_something não está declarada!

Se você olhar um pouco mais,

 printf("String: %s\n", do_something(dest, src)); char *do_something(char *dest, const char *src) { return dest; } 

você verá que você declara a function depois de usá-la.

Você precisará modificar esta parte com este código:

 char *do_something(char *dest, const char *src) { return dest; } printf("String: %s\n", do_something(dest, src)); 

Felicidades 😉

Declaração de Declaração de Função AC

Em C, declarações de function não funcionam como em outras linguagens: O próprio compilador C não pesquisa para trás e para frente no arquivo para encontrar a declaração da function do lugar que você a chama, e ela não verifica o arquivo várias vezes para descobrir os relacionamentos: O compilador só procura no arquivo exatamente uma vez , de cima para baixo. Conectar chamadas de function a declarações de function é parte do trabalho do vinculador e só é feito depois que o arquivo é compilado em instruções de assembly bruta.

Isso significa que, à medida que o compilador percorre o arquivo, a primeira vez que o compilador encontra o nome de uma function, uma de duas coisas tem que ser o caso: Ele está vendo a própria declaração de function, caso em que o compilador sabe Exatamente qual é a function e quais tipos ela toma como argumentos e quais tipos ela retorna – ou se é uma chamada para a function, e o compilador tem que adivinhar como a function será eventualmente declarada.

(Há uma terceira opção, onde o nome é usado em um protótipo de function, mas vamos ignorar isso por enquanto, já que se você está vendo esse problema em primeiro lugar, provavelmente não está usando protótipos.)

Lição de História

Nos primórdios de C, o fato de que o compilador tinha que adivinhar tipos não era realmente um problema: todos os tipos eram mais ou menos os mesmos – praticamente tudo era um int ou um ponteiro, e eles eram o mesmo tamanho. (Na verdade, em B, a linguagem que precedeu C, não havia nenhum tipo; tudo era apenas um int ou pointer e seu tipo era determinado unicamente pelo modo como você o usava!) Assim, o compilador poderia adivinhar com segurança o comportamento de qualquer function apenas com base no número de parâmetros que foram passados: Se você passou dois parâmetros, o compilador iria empurrar duas coisas para a pilha de chamadas e, presumivelmente, o callee teria dois argumentos declarados, e que todos se alinham. Se você passasse apenas um parâmetro, mas a function esperasse dois, ele ainda funcionaria e o segundo argumento seria apenas ignorado / lixo. Se você passasse três parâmetros e a function esperasse dois, também funcionaria, e o terceiro parâmetro seria ignorado e pisado pelas variables ​​locais da function. (Algum código C antigo ainda espera que essas regras de argumentos incompatíveis funcionem também.)

Mas ter o compilador permitindo que você passe qualquer coisa para qualquer coisa não é realmente uma boa maneira de projetar uma linguagem de programação. Funcionou bem nos primeiros dias, porque os primeiros programadores de C eram na maioria magos, e eles sabiam que não passavam o tipo errado para as funções, e mesmo que entendessem os tipos errados, sempre havia ferramentas como lint que poderiam fazer mais dobras duplas. Verificação do seu código C e avisá-lo sobre essas coisas.

Avancemos para hoje e não estamos no mesmo barco. C cresceu, e muitas pessoas estão programando nele que não são assistentes, e para acomodá-los (e para acomodar todos os outros que usavam regularmente lint qualquer maneira), os compiladores assumiram muitas das habilidades que antes eram parte de lint – especialmente a parte em que eles verificam seu código para garantir que ele é seguro para o tipo. Os primeiros compiladores C permitiriam que você escrevesse int foo = "hello"; e apenas atribuiria alegremente o ponteiro ao número inteiro, e caberia a você certificar-se de que não estava fazendo nada estúpido. Compiladores C modernos queixam-se em voz alta quando você erra seus tipos, e isso é uma coisa boa.

Digite Conflitos

Então, o que tudo isso tem a ver com o misterioso erro de tipo conflitante na linha da declaração de function? Como eu disse acima, os compiladores C ainda precisam saber ou adivinhar o que um nome significa na primeira vez que eles verem esse nome enquanto eles examinam o arquivo: Eles podem saber o que significa se for uma declaração de function real (ou function “protótipo”, mais sobre isso em breve), mas se é apenas uma chamada para a function, eles têm que adivinhar . E, infelizmente, a suposição é muitas vezes errada.

Quando o compilador viu sua chamada para do_something() , ele observou como foi invocado e concluiu que do_something() seria declarado assim:

 int do_something(char arg1[], char arg2[]) { ... } 

Por que concluiu isso? Porque é assim que você chama isso! (Alguns compiladores C podem concluir que foi int do_something(int arg1, int arg2) , ou simplesmente int do_something(...) , ambos os quais são ainda mais distantes do que você deseja, mas o ponto importante é que, independentemente de como o compilador adivinha os tipos, adivinha-os diferentemente do que sua function real usa.)

Mais tarde, à medida que o compilador faz a varredura no arquivo, ele vê sua declaração real de char *do_something(char *, char *) . Essa declaração de function não está nem perto da declaração que o compilador adivinhou, o que significa que a linha em que o compilador compilou a chamada foi compilada incorretamente e o programa simplesmente não funcionará. Por isso, imprime corretamente um erro informando que seu código não funcionará como está escrito.

Você pode estar se perguntando: “Por que isso supõe que estou retornando um int ?” Bem, isso pressupõe esse tipo porque não há nenhuma informação em contrário: printf() pode aceitar qualquer tipo em seus argumentos variables, então sem uma resposta melhor, int é tão bom quanto qualquer outro. (Muitos compiladores C antigos sempre assumiam int para cada tipo não especificado, e assumiam que você queria dizer ... para os argumentos para cada function declarada f() – não void – é por isso que muitos padrões de código modernos recomendam sempre colocar os argumentos em branco se realmente não deveria haver nenhum.)

O conserto

Existem duas correções comuns para o erro de declaração de function.

A primeira solução, que é recomendada por muitas outras respostas aqui, é colocar um protótipo no código-fonte acima do local onde a function é chamada pela primeira vez. Um protótipo se parece com a declaração da function, mas tem um ponto e vírgula onde o corpo deve estar:

 char *do_something(char *dest, const char *src); 

Colocando o protótipo em primeiro lugar, o compilador sabe como será a function, então não precisa adivinhar. Por convenção, os programadores costumam colocar protótipos no topo do arquivo, logo abaixo das instruções #include , para garantir que eles sempre serão definidos antes de qualquer uso potencial deles.

A outra solução, que também aparece em algum código do mundo real, é simplesmente reordenar suas funções para que as declarações de function sejam sempre antes de qualquer coisa que as chame! Você poderia mover toda a function char *do_something(char *dest, const char *src) { ... } acima da primeira chamada, e o compilador saberia exatamente como é a function e não teria que adivinhar .

Na prática, a maioria das pessoas usa protótipos de function, porque você também pode usar protótipos de funções e movê-los para os arquivos de header ( .h ) para que o código em outros arquivos .c possa chamar essas funções. Mas a solução funciona e muitas bases de código usam ambas.

C99 e C11

É útil observar que as regras são ligeiramente diferentes nas versões mais recentes do padrão C. Nas versões anteriores (C89 e K & R), o compilador realmente adivinharia os tipos no tempo de chamada de function (e os compiladores da era K & R nem sempre avisavam se estavam errados). C99 e C11 exigem que a declaração / protótipo da function preceda a primeira chamada, e é um erro, se não ocorrer. Mas muitos compiladores C modernos – principalmente para compatibilidade retroativa com código anterior – só avisam sobre um protótipo ausente e não o consideram um erro.

Quando você não dá um protótipo para a function antes de usá-la, C assume que recebe qualquer número de parâmetros e retorna um int. Então, quando você tenta usar do_something pela primeira vez, esse é o tipo de function que o compilador está procurando. Isso deve produzir um aviso sobre uma “declaração de function implícita”.

Então, no seu caso, quando você realmente declara a function mais tarde, C não permite a sobrecarga de function, então fica irritada porque você declarou duas funções com protótipos diferentes, mas com o mesmo nome.

Resposta curta: declare a function antes de tentar usá-la.

Certifique-se de que os tipos na declaração de function sejam declarados primeiro.

/* start of the header file */
.
.
.
struct intr_frame{...}; //must be first!
.
.
.
void kill (struct intr_frame *);
.
.
.
/* end of the header file */

Isso geralmente acontece quando você modifica a definição da function CA e se esquece de atualizar a definição de header correspondente.