В C, является ли объявление функции extern, которая исключает квалификатор const / restrict, эквивалентным исходному объявлению?

#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 .