Совместимость типов функций, не включающих прототип

Существует правило совместимости типов функций N2310 6.7.6.3(p15):

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

Я могу представить пример:

#include <stdio.h>

int foo();

float bar();

int main(void){
    printf("%d\n", foo(1, 3)); //fine, int is unchanged by default argument promotion
    printf("%f\n", bar(1.0f, 2.0f)); //error, float is promoted to double
}

int foo(int a, int b){
    return a + b;
}

float bar(float b, float c){
    return b + c;
}

Что я нашел противоречивым, так это то, что 6.5.2.2(p6) упоминает, что:

Если количество аргументов не равно количеству параметров, поведение не определено.

В случае int foo() он имеет пустой identifier-list. Так дает ли вызов printf("%d\n", foo(1, 3)); UB (было предоставлено 2 аргумента)?

В любом случае правила выглядят довольно странно и как-то неестественно. Что было причиной этого? Я предполагаю некоторую обратную совместимость с предыдущими версиями Стандарта...?


person Some Name    schedule 12.10.2019    source источник
comment
В bar(1.0, 2.0), 1.0 и 2.0 не повышаются до double. Они double.   -  person Eric Postpischil    schedule 12.10.2019


Ответы (1)


C 2018 6.7.6.3 15 сообщает, совместимы ли два типа. Таким образом, его можно использовать для сравнения двух объявлений. Для foo у вас есть:

целое число(); int foo(int a, int b) {...}

Из них второй имеет список параметров, а первый задается объявлением функции, которое не является частью определения функции и содержит пустой список идентификаторов. Таким образом, применяется правило 6.7.6.3 15. В нем говорится, что список параметров не должен иметь ограничитель с многоточием (и это не так), и что тип каждого параметра должен быть совместим с типом, полученным в результате продвижения аргумента по умолчанию (и они совместимы, поскольку int производит int).

Тогда для bar имеем:

float bar();
float bar(float b, float c) {...}

Снова применяется 6.7.6.3 15. Но в этом случае каждый параметр имеет тип, который не является результатом продвижения аргумента по умолчанию, поскольку продвижение по умолчанию преобразует float в double. Таким образом, эти два объявления объявляют bar с несовместимыми типами.

Относительно 6.5.2.2 6:

… Если количество аргументов не равно количеству параметров, поведение не определено…

Это относится к количеству параметров фактической функции, а не к количеству параметров, появляющихся в (пустом) списке в объявлении.

В любом случае правила выглядят довольно странно и как-то неестественно. Что было причиной этого? Я предполагаю некоторую обратную совместимость с предыдущими версиями Стандарта...?

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

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

Когда вызов функции анализируется компилятором, для подготовки вызова используются правила из 6.5.2.2. Эти правила говорят, что аргументы обрабатываются по-разному в соответствии с объявлением функции, которое видно в точке вызова. (Технически, к типу выражения, обозначающего вызываемую функцию. Часто это имя функции, но может быть и указатель на функцию, включая функцию, вычисленную выражением приведения.)

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

person Eric Postpischil    schedule 12.10.2019