Получение "конфликтующих типов для функции" в C, почему? - PullRequest
65 голосов
/ 11 октября 2009

Я использую следующий код:

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

Реализация do_something здесь не важна. Когда я пытаюсь скомпилировать вышеизложенное, я получаю два исключения:

ошибка: конфликтующие типы для do_something (при вызове printf)
ошибка: предыдущее неявное объявление do_something было здесь (в строке прототипа)

Почему?

Ответы [ 10 ]

116 голосов
/ 11 октября 2009

Вы пытаетесь вызвать do_something, прежде чем объявить это. Вам нужно добавить прототип функции перед строкой printf:

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

Или вам нужно переместить определение функции над строкой printf. Вы не можете использовать функцию до того, как она будет объявлена.

22 голосов
/ 11 октября 2009

В «классическом» языке C (C89 / 90), когда вы вызываете необъявленную функцию, C предполагает, что она возвращает int, а также пытается извлечь типы своих параметров из типов фактических аргументов (нет, он не предполагает, что у него нет параметров, как кто-то предлагал ранее).

В вашем конкретном примере компилятор будет смотреть на вызов do_something(dest, src) и неявно выводить объявление для do_something. Последний будет выглядеть следующим образом

int do_something(char *, char *)

Однако позже в коде вы явно объявляете do_something как

char *do_something(char *, const char *)

Как видите, эти объявления отличаются друг от друга. Это то, что компилятору не нравится.

7 голосов
/ 11 октября 2009

Вы не заявили об этом до того, как использовали его.

Вам нужно что-то вроде

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

до печати. ​​

Пример:

#include <stdio.h>
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;
}

В качестве альтернативы, вы можете поставить всю функцию do_something перед printf.

6 голосов
/ 11 октября 2009

Вы должны объявить функцию, прежде чем использовать ее. Если имя функции появляется перед ее объявлением, компилятор C будет следовать определенным правилам и сам сделает объявление. Если это не так, вы получите эту ошибку.

У вас есть два варианта: (1) определить его перед использованием или (2) использовать предварительное объявление без реализации. Например:

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

Обратите внимание на точку с запятой в конце.

5 голосов
/ 06 апреля 2017

A C Функциональная декларация Backgrounder

В C объявления функций не работают так же, как в других языках: сам компилятор C не выполняет поиск назад и вперед в файле, чтобы найти объявление функции из того места, где вы ее вызываете, и не работает сканируйте файл несколько раз, чтобы выяснить отношения: либо компилятор сканирует только вперед в файле ровно один раз сверху вниз. Подключение вызовов функций к объявлениям функций является частью работы компоновщика и выполняется только после , когда файл компилируется в необработанные инструкции по сборке.

Это означает, что, когда компилятор просматривает файл вперед, в самый первый раз, когда компилятор встречает имя функции, должна быть одна из двух вещей: он либо видит само объявление функции, и в этом случае компилятор точно знает, что это за функция и какие типы она принимает в качестве аргументов и какие типы она возвращает - или это вызов функции, и компилятор должен угадать как функция в конечном итоге будет объявлена.

(есть третий вариант, когда имя используется в прототипе функции, но пока мы его игнорируем, поскольку, если вы видите эту проблему в первую очередь, вы, вероятно, не используете прототипы.)

Урок истории

В первые дни C тот факт, что компилятору приходилось угадывать типы, на самом деле не был проблемой: все типы были более или менее одинаковыми - почти все были либо int, либо указателем, и они были одинакового размера. (На самом деле, в B, языке, предшествующем C, не было вообще никаких типов; все было просто int или указателем, а его тип определялся исключительно тем, как вы его использовали!) Таким образом, компилятор мог безопасно угадывать поведение любого Функция основана на количестве переданных параметров: если вы передадите два параметра, компилятор поместит две вещи в стек вызовов, и, предположительно, вызываемый объект объявит два аргумента, и все это выстроится в линию. Если вы передаете только один параметр, но функция ожидает два, она все равно будет работать, а второй аргумент будет просто проигнорирован / garbage. Если вы передали три параметра, а функция ожидала двух, то она все равно работала бы, и третий параметр игнорировался и растекался локальными переменными функции. (Некоторый старый код на C все еще ожидает, что эти правила несоответствующих аргументов тоже будут работать.)

Но наличие компилятора, позволяющего вам передавать что-либо чему-либо, на самом деле не является хорошим способом разработки языка программирования. В первые дни это работало хорошо, потому что ранние программисты на С были в основном волшебниками, и они знали, что нельзя передавать неправильный тип функциям, и даже если они действительно ошибались в типах, всегда были такие инструменты, как lint, которые могли бы работать глубже перепроверьте ваш код C и предупредите вас о таких вещах.

Перенесемся в сегодняшний день, и мы не совсем в одной лодке. C вырос, и в нем программируют многие люди, которые не являются волшебниками, и, чтобы приспособиться к ним (и к тому, чтобы приспособиться ко всем, кто регулярно использует lint), компиляторы взяли на себя многие из способностей, которые были ранее часть lint - особенно часть, где они проверяют ваш код, чтобы убедиться, что он безопасен для типов Ранние компиляторы Си позволяли вам писать int foo = "hello";, и он просто беспечно назначал указатель на целое число, и вы должны были убедиться, что вы не делаете ничего глупого. Современные компиляторы Си громко жалуются на неправильные типы, и это хорошо.

Типовые конфликты

Так как же все это связано с загадочной ошибкой конфликтующего типа в строке объявления функции? Как я уже говорил выше, компиляторы C все еще должны либо знать , либо догадываться , что означает имя, когда они впервые видят это имя, когда сканируют файл вперед: они могут знать, что это значит, если это само объявление функции (или функция "прототип", подробнее об этом в ближайшее время), но если это просто вызов функции, они должны предположить . И, к сожалению, догадка часто неверна.

Когда компилятор увидел ваш вызов do_something(), он посмотрел, как он вызывался, и пришел к выводу, что do_something() в конечном итоге будет объявлен так:

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

Почему это так? Потому что именно так вы назвали это! (Некоторые компиляторы C могут прийти к выводу, что это был int do_something(int arg1, int arg2) или просто int do_something(...), оба из которых даже на дальше от того, что вы хотите, но важный момент заключается в том, что независимо от того, как компилятор угадывает типы он угадывает их не так, как использует ваша настоящая функция.)

Позже, когда компилятор просматривает файл вперед, он видит ваше фактическое объявление char *do_something(char *, char *). Это объявление функции даже не близко к объявлению, которое угадал компилятор, что означает, что строка, в которой компилятор скомпилировал вызов, была скомпилирована неправильно, и программа просто не будет работать. Поэтому он правильно выводит ошибку, сообщающую, что ваш код не будет работать так, как написано.

Вы можете спросить: "Почему это предполагает, что я возвращаю int?" Ну, он предполагает этот тип, потому что нет никакой информации об обратном: printf() может принимать любой тип в своих переменных-аргументах, поэтому без лучшего ответа int является таким же хорошим предположением, как и любое другое. (Многие ранние компиляторы C всегда предполагали int для каждого неопределенного типа и предполагали, что вы имели в виду ... для аргументов для каждой объявленной функции f() - не void - поэтому многие современные стандарты кода рекомендуют всегда ставить void для аргументов, если на самом деле не должно быть никаких.)

Исправление

Существует два распространенных исправления ошибки объявления функции.

Первое решение, которое рекомендуется многими другими ответами здесь, состоит в том, чтобы поместить прототип в исходный код над местом, где функция сначала вызывается. Прототип выглядит так же, как объявление функции, но имеет точку с запятой, где тело должно быть:

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

Если поместить прототип первым, то компилятор знает , как будет выглядеть функция в конечном итоге, поэтому не нужно угадывать. По соглашению, программисты часто помещают прототипы в верхнюю часть файла, прямо под операторами #include, чтобы гарантировать, что они всегда будут определены до любого их потенциального использования.

Другое решение, которое также появляется в некотором реальном коде, состоит в том, чтобы просто переупорядочить ваши функции так, чтобы объявления функций всегда были перед всем, что их вызывает! Вы можете переместить всю функцию char *do_something(char *dest, const char *src) { ... } над первым вызовом, и тогда компилятор будет точно знать, как выглядит функция, и не нужно будет угадывать.

На практике большинство людей используют прототипы функций, потому что вы также можете взять прототипы функций и переместить их в файлы заголовков (.h), чтобы код в других файлах .c мог вызывать эти функции. Но любое решение работает, и многие кодовые базы используют оба.

C99 и C11

Полезно отметить, что правила немного отличаются в новых версиях стандарта C. В более ранних версиях (C89 и K & R) компилятор действительно будет угадывать типы во время вызова функции (а компиляторы эпохи K & R часто даже не предупреждают вас, если они ошибаются). C99 и C11 требуют, чтобы объявление функции / прототип предшествовало первому вызову, и если это не так, это ошибка. Но многие современные компиляторы C - главным образом для обратной совместимости с более ранним кодом - будут только предупреждать об отсутствующем прототипе и не будут считать это ошибкой.

4 голосов
/ 11 октября 2009

C Заповедь № 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

3 голосов
/ 08 августа 2015

Смотрите снова:

char dest[5];
char src[5] = "test";

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

Сфокусируйтесь на этой строке:

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

Вы можете ясно видеть, что функция do_something не объявлена!

Если вы посмотрите немного дальше,

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

char *do_something(char *dest, const char *src)
{
return dest;
}

вы увидите, что объявляете функцию после того, как вы ее используете.

Вам нужно будет изменить эту часть с помощью этого кода:

char *do_something(char *dest, const char *src)
{
return dest;
}

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

Ура;)

3 голосов
/ 11 октября 2009

Когда вы не передаете прототип функции перед ее использованием, C предполагает, что она принимает любое количество параметров и возвращает int. Поэтому, когда вы впервые пытаетесь использовать do_something, это тип функции, которую ищет компилятор. При этом должно появиться предупреждение о «неявном объявлении функции».

Так что в вашем случае, когда вы на самом деле объявляете функцию позже, C не допускает перегрузку функций, поэтому это вызывает недовольство, потому что вы объявили две функции с разными прототипами, но с одинаковым именем.

Краткий ответ: объявите функцию, прежде чем пытаться ее использовать.

1 голос
/ 08 июля 2016

Это часто происходит, когда вы изменяете определение функции c и забываете обновить соответствующее определение заголовка.

0 голосов
/ 22 марта 2012

Убедитесь, что типы в объявлении функции объявлены первыми.

/* start of the header file */



struct intr_frame{...}; //must be first!



void kill (struct intr_frame *);



/* end of the header file */

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...