Що цікаво, так це різниця між умовами викликів функцій з та без прототипу. Розглянемо старе визначення стилю:
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 слід віддавати перевагу, якщо у вас є вибір, оскільки він позбавляється від попереднього визначення старого стилю. Якщо такі суперечливі типи функцій для функції з'являються в одній і тій же одиниці перекладу, компілятор зазвичай повідомляє про це (але це не потрібно). Якщо подібні суперечності виникають у кількох одиницях перекладу, помилка, можливо, залишиться непоміченою і може призвести до важко передбачуваних помилок. Краще уникати цих визначень старого стилю.