Являются ли оба этих варианта использования изменчивых квалификаторов избыточными?

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

  1. ПРИМЕР Следующей пары объявлений демонстрирует разницу между "переменным указателем на постоянное значение" и "постоянным указателем на переменное значение".

     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, второй вариант не нужен