Функция без прототипа вызывается с несовместимым типом - PullRequest
6 голосов
/ 29 марта 2019

Я прочитал раздел Standard N1570 6.5.2.2 Function calls и был озадачен особым значением типа функции, который включает в себя прототип.Точно 6.5.2.2(p6)

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

- один повышенный тип является целочисленным типом со знаком, другой повышенный тип является целочисленным типом без знака, и значение представимо в обоих типах;

- оба типа являются указателями на квалифицированные или неквалифицированные версии символьного типа или void.

6.5.2.2(p7) предоставляет правило вызова функции с прототипом:

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

Рассмотрим следующий пример:

struct test_arg{
    int a;
};

void test_no_prototype(const struct test_arg a){ }

void test_with_prototype(const struct test_arg a);

void test_with_prototype(const struct test_arg a){ }

int main(){
    struct test_arg test = {.a = 42};
    test_no_prototype(test);   //1 UB?
    test_with_prototype(test); //2 Fine?
}

Я думаю, что 1 - это UB, потому что test_no_prototype не включает прототип, а test имеет неквалифицированную версию struct test_arg, но аргумент имеет тип const struct test_arg, который несовместим с struct test_arg из-за разной квалификации.

Я думаю, что 2 - это хорошо, потому что test_with_prototype включает в себя прототип и простые ограничения присваивания из 6.5.16.1(p1) позволяют присваивать переменную квалифицированного типа структуры из неквалифицированной версиита же структура.

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

1 Ответ

7 голосов
/ 29 марта 2019

Термин prototype не означает объявление функции, предшествующей ее определению.Это означает объявление функции, которая объявляет типы ее параметров (C 2018 6.2.1 2).

test_no_prototype имеет прототип, потому что void test_no_prototype(const struct test_arg a){ } объявляет тип ее параметра const struct test_arg.

Примером объявления без прототипа является void test_no_prototype();.Это старый стиль объявления, который не должен использоваться в новом коде.

...