#c #syntax #language-lawyer #declaration #volatile
#c #синтаксис #язык-юрист #объявление #volatile
Вопрос:
Рассмотрим
volatile int volatile * volatile vip; // (1)
и
volatile int volatile * volatile vipa[10]; // (2)
Запускаются обе строки кода -Wduplicate-decl-specifier
(см. версию 236142
и примечания к выпуску gcc7). Я хотел бы знать, могу ли я удалить некоторые volatile
спецификаторы из данного кода без изменения семантики кода, а также понять причины, стоящие за этим.
Таким образом, следующие вопросы:
a. В (1) ссылаются ли оба volatile
квалификатора на 1-й и 2-й int
, что означает «дублирование» в терминах gcc? (Здесь я смотрю на C99 6.7.3.4.)
b. В (2) относится ли один из volatile
определителей к типу массива, а не к int
самому указателю, так что выполняется C99 6.7.3.8:
Если спецификация типа массива включает в себя какие-либо квалификаторы типа, то таким образом определяется тип элемента, а не тип массива.
или volatile
спецификаторы в (2) влияют только на int
и pointer
тип, а не на тип массива?
c. Если ответ на b отрицательный, как мне объявить volatile
тип массива, который описан в C99 6.7.3.8? Это синтаксис, описанный вhttps://en.cppreference.com/w/c/language/array (цитата следует)?
квалификаторы — любая комбинация квалификаторов const, restrict или volatile, разрешенных только в списках параметров функций; это определяет тип указателя, в который преобразуется этот параметр массива
Давайте рассмотрим этот вопрос о C99. Если в C11 есть какие-либо различия в этом отношении, пожалуйста, сделайте пометку.
Ответ №1:
TL; DR:
- В (1) ссылаются ли оба
volatile
квалификатора на 1-й и 2-йint
, что означает «дублирование» в терминах gcc? (Здесь я смотрю на C99 6.7.3.4.)
да, они оба соответствуют int
и являются дубликатом.
- В (2) ссылается ли один из изменчивых квалификаторов на тип массива и
not
на int или на сам указатель, так что C99 6.7.3.8 выполняется:
C99 6.7.3.8 здесь не выполняется. Квалификатор уже применяется к типу элемента. Можно применить определитель к массиву с typedef , но это также определяет тип элемента (см. Ниже)
c. Если ответ на b отрицательный, как мне объявить тип массива volatile, который описан в C99 6.7.3.8?
Например, с помощью typedef
.
Стандарт C явно разрешает использование квалификаторов более одного раза. C11 n1570 6.7.3p5:
Если один и тот же квалификатор появляется более одного раза в одном и том же списке спецификаторов-квалификаторов, либо напрямую, либо через одно или несколько определений типов, поведение такое же, как если бы оно появилось только один раз.
т. Е. То, что -Wduplicate-decl-specifier
не является ошибкой как таковой, но такой код дословно является подозрительным — должно ли быть, volatile int *volatile
что было написано с ошибкой, так как volatile int volatile *
из-за этого указатель был неквалифицирован…
Квалификаторы применяются к типу слева от классификатора, за исключением случаев, когда сам классификатор является крайним левым, и в этом случае это так, как если бы он был справа от базового типа, т.Е.
volatile int *
и
int volatile *
означает одно и то же. Поэтому в volatile int volatile
вы можете удалить один из них. Таким образом, то, что вам нужно, это
volatile int *volatile vipa[10];
Это означает, что vipa is an array of 10
volatile -qualified pointers to
volatileint's.
Что означает C99 6.7.3p8 / C11 6.7.3p9, так это то, что массив сам по себе не может быть volatile - его адрес постоянен, только его элементы могут быть квалифицированы. следовательно, если тип массива определен, он применяется только к его элементам. Это даже так, если указан typedef:
typedef int intarray[5];
const intarray myarray;
объявит myarray
, как если бы с помощью
const int myarray[5];
тогда как, если бы вы использовали typedef для указателя:
typedef int *intptr;
const intptr myptr;
этот квалификатор не повлияет на указанный тип, но будет эквивалентен
int *const myptr;
Хотя оба volatile int
и int volatile
строго разрешены, стандарт C предпочитает первое. C11 n1570 6.7.6.1p3:
ПРИМЕР Следующей пары объявлений демонстрирует разницу между "переменным указателем на постоянное значение" и "постоянным указателем на переменное значение".
const int *ptr_to_constant; int *const constant_ptr;
Содержимое любого объекта, на который указывает
ptr_to_constant
, не должно изменяться с помощью этого указателя, ноptr_to_constant
само по себе может быть изменено, чтобы указывать на другой объект. Аналогично, содержимое, на котороеint
указываетconstant_ptr
, может быть изменено, ноconstant_ptr
само по себе всегда должно указывать на одно и то же местоположение.
Кроме того, можно добавить определитель типа для массива в скобках, но только в параметрах функции, так что вы можете написать
void foo(int array[volatile])
что означает почти одно и то же, и параметр распадается до определенного указателя
void foo(int *volatile array)
но вы можете использовать static
спецификатор только с прежним стилем.
Комментарии:
1. Спасибо! Просто убедитесь, что я правильно понял эту часть. "Если спецификация типа массива включает в себя какие-либо квалификаторы типа ..." - это, по сути, последнее предложение в вашем ответе, правильно? Выглядит ли "конкретный тип массива, который включает квалификаторы" как последнее объявление в ответе?
2. Тот факт, что массив не может быть определен, не связан с тем фактом, что его адрес постоянен. Каждый объект имеет постоянный адрес. Причина, по которой квалификаторы применяются к элементам, а не к массивам, заключается в том, что массив никогда не может использоваться как объект таким образом, чтобы применялись квалификаторы.
Ответ №2:
объяснение простое.
volatile int *
== int volatile *
в этом случае порядок не имеет значения.
Итак volatile int * volatile x;
== int volatile * volatile x;
если у вас есть volatile int volatile *
, вы уже объявили его как volatile, второй вариант не нужен