#c
#c
Вопрос:
Например, fwrite
имеет следующую подпись:
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
Если бы я объявил:
size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream);
(исключая const),
гарантировано ли, что при заданном неконстантном аргументе оно все равно будет вести себя так же?
Да, я знаю, что надлежащая практика заключается в включении заголовка, но ради этого вопроса я предполагаю, что это не вариант.
Комментарии:
1. В вашей предлагаемой подписи как разрешаются имена параметров?
2. Да, это то же самое. В C. нет именованных параметров (но они могут использоваться для чтения человеком), кстати, в ваших
;
прототипах они отсутствуют3. Я добавил имена параметров в свое новое объявление, просто чтобы избежать путаницы.
4. Да, это сработало бы. Но, наоборот, с реальным определением:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
и фиктивным определениемsize_t fread(const void *ptr, size_t size, size_t nmemb, FILE *stream)
может быть проблематичным с:buf[5] = 20; fread(buf,1,20,stream); x = buf[5];
Это может сообщить оптимизатору, что он может изменить порядокbuf[5] = 20; x = buf[5]; fread(buf,1,20,stream);
этого как:fread
поскольку предполагается, что это не изменит буфер, что приведет к UB.5. @wildplasser использование неправильного модификатора, как в вопросе, вызывает неопределенное поведение, поэтому сгенерированный код может измениться (или программа будет отклонена). Но ваш комментарий по-прежнему неверен для других случаев, компилятор может воспользоваться информацией, предоставленной квалификаторами . (и должно делать это в случае
volatile
) .
Ответ №1:
В C 2018 6.7.6.3 15 говорится:
Для совместимости двух типов функций оба должны указывать совместимые возвращаемые типы. Более того, списки типов параметров, если присутствуют оба, должны совпадать по количеству параметров и использованию многоточия; соответствующие параметры должны иметь совместимые типы…
Итак, мы видим, что для того, чтобы fwrite
in size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
был совместим с fwrite
in size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream);
, const void *
должен быть совместим с void *
.
В C 2018 6.7.6.1 2 говорится:
Для совместимости двух типов указателей оба должны быть одинаково квалифицированы и оба должны быть указателями на совместимые типы.
Поскольку const *
и void *
не имеют идентичного определения, они несовместимы, поэтому два fwrite
объявления выше несовместимы.
Кроме того, в C 2018 6.5.2.2 9 говорится о вызове функции:
Если функция определена с типом, который несовместим с типом (выражения), на который указывает выражение, обозначающее вызываемую функцию, поведение не определено.
Это означает, что если fwrite
вызывается с использованием одного из приведенных выше объявлений, но определяется с помощью другого объявления (с заменой точки с запятой на блок, реализующий функцию), поведение не определено стандартом C.
Примечание
C 2018 6.7.6.3 15 включает:
… (При определении совместимости типов и составного типа каждый параметр, объявленный с типом функции или массива, принимается как имеющий скорректированный тип, а каждый параметр, объявленный с определенным типом, принимается как имеющий неквалифицированную версию своего объявленного типа.)
Это не применяется в fwrite
объявлении. В нем говорится, что если параметр, например, указан с const
, это игнорируется для определения совместимости. Таким образом, const size_t size
было бы фактически таким же, как size_t size
. Однако в const void *ptr
параметр не const
определен; это указатель на const
определенный тип. Параметр ptr
был бы const
определен, если бы он был void * const ptr
или const void * const ptr
.