Почему аргументы fopen ограничены квалификацией в файле заголовка C и <stdio.h>?

Стандартная библиотечная функция fopen объявляется в <stdio.h> как:

FILE *fopen(const char * restrict filename, const char * restrict mode);

Это также то, как прототип функции появляется в стандарте C.

Почему аргументы restrict квалифицированы?

Ответ 1

Кажется, нет никаких веских причин для аргументов fopen, которые должны быть restrict квалифицированы в прототипе в <stdio.h>.

restrict Определение указателя - это обещание программиста, что объект, на который указывает указанный указатель, будет доступен только через этот и любой другой указатель, основанный на нем в пределах данной области.

В прототипе функции такое обещание является спорным.

Nate Eldredge предложил объяснение, что это означает, что вам запрещено указывать имя файла и режим в одну строку. Но это утверждение кажется несущественным, и такое ограничение не требуется и не упоминается в определении fopen в разделе 7.21.5.3 Стандарта C.

Прототип для setbuf имеет тот же restrict квалификатор для своих аргументов:

void setbuf(FILE * restrict stream, char * restrict buf);

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

То же самое можно сказать и о реализации fopen, где программист сообщает компилятору, что структура FILE, управляемая fopen, не перекрывает имя файла или режим. Но определение как filename, так и mode является ложным обещанием, поскольку оно подразумевает ограничение, которое отсутствует в стандарте.

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

Ответ 2

Это означает, что данные не будут изменены. Фактического исполнения этого обещания не существует, как это часто бывает в C, но неспособность жить этим обещанием может привести к поведению undefined.

По сути, квалификатор restrict означает, что никакой другой указатель не изменит значение имени файла или режима доступа, пока дескриптор файла действителен. Вот выдержка из веб-сайта, которая охватывает это довольно неплохо:

Ограничение - это контракт "без каких-либо факторов, связанных с данными", между программистом и компилятором. Компилятор полагается на эту информацию для оптимизации. Если данные, по сути, сглажены, результаты undefined, и программист не должен ожидать, что компилятор выдаст предупреждение. Компилятор предполагает, что программист не лежит.

Итак, почему он там? Поскольку, когда была написана библиотечная функция fopen, автор решил попросить вас не изменять строки, которые вы передаете, после того как вы попросите его открыть файл. Чтобы быть откровенным, я не понимаю, почему это было бы даже запрошено, потому что, насколько мне известно, после открытия файла имеет смысл только файловый дескриптор, и имя файла и режим никогда не упоминаются снова внутри.

Ответ 3

Формальное определение значения restrict приведено в разделе раздела 6.7.3.1 из C2011. Обратите внимание, в частности, что элементы списка параметров прототипа функции, который не является частью определения функции, не соответствуют условиям, изложенным в параграфе 1 этого раздела:

Пусть D - объявление обычного идентификатора, которое предоставляет средство для обозначения объекта P в качестве ограничивающего ограничения указателя на тип T.

Список параметров такого прототипа не предоставляет никаких средств для такого обозначения. Поэтому ничто в этом разделе не дает прямого влияния на квалификатор restrict в этом контексте.

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

Обратите также внимание, однако, что restrict не является полным обещанием отсутствия псевдонимов. Вместо этого это квалифицированное обещание, что если цель restrict -qualified pointer будет изменена каким-либо образом, тогда она будет доступна только через этот указатель в пределах области указателя.

Возвращаясь к fopen(), тогда в t20 > в прототипе функции нечего сказать о определенности поведения любого вызова функции. То есть это не по своей сути undefined:

char s[] = "r";
FILE *f = fopen(s, s);

Выполнение функции - это еще одна история - или это было бы, если бы указательные цели не были также const -qualified. Предположим, что прототип выражает эффективные квалификаторы для определения функции, если два аргумента друг друга и, если их цель была изменена функцией, тогда функция будет вызывать поведение undefined, если она обратилась к target через другой указатель. Поскольку const квалификация целевых указателей означает, что функция будет вызывать UB, изменяя цель в первую очередь, restrict квалификация спорна.С >

Выполнение функции - это еще одна история. Хотя квалификация const целевых показателей параметра означает, что мы должны предположить, что fopen() не будет пытаться модифицировать их с помощью этих указателей, сами цели не обязательно const. Их можно было бы модифицировать вне fopen(), используя значение const l, и это все равно будет иметь отношение к квалификации restrict. Предположим, что прототип выражает эффективные квалификаторы для определения функции. Если два аргумента друг друга и если их общая цель изменена в любом месте программы, тогда fopen() будет вызывать UB, когда он обращается к цели с помощью обоих указателей.