Подтвердить что ты не робот

Есть ли разница между спецификатором _Atomic type и спецификатором типа?

Почему стандарт делает эту разницу?

Кажется, оба обозначают, таким же образом, атомный тип.

4b9b3361

Ответ 1

Спецификаторы атомного типа: -:)

Syntax:     _Atomic ( type-name );

Вы можете объявить атомное целое число, подобное этому:

        _Atomic(int) counter;

Ключевое слово _Atomic можно использовать в форме _Atomic(T), где T является типом, как спецификатор типа, эквивалентный _Atomic T. Таким образом, _Atomic(T) x, y; объявляет x и y одним и тем же типом, даже если T - тип указателя. Это позволяет использовать тривиальную совместимость с С++ 0x с макросом С++ только с выражением _Atomic(T) как атомарным.

Спецификаторы атомного типа не должны использоваться, если реализация не поддерживает атомные типы. Имя типа в спецификаторе атомного типа не должно ссылаться на тип массива, тип функции, атомный тип или квалифицированный тип.

Свойства, связанные с атомными типами, имеют смысл только для выражений, которые являются lvalues.

If the _Atomic keyword is immediately followed by a left parenthesis, it is interpreted as a type specifier (with a type name), not as a type qualifier.

Атрибуты типа Atomic: -:)

        _Atomic volatile int *p;

It specifies that p has the type ‘‘pointer to volatile atomic int’’, a pointer to a volatile-qualified atomic type.

Типы, отличные от типов указателей, ссылочный тип которых является типом объекта, не должны быть ограничены. Тип, измененный классификатором _Atomic, не должен быть типом массива или типом функции. Свойства, связанные с квалифицированными типами, имеют смысл только для выражений, которые являются lvalues.

Если один и тот же определитель появляется более одного раза в том же списке спецификаторов-спецификаторов, либо напрямую, либо через один или несколько typedefs, поведение такое же, как если бы оно появилось только один раз. Если в спецификаторе-классификаторе-списке появляются другие квалификаторы вместе с квалификатором _Atomic, результирующий тип - это так называемый атомный тип.

Ключевое слово _Atomic используется отдельно, как спецификатор типа. Реализации разрешено ослабить требование иметь такое же представление и выравнивание соответствующего неатомного типа, если при этом выполняются соответствующие преобразования, в том числе с помощью оператора трансляции.

Ответ 2

Да. Есть разница. Когда он используется как спецификатор типа, тогда стандарт ограничивает его как (6.7.2.4 p (3)):

Имя типа в спецификаторе атомного типа не должно ссылаться на тип массива , тип функции, атомный тип или квалифицированный тип.

Например

typedef int arr[5];  

arr может быть именем типа, когда _Atomic используется как определитель, но не может использоваться как имя типа, если _Atomic используется как спецификатор типа (например, _Atomic (arr))

Ответ 3

После многих попыток я нашел, зачем это нужно: указатели !

Предположим, у вас есть:

int foo = 1;
int bar = 2;
int *p = &foo;

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

По причинам, которые касаются вашей программы, вы можете захотеть:

  • foo должен быть атомарным, так что вы можете, например, атомарно изменить значение foo на 2 вместо 1.
  • p должен быть атомарным, так что вы можете, например, атомарно изменять то, на что указывает p, и указывать на bar вместо foo.

В первом случае сделать foo атомарным легко, при его чтении не возникает двусмысленности:

_Atomic int foo;
atomic_store_explicit(&foo , 2, memory_order_release); /* valid atomic op. */

Но теперь вы хотите сделать p атомарным, если напишите:

_Atomic int *p;

... это не то, что вы хотите!

То есть, как объяснено выше, неатомный указатель на атомарный int. Строго говоря, нет никакой гарантии, что этот указатель будет правильно выровнен, чтобы иметь возможность выполнять атомарные операции над ним (хотя вам будет трудно заставить компилятор сместить указатель!). Это означает, что, если вам удалось сделать указатель смещенным, атомарные операции с ним будут иметь шанс на сбой. С другой стороны, вам нужен атомарный указатель на int, который не является обязательным атомарным.

Итак, вы должны написать:

int bar = 2;
_Atomic (int *) p;
atomic_store(&p , &bar); /* now valid atomic operation */

Теперь у вас есть атомный указатель!

Обратите внимание, что для очень простого случая сделать foo int атомарным, вы могли бы также написать любое из этих 3-х объявлений, последнее использует вспомогательный typedef, определенный в stdatomic.h:

typedef _Atomic int atomic_int;

_Atomic int foo;
_Atomic (int) foo;
atomic_int foo;

Я сделал это "легким для понимания" с помощью int и указателя на и int, но когда вам приходится иметь дело с

_Atomic (struct foobar *) *q;

Теперь вы будете знать, что сам q не является атомарным указателем, но он указывает на атомарный указатель на структуру foobar!

И так демонстрация:

#include <stdatomic.h>
void test()
{
    _Atomic int foo = 1;         /*   Atomic   */
    _Atomic int *pf = &foo;      /* Non Atomic */
    _Atomic int **ppf = &pf;     /* Non Atomic */
    int bar = 2;                 /* Non Atomic */
    _Atomic (int *) pb = &bar;   /*   Atomic   */
    _Atomic (int *) *ppb = &pb;  /* Non Atomic */

    int *res;

    res = atomic_load(ppf); /* Not OK, yields a warning */
    res = atomic_load(ppb); /* This is correct */
}

В тесте функции:

test.c: 13: 6: предупреждение: присваивание из несовместимого типа указателя [-Wincompatible-pointer-types]

res = atomic_load (ppf);

Действительно, первый atomic_load пытается вернуть неатомарный указатель на int: указатель на int является атомарным, а не указателем. Это также может привести к сбою, потому что нет гарантии того, что & pf (содержимое ppf) правильно выровнено для атомарной операции (хотя практически здесь, вам бы пришлось привести pf к не выровненному int, чтобы он потерпел неудачу).

Второй atomic_load корректно работает с атомарным указателем и возвращает его в res.