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

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

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

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

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

3 ответа

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Другие вопросы по тегам