Що цікаво, так це різниця між умовами викликів функцій з та без прототипу. Розглянемо старе визначення стилю:
void f(a)
float a; {
}
У цьому випадку умова виклику полягає в тому, що всі аргументи просуваються перед передачею функції (наприклад, floatаргумент спочатку підвищується до double, перед передачею). Отже, якщо fотримує а, doubleале параметр має тип float(що цілком дійсний), компілятор повинен видавати код, який перетворює подвійний у плаваючий перед виконанням тіла функції.
Якщо ви включите прототип, компілятор більше не робить таких автоматичних рекламних акцій, і будь-які передані дані перетворюються у типи параметрів прототипу, як за призначенням. Отже, наступне не є законним і спричиняє невизначену поведінку:
void f(float a);
void f(a)
float a; {
}
У цьому випадку визначення функції перетворить надісланий параметр із double(рекламованої форми) у, floatоскільки визначення є старим стилем. Але параметр подано як плаваючий, оскільки функція має прототип. Наприклад, дзвін дає
main.c: 3: 9: попередження: підвищений тип "подвійний" параметра функції K&R не сумісний з типом параметра "float", оголошеним у попередньому прототипі [-Wknr-promoted-parameter]
Варіанти вирішення суперечностей:
void f(double a);
void f(a)
float a; {
}
void f(float a);
void f(float a) {
}
Варіант 2 слід віддавати перевагу, якщо у вас є вибір, оскільки він позбавляється від попереднього визначення старого стилю. Якщо такі суперечливі типи функцій для функції з'являються в одній і тій же одиниці перекладу, компілятор зазвичай повідомляє про це (але це не потрібно). Якщо подібні суперечності виникають у кількох одиницях перекладу, помилка, можливо, залишиться непоміченою і може призвести до важко передбачуваних помилок. Краще уникати цих визначень старого стилю.