2013-04-18 3 views
5

С помощью следующего кода:Вопросы о C Прототипы функций и Сборнике

int main(){ 
    printf("%f\n",multiply(2)); 
    return 0; 
} 

float multiply(float n){ 
    return n * 2; 
} 

Когда я пытаюсь скомпилировать я получаю одно предупреждение: «„% е“ожидает„двойной“, но аргумент имеет тип„Int“» и две ошибки: «конфликтующие типы для« multiply »,« предыдущее неявное объявление «multiply» было здесь ».

Вопрос 1: Я предполагаю, что это потому, что, учитывая компилятор не имеет знания о функции «умножение», когда он приходит через него в первый раз, он будет изобрести прототип, и изобрел прототипы всегда предполагать «Int» оба возвращаются и принимаются как параметр. Таким образом, изобретенный прототип будет «int multiply (int)», и, следовательно, ошибки. Это верно?

Теперь предыдущий код даже не компилируется. Однако, если я нарушу код в двух файлах, как это:

#file1.c 
int main(){ 
    printf("%f\n",multiply(2)); 
    return 0; 
} 

#file2.c 
float multiply(float n){ 
    return n * 2; 
} 

и выполнить «НКУ file1.c file2.c -o файл» он все равно будет давать одно предупреждение (что Printf ожидает двойной, но получает Int), но ошибки больше не появятся, и они будут компилироваться.

Вопрос 2: Как происходит, когда я разбиваю код на 2 файла, который он компилирует?

Вопрос 3: После запуска программы выше (версия разбита на 2 файла) результатом является то, что на экране печатается 0,0000. Как так? Я предполагаю, что компилятор снова изобрел прототип, который не соответствует функции, но почему печатается 0? И если я изменил printf («% f») на printf («% d»), он напечатает 1. Опять же, какое-либо объяснение того, что происходит за кулисами?

Большое спасибо.

ответ

4

Так изобрел прототип будет «Int умножения (INT)», и, следовательно, ошибки. Это верно?

Абсолютно.Это делается для обратной совместимости с pre-ANSI C, у которой отсутствовали прототипы функций, и все объявленное без типа было неявным int. Компилятор компилирует ваш main, создает неявное определение int multiply(int), но когда он находит реальное определение, он обнаруживает ложь и рассказывает об этом.

Как это происходит, когда я разбиваю код на 2 файла, которые он компилирует?

компилятор никогда не обнаруживает ложь о прототипе, так как он собирает один файл в то время: он предполагает, что multiply принимает int и возвращает в вашем main в int, и не находит никаких противоречий в multiply.c. Однако выполнение этой программы приводит к неопределенному поведению.

После запуска программы выше (версия разбита на 2 файла) результатом является то, что на экране печатается 0,0000.

Это результат неопределенного поведения, описанного выше. Программа будет компилироваться и связываться, но поскольку компилятор считает, что multiply занимает int, он никогда не будет конвертировать 2 в 2.0F, а multiply никогда не узнает. Аналогичным образом, неправильное значение, вычисленное удвоением , переинтерпретировано как float в вашей функции multiply, будет снова рассматриваться как int.

+0

Имеет смысл. Единственный момент, который до сих пор не ясен, - это то, почему компилятор не найдет ложь/противоречие с 2 файлами. В конце концов, он все равно создаст прототип int multiply (int) при работе через main.c, и как только он попадет во второй файл, он встретит объявление функции float multiply (float), что явно противоречит предыдущему опытный образец. –

+2

@ Даниэль Хороший момент! Я отредактировал ответ, чтобы упомянуть, почему: по сути, компилятор компилирует каждый файл отдельно, по одному источнику за раз. Это отличается от, скажем, Java, который рассматривает все файлы одновременно. С C и C++ каждая единица перевода (это причудливое имя для файла .c') компилируется отдельно, а затем компоновщик собирает результаты вместе. К тому моменту, когда линкер попадает в игру, вся информация о типе отсутствует: компоновщики работают на гораздо более низком уровне байтов, смещений и адресов, поэтому они также не могут обнаружить расхождение. – dasblinkenlight

+1

@ DanielS: компилятор работает с одним файлом за раз; работая над файлом 'file1.c', он ничего не знает о содержимом' file2.c' и наоборот. При создании 'file1.c' он знает только, что' multiply' не был объявлен перед использованием. При создании 'file2.c' он не знает, что' multiply' используется в другой единицы перевода без соответствующего объявления в области видимости. –

1

Вопрос 1: Да, вы в порядке. Если нет прототипа функции, типа по умолчанию int

Вопрос 2: При компиляции этого кода в виде одного файла, компилятор видит, что уже есть функция с именем multiply и имеет другой тип, чем предполагались (double вместо int). Таким образом, компиляция не работает.

Когда вы отделяете это в двух файлах, компилятор делает два файла .o. В первом случае предполагается, что функция multiply() будет находиться в другом файле. Затем компоновщик связывает оба файла с двоичным и в соответствии с именем multiply вставляет вызов float multiply() на место int multiply(), предположительно компилятором в первом файле .o.

Вопрос 3: Если вы читали int2 как float, вы получите очень небольшое количество (~ 1/2^25), так что после этого нужно умножить его на 2 и она по-прежнему остается слишком мало для формата %f. Вот почему вы видите 0.00000.

1

Неопределенная функция имеет тип возврата int (вот почему вы получаете предупреждение, компилятор считает, что он возвращает целое число) и неизвестное количество неопределенных аргументов.

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

1

Question1:

Так изобрел прототип будет "ИНТ умножения (целое)", и, следовательно, ошибки. Это верно?

Не exactelly да, потому что это зависит от вашего Cx (C89, C90, C99, ...)

для возврата значений функции, до c99 она была явно специфическая эд, что, если не объявление функции не было видно переводчик предоставил один. Эти неявные декларации по умолчанию для возвращаемого типа ИНТ

Обоснования из C Standard (6.2.5 стр 506)

До C90 не было никаких функциональных прототипов. Разработчики ожидали, что смогут обмениваться аргументами, которые подписали и без знака версии того же целочисленного типа. Приведя аргумент, если тип параметра в определении функции имел другую подпись, рассматривался как противостоящий простой системе проверки типов C и малоинтрузивный код . Внедрение прототипов не полностью устранило проблему взаимозаменяемости аргументов . В значении многоточия указано, что ничего не известно о эллипсисе 1590 , не содержит информации о ожидаемом типе аргументов. Аналогично, для возвращаемых значений функции до C99 было явно указано, что , если не было видно объявления функции, предоставленного переводчиком. Эти неявные объявления по умолчанию были возвращены типу int. Если фактическая функция привела к возврату типа unsigned int, такое объявление по умолчанию могло бы вернуть неожиданный результат. Многие разработчики имели случайное отношение к объявлениям функций. Остальные из нас должны жить с последствиями Комитета, а не , желая сломать весь исходный код, который они написали. взаимозаменяемость возвращаемых значений функций теперь спорный вопрос, потому C99 требует, чтобы объявление функции будет видна в точке вызова (декларация по умолчанию больше не входит в комплект)

Вопрос 2:

Как это происходит, когда я разбиваю код на 2 файла, которые он компилирует?

он будет компилировать и он будет рассматриваться как указано в первом вопросе exactelly тот же

Смежные вопросы