В книге Modern C Йенс Густедт утверждает, что:
За исключением типов символов, псевдонимами могут быть только указатели одного и того же базового типа.
Но затем я нахожу это объявление fgetpos()
в Приложении B стандарта C:
int fgetpos(FILE * restrict stream, fpos_t * restrict pos);
где тип fpos_t
определяется как:
который представляет собой полный тип объекта, отличный от типа массива, способный записывать всю информацию, необходимую для однозначного указания каждой позиции в файле.
что вызывает вопрос:
Почему псевдоним fpos_t *
может быть FILE *
? Это потому, что fpos_t
может быть typedef
для FILE
? Если нет, то что оправдывает использование restrict
здесь?
Большинство (?) квалификаторов restrict
в прототипах стандартной библиотеки используются для указателей с разными базовыми типами, поэтому здесь нет ничего особенного.
В нем также говорится, что указатель, который вы передаете от вызываемого объекта, не является псевдонимом другого указателя того же типа в коде вызываемого объекта.
Если FILE
ограничивает одного и того же участника fpos_t
, то вызов fgetpos()
с этими двумя нарушает из-за restrict
.
Во-первых, restrict
в объявлении функции, которое не является определением, не имеет никакого эффекта в семантике C. В C 2018 6.7.6.3 15 говорится, что квалификаторы параметров игнорируются при определении совместимости функций, а это означает, что функция может быть объявлена с restrict
в ее параметрах, но определена без restrict
или наоборот. Таким образом, использование restrict
в документации, описывающей процедуру, — это просто совет читателю.
Обычно FILE
и fpos_t
не являются псевдонимами друг друга. (И обратите внимание, что нас интересует объект, а не указатель. Правила псевдонимов в 6.5.7 касаются того, какие типы lvalue могут использоваться для доступа к объекту, а не указателей, которые могут использоваться [за исключением, конечно, рассмотрения фактического псевдоним указателя].) Однако вы могли бы (в том смысле, что это возможно, но не желательно) объявить объединение, которое содержит как FILE
(если ваша реализация C определяет его как полный тип), так и fpos_t
, поэтому вы передаете в правильно подготовленном объекте FILE
и готовы пожертвовать им и заменить его на fpos_t
в той же памяти. Вы также можете сделать это с динамически выделяемой памятью, поскольку запись нового типа (fpos_t
) поверх старого типа (FILE
) является разрешенной формой псевдонимов (при записи в динамически выделяемую память любой тип, который вы используете, является эффективным типом, что разрешено для псевдонимов). Итак, restrict
в документации fgetpos
советует вам не делать этого.
Если поставить restrict
в документации, это означает, что передача одних и тех же указателей для обоих может вызвать UB, верно?
@Barmar: Можно подумать, и это должно быть намерение, но я не вижу, чтобы это было заявлено.
Возможно, это более четко указано в C23, чем в C17/18: 6.7.4.2.8-9 «Цена состоит в том, что программисту приходится проверять все эти вызовы, чтобы убедиться, что ни один из них не приводит к неопределенному поведению. Например, второй вызов f
в g
имеет неопределенное поведение, поскольку доступ к каждому из d[1]
-d[49]
осуществляется как через p
, так и через q
"
Они не могут использовать псевдонимы друг друга. Я думаю, что в объявлении используется
restrict
просто для того, чтобы предоставить компилятору как можно больше информации. Вероятно, это избыточно.