Загадочное слово static

Одно из самых загадочных директив в языке С static является важнейшим при сборке проектов из исходников разных производителей кода и очень критично. Также важно для понимания , где искать странные ошибки сборки.

В двух словах static говорит компилятору/линковщику разместить данный объект в НЕ в стеке, а сегменте BSS (Block Started by Symbol).

BSS это просто сегмент памяти RAM , который фиксируется сразу при компиляции/линковке вашей программы.

фотка 1

К тому же синтаксически static гарантирует , что объект , объявленный с директивой static в конкретном файле blabalbla.c будет использоваться только в этом файле (blabalbla.c).

Это будет только локальный объект и в других файлах *.с будет не доступен . Таким образом в других файлах можно обзывать объекты таким же именем и конфликтов не будет. Это просто удобно.

Объект, объект, объект .... А каком объекте мы вообще говорим. И тут оказывается , что static надо отдельно рассматривать применительно к разным объектам :

переменная глобальная (где-то над всеми функциями в файле *.с)
переменная локальная (где-то в функции)
класс
метод класса
переменная класса (он же член)
функция где-то в каком-то с файле

Все эти варианты надо рассматривать на практических примерах. Начнем с простейшего :

глобальная переменная в файле *.с где-то в самом начале

#include 

static int COUNT=0;

void func1()
{
    printf("");
    //int COUNT=0;
    printf("%d",COUNT++);
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    
    printf("start main\n");

    for (int i = 0; i < 10; ++i)
    {
        func1();
    }

    printf("\nend main\n");

    return a.exec();
}

Когда используем static для глобальной переменной COUNT , то COUNT попадет .bss сегмент.
Примечание: теперь из другого файла с до COUNT не достучитесь теперь никак.

Когда без static COUNT попадет в кучу (heap).
Примечание: Теперь из другого файла с можно достучаться до COUNT через директиву extern.
На самом деле непонятно то ли в кучу, то ли в .bss.
objdump.exe -D -h -e -T -S -g -r main.o > main.txt показывает неоднозначно как-то.

локальная переменная где-то в функции


#include 

void func1()
{
    printf("");
    static int COUNT=0; // примечательно , что присваивание 0  происходит только один раз 
    //при инициализации программы , то есть в начале загрузки программы в память (.bss сегмент), еще до main
    printf("%d",COUNT++);
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    
    printf("start main\n");

    for (int i = 0; i < 10; ++i)
    {
        func1();
    }

    printf("\nend main\n");

    return a.exec();
}

Вывод:

start main
0123456789
end main

Если static не использовать в данном примере вывод будет таким:

start main
0000000000
end main

В данном варианте без static переменная COUNT создается на стеке для каждого вызова функции func1 заново (и ей присваивается 0 каждый раз заново).

Маленькая ржачка : никогда не называйте свои проекты в Qt словом static, добавьте еще хотя бы один символ.

статическая функция

#include <stdio.h>

static void func1() // 
{
    static int COUNT123=0;
    printf("%d",COUNT123++);
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    printf("start main\n");

    for (int i = 0; i < 10; ++i)
    {
        func1();
    }

    printf("\nend main\n");

    return a.exec();
}

На первый взгляд кроме того , что функцию func1 нельзя теперь вызвать из других файлов с вроде различий нет (что func1 со static , что без нее).
Но на самом деле в коде асеммблерный код немного меняется. Без static так :

void func1()
{
       0:	6b 58 01 00          	imul   $0x0,0x1(%eax),%ebx
       4:	02 00                	add    (%eax),%al
    static int COUNT123=0;
    printf("%d",COUNT123++);
       6:	00 00                	add    %al,(%eax)
			6: secrel32	.debug_abbrev
       8:	00 00                	add    %al,(%eax)
       a:	04 01                	add    $0x1,%al
       c:	47                   	inc    %edi
       d:	4e                   	dec    %esi
       e:	55                   	push   %ebp
       f:	20 43 2b             	and    %al,0x2b(%ebx)
      12:	2b 20                	sub    (%eax),%esp
      14:	34 2e                	xor    $0x2e,%al
      16:	34 2e                	xor    $0x2e,%al
      18:	30 00                	xor    %al,(%eax)
      1a:	04 2e                	add    $0x2e,%al
      1c:	2e                   	cs
      1d:	5c                   	pop    %esp
      1e:	73 74                	jae    94 <.debug_info+0x94>
      20:	61                   	popa   
      21:	74 69                	je     8c <.debug_info+0x8c>
      23:	63 5f 5c             	arpl   %bx,0x5c(%edi)
}

А когда staic func1 так:

static void func1()
{
       0:	60                   	pusha    / / !!!!
       1:	58                   	pop    %eax  / / !!!!
       2:	01 00                	add    %eax,(%eax) // и далее все тоже самое
       4:	02 00                	add    (%eax),%al
    static int COUNT123=0;
    printf("%d",COUNT123++);
       6:	00 00                	add    %al,(%eax)
			6: secrel32	.debug_abbrev
       8:	00 00                	add    %al,(%eax)
       a:	04 01                	add    $0x1,%al
       c:	47                   	inc    %edi
       d:	4e                   	dec    %esi
       e:	55                   	push   %ebp
       f:	20 43 2b             	and    %al,0x2b(%ebx)
      12:	2b 20                	sub    (%eax),%esp
      14:	34 2e                	xor    $0x2e,%al
      16:	34 2e                	xor    $0x2e,%al
      18:	30 00                	xor    %al,(%eax)
      1a:	04 2e                	add    $0x2e,%al
      1c:	2e                   	cs
      1d:	5c                   	pop    %esp
      1e:	73 74                	jae    94 <.debug_info+0x94>
      20:	61                   	popa   
      21:	74 69                	je     8c <.debug_info+0x8c>
      23:	63 5f 5c             	arpl   %bx,0x5c(%edi)
}