Недопонимание с объявлением структуры с ключевым словом static

Почему можно так:

struct test {
    int a;
};

typedef struct test * PTR_TEST;

Но если сделать так, то появляется следующее предупреждение: useless storage class specifier in empty declaration

static struct test {
        int a;
    };
    
typedef struct test * PTR_TEST;

Ответы (2 шт):

Автор решения: HolyBlackCat

Сравните следующий синтаксис:

  1. struct A {/*...*/}; — определение структуры
  2. struct A x, y; — определение переменных типа структуры
  3. struct A; — объявление структуры.

На самом деле это одна и та же конструкция, просто в случаях (1) и (3) список переменных пуст, а в (2) и (3) тело структуры опущено.

Следовательно можно писать вот так: struct A {/*...*/} x, y;, одновременно определяя и структуру, и переменные этого типа.

Если написать static A {/*...*/} x, y;, то static будет применен только к переменным (x, y), но не к самой структуре A - статическими бывают только переменные, но не сами структуры.

Поэтому static имеет смысл только если список переменных не пуст. Но, видимо для единообразия, static не стали запрещать даже если он пуст.

→ Ссылка
Автор решения: nick_n_a

Структура не может быть статиком, статик - это свойство переменной. Запись static struct test допустима, но у вас пропущено тогда имя переменной, которая закреплена за этой структурой. Т.е.

static int myvar1; // обьявили статическую переменную myvar1
static struct test {
 int a;
 } myvar2; // обьявили статическую переменную myvar2

Под общий статик - нельзя вынести как в с#, но многие версии c++ допускают делать статиком отдельные элементы структуры, т.е. если вы хотите полностью статик-структуру, то прийдется каждый элемент структуры делать статиком поотдельности. T. к. c методами (условно) всё ок, то поля обьявляються чуть сложнее. Это будет выглядеть так (в чистом си такое не работает):

struct test{
  static int a; // Этот элемент будет "общим" 
                // для всех экземпляров test
};

int test::a; // это требуется дописать при использовании static

А дальше тоже интересно. Доступ к "а" возможен двумя способами

test var1;
var1.a = 2; // Присвоили в а двойку
test::a = 1; // перетёрли двойку в еденицу.

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

UDP: Ещё один необычный способ, позволяющий и в си и в с++ обьявить статик - пометка static в локальных переменных переносит переменную из области стека в область глобальных переменных.

 int * getStatic() { 
   static int q =0; 
   return &q; 
   }

Тут не присваивание, а инициализатор по-умолчанию, поэтому после вызова метода q будет не всегда ноль, а после первого вызова и пока не будет присвоено другое значение. Без этого - есть риск что в переменной будет "мусор". Если в си смысла в этом особо нету, то в плюсах не нужно писать int test::a; дополнительно, что возможно удобнее, но появляется дополнительный вызов (который оптимизатор скорее всего уберёт). В случаях структур и классов - как инициализатор по-умолчанию нужно {} использовать. Так же инициализатор по-умолчанию скорее всего переведёт переменную в область bss (примерно тоже что и глобальная, только незанимает место в бинарнике).

#include <stdio.h>
int*  q() {
   static int n=0;
  return &n;
}

int main() {
    *q() = 1;
    printf("Hello, world!");
    printf ( ((*q())==1) ? "1" : "0"); // вернёт 1
    return 0;
}
→ Ссылка