it-swarm.com.ru

Что означает "статический" в C?

Я видел слово static, используемое в разных местах кода C; это как статическая функция/класс в C # (где реализация разделяется между объектами)? 

941
David
  1. Статическая переменная внутри функции сохраняет свое значение между вызовами.
  2. Статическая глобальная переменная или функция «видна» только в файле, в котором она объявлена

(1) является более иностранной темой, если вы новичок, так что вот пример:

#include <stdio.h>

void foo()
{
    int a = 10;
    static int sa = 10;

    a += 5;
    sa += 5;

    printf("a = %d, sa = %d\n", a, sa);
}


int main()
{
    int i;

    for (i = 0; i < 10; ++i)
        foo();
}

Это печатает:

a = 15, sa = 15
a = 15, sa = 20
a = 15, sa = 25
a = 15, sa = 30
a = 15, sa = 35
a = 15, sa = 40
a = 15, sa = 45
a = 15, sa = 50
a = 15, sa = 55
a = 15, sa = 60

Это полезно в тех случаях, когда функция должна сохранять некоторое состояние между вызовами, и вы не хотите использовать глобальные переменные. Остерегайтесь, однако, эту функцию следует использовать очень экономно - она ​​делает ваш код не поточно-ориентированным и трудным для понимания.

(2) Широко используется как функция «контроля доступа». Если у вас есть файл .c, реализующий некоторые функции, он обычно предоставляет пользователям только несколько «публичных» функций. Остальные его функции должны быть сделаны static, чтобы пользователь не мог получить к ним доступ. Это инкапсуляция, хорошая практика.

Цитирование Википедия :

На языке программирования C, статический используется с глобальными переменными и функции, чтобы установить их область содержащий файл. В локальных переменных static используется для хранения переменной в статически выделенной памяти вместо автоматически выделенного объем памяти. Пока языка нет диктовать реализацию любого тип памяти, статически выделенный память обычно резервируется в данных сегмент программы при компиляции время, а автоматически выделенная память обычно реализован как транзитный стек вызовов.

Смотрите здесь и здесь для более подробной информации.

И чтобы ответить на ваш второй вопрос, это не так, как в C #.

Однако в C++ static также используется для определения атрибутов класса (общих для всех объектов одного класса) и методов. В Си нет классов, поэтому эта особенность неактуальна.

1328
Eli Bendersky

Здесь есть еще одно использование, которое не рассматривается, и оно является частью объявления типа массива в качестве аргумента функции:

int someFunction(char arg[static 10])
{
    ...
}

В этом контексте это указывает, что аргументы, передаваемые этой функции, должны быть массивом типа char, содержащим не менее 10 элементов. Для получения дополнительной информации см. Мой вопрос здесь .

209
dreamlax

Краткий ответ ... это зависит.

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

  2. Статические глобальные переменные не видны вне файла C, в котором они определены.

  3. Статические функции не видны вне файла C, в котором они определены.

149
cmcginty

Пример области действия нескольких файлов

Здесь я иллюстрирую, как static влияет на объем определений функций в нескольких файлах.

a.c

#include <stdio.h>

/*
Undefined behavior: already defined in main.
Binutils 2.24 gives an error and refuses to link.
https://stackoverflow.com/questions/27667277/why-does-borland-compile-with-multiple-definitions-of-same-object-in-different-c
*/
/*int i = 0;*/

/* Works in GCC as an extension: https://stackoverflow.com/a/3692486/895245 */
/*int i;*/

/* OK: extern. Will use the one in main. */
extern int i;

/* OK: only visible to this file. */
static int si = 0;

void a() {
    i++;
    si++;
    puts("a()");
    printf("i = %d\n", i);
    printf("si = %d\n", si);
    puts("");
}

main.c

#include <stdio.h>

int i = 0;
static int si = 0;

void a();    

void m() {
    i++;
    si++;
    puts("m()");
    printf("i = %d\n", i);
    printf("si = %d\n", si);
    puts("");
}

int main() {
    m();
    m();
    a();
    a();
    return 0;
}

GitHub вверх по течению .

Скомпилируйте и запустите:

gcc -c a.c -o a.o
gcc -c main.c -o main.o
gcc -o main main.o a.o

Результат:

m()
i = 1
si = 1

m()
i = 2
si = 2

a()
i = 3
si = 1

a()
i = 4
si = 2

Интерпретация

  • для si есть две отдельные переменные, по одной для каждого файла
  • есть одна переменная общего доступа для i

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

В программировании на C файлы часто используются для представления «классов», а переменные static представляют частные статические члены класса.

Что говорят об этом стандарты

C99 N1256 черновик 6.7.1 «Спецификаторы класса хранения» говорят, что static является «спецификатором класса хранения».

6.2.2/3 «Связи идентификаторов» говорит, что static подразумевает internal linkage:

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

и 6.2.2/2 говорит, что internal linkage ведет себя так, как в нашем примере:

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

где блок перевода - это исходный файл после предварительной обработки.

Как GCC реализует это для ELF (Linux)?

С привязкой STB_LOCAL.

Если мы скомпилируем:

int i = 0;
static int si = 0;

и разберите таблицу символов с помощью:

readelf -s main.o

вывод содержит:

Num:    Value          Size Type    Bind   Vis      Ndx Name
  5: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    4 si
 10: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    4 i

таким образом, связывание - единственное существенное различие между ними. Value - это просто их смещение в секции .bss, поэтому мы ожидаем, что оно будет другим.

STB_LOCAL задокументирован в спецификации ELF по адресу http://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.html :

STB_LOCAL Локальные символы не видны за пределами объектного файла, содержащего их определение. Локальные символы одного и того же имени могут существовать в нескольких файлах, не мешая друг другу

что делает его идеальным выбором для представления static.

Переменные без статики - это STB_GLOBAL, а в спецификации говорится:

Когда редактор ссылок объединяет несколько перемещаемых объектных файлов, он не позволяет использовать несколько определений символов STB_GLOBAL с одним и тем же именем. 

что согласуется с ошибками ссылок в нескольких нестатических определениях.

Если мы запускаем оптимизацию с помощью -O3, символ si полностью удаляется из таблицы символов: его нельзя использовать извне. TODO зачем вообще хранить статические переменные в таблице символов, когда нет оптимизации? Могут ли они быть использованы для чего-либо? Возможно для отладки.

Смотрите также

C++ анонимные пространства имен

В C++ вы можете захотеть использовать анонимные пространства имен вместо статических, что дает аналогичный эффект, но дополнительно скрывает определения типов: Безымянный/анонимные пространства имен против статических функций

Это зависит:

int foo()
{
   static int x;
   return ++x;
}

Функция вернет 1, 2, 3 и т.д. --- переменная не находится в стеке.

a.c:

static int foo()
{
}

Это означает, что эта функция имеет область действия только в этом файле. Таким образом, a.c и b.c могут иметь разные foo()s, и foo не предоставляется совместно используемым объектам. Поэтому, если вы определили foo в a.c, вы не сможете получить к нему доступ из b.c или из других мест.

В большинстве библиотек C все «частные» функции являются статическими, а большинство «открытых» - нет.

34
Artyom

Люди продолжают говорить, что «статический» в Си имеет два значения. Я предлагаю альтернативный способ просмотра, который дает единственное значение:

  • Применение «статического» к элементу заставляет этот элемент иметь два свойства: (a) он не виден за пределами текущей области; (б) он постоянен.

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

Например, рассмотрим переменные. Переменные, объявленные вне функций, уже имеют постоянство (в сегменте данных), поэтому применение «статического» может только сделать их невидимыми вне текущей области (модуля компиляции). Напротив, переменные, объявленные внутри функций, уже не видны вне текущей области (функции), поэтому применение «статического» может только сделать их постоянными.

Применение «статического» к функциям аналогично применению его к глобальным переменным - код обязательно является постоянным (по крайней мере, внутри языка), поэтому можно изменять только видимость.

ПРИМЕЧАНИЕ: эти комментарии относятся только к C. В C++ применение «static» к методам класса действительно дает ключевое слово в другом значении. Аналогично для расширения аргумента массива C99.

19
PMar

Из Википедии:

В языке программирования C static используется с глобальными переменными и функциями, чтобы установить их область действия для содержащегося файла. В локальных переменных static используется для хранения переменной в статически выделенной памяти вместо автоматически выделенной памяти. Хотя язык не диктует реализацию памяти любого типа, статически распределенная память обычно резервируется в сегменте данных программы во время компиляции, в то время как автоматически распределенная память обычно реализуется как временный стек вызовов.

12
OscarRyz

static означает разные вещи в разных контекстах.

  1. Вы можете объявить статическую переменную в функции C. Эта переменная видна только в функции, однако она ведет себя как глобальная в том смысле, что она инициализируется только один раз и сохраняет свое значение. В этом примере каждый раз, когда вы вызываете foo(), он будет печатать все большее число. Статическая переменная инициализируется только один раз.

    void foo ()
    {
    static int i = 0;
    printf("%d", i); i++
    }
    
  2. Другое использование статического - это когда вы реализуете функцию или глобальную переменную в файле .c, но не хотите, чтобы ее символ был виден за пределами .obj, сгенерированного файлом. например.

    static void foo() { ... }
    
12
m-sharp

Ненавижу отвечать на старый вопрос, но я не думаю, что кто-либо упоминал, как K & R объясняет это в разделе A4.1 «языка программирования Си».

Вкратце, статическое слово используется со значениями two:

  1. Статический - это один из двух классов хранения (другой - Автоматический). Статический объект сохраняет свое значение между вызовами. Объекты, объявленные вне всех блоков, всегда статичны и не могут быть сделаны автоматически.
  2. Но когда statickeyword (большое внимание уделяется его использованию в коде В качестве ключевого слова) используется с объявлением, это дает внутреннюю связь этого объекта, поэтому его можно использовать только внутри этого модуля перевода. Но если ключевое слово используется в функции, оно меняет класс хранения объекта (в любом случае объект будет виден только внутри этой функции). Противоположностью static является ключевое слово extern, которое дает внешнюю связь объекта.

Питер Ван Дер Линден дает следующие два значения в «Программировании Expert C»:

  • Внутри функции сохраняет свое значение между вызовами.
  • На уровне функций виден только в этом файле.
6
nobism

Если вы объявите переменную в статической функции, ее значение не будет сохранено в стеке вызовов функций и будет по-прежнему доступно при повторном вызове функции.

Если вы объявляете глобальную переменную статической, ее область действия будет ограничена тем файлом, в котором вы ее объявили. Это немного безопаснее, чем обычный глобальный, который может быть прочитан и изменен во всей вашей программе.

5
Sam Hoice

В Си static имеет два значения, в зависимости от области его использования. В глобальной области, когда объект объявляется на уровне файла, это означает, что этот объект виден только внутри этого файла.

В любой другой области он объявляет объект, который будет сохранять свое значение в разное время, когда вводится конкретная область. Например, если int искажено в процедуре:

void procedure(void)
{
   static int i = 0;

   i++;
}

значение 'i' инициализируется в ноль при первом вызове процедуры, и значение сохраняется при каждом последующем вызове процедуры. если бы «я» было напечатано, он вывел бы последовательность 0, 1, 2, 3, ...

5
Gary

Если вы объявите это в файле mytest.c:

static int my_variable;

Тогда эту переменную можно увидеть только из этого файла. Переменная не может быть экспортирована в другом месте.

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

Статическая функция не может быть экспортирована извне файла. Таким образом, в файле * .c вы скрываете функции и переменные, если объявляете их статическими.

4
ant2009

Важно отметить, что статические переменные в функциях инициализируются при первом входе в эту функцию и сохраняются даже после завершения их вызова; в случае рекурсивных функций статическая переменная инициализируется только один раз и сохраняется также во всех рекурсивных вызовах и даже после завершения вызова функции.

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

3
Starhowl

Статические переменные в C имеют время жизни программы.

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

Например:

void function()
{
    static int var = 1;
    var++;
    printf("%d", var);
}

int main()
{
    function(); // Call 1
    function(); // Call 2
}

В приведенной выше программе var хранится в сегменте данных. Его время жизни - вся программа на Си.

После вызова функции 1 var становится 2. После вызова функции 2 var становится 3.

Значение var не уничтожается между вызовами функций.

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

Инициализированные статические переменные хранятся в сегменте данных программы на Си, а неинициализированные - в сегменте BSS.

Еще одна информация о статических: если переменная является глобальной и статической, она имеет время жизни программы на Си, но имеет область видимости файла. Это видно только в этом файле.

Чтобы попробовать это:

file1.c

static int x;

int main()
{
    printf("Accessing in same file%d", x):
}

file2.c

    extern int x;
    func()
    {
        printf("accessing in different file %d",x); // Not allowed, x has the file scope of file1.c
    }

run gcc -c file1.c

gcc -c file2.c

Теперь попробуйте связать их, используя:

gcc -o output file1.o file2.o

Это даст ошибку компоновщика, так как x имеет область видимости файла file1.c, и компоновщик не сможет разрешить ссылку на переменную x, используемую в file2.c.

Рекомендации:

  1. http://en.wikipedia.org/wiki/Translation_unit_(programming)
  2. http://en.wikipedia.org/wiki/Call_stack
3
Sahil Manchanda

Статическая переменная - это специальная переменная, которую вы можете использовать в функции, она сохраняет данные между вызовами и не удаляет их между вызовами. Например:

void func(){
    static int count; // If you don't declare its value, the value automatically initializes to zero
    printf("%d, ", count);
    count++;
}

void main(){
    while(true){
        func();
    }
}

Результат:

0, 1, 2, 3, 4, 5, ...

2
Yagel

Значение статической переменной сохраняется между вызовами различных функций, и ее область действия ограничена локальным блоком Статическая переменная всегда инициализируется значением 0

1
Jonathon

Есть 2 случая:

(1) Объявлены локальные переменные static: размещены в сегменте данных вместо стека. Его значение сохраняется при повторном вызове функции.

(2) Объявлены глобальные переменные или функции static: Невидимая внешняя единица компиляции (т.е. это локальные символы в таблице символов во время компоновки).

1
Jonny Kong

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

#include<stdio.h> 
int counterFunction() 
{ 
  static int count = 0; 
  count++; 
  return count; 
} 

int main() 
{ 
  printf("First Counter Output = %d\n", counterFunction()); 
  printf("Second Counter Output = %d ", counterFunction()); 
  return 0; 
}

Выше программа даст нам такой вывод:
Выход первого счетчика = 1 
Выход второго счетчика = 1 
Потому что, как только мы вызовем функцию, она инициализирует count = 0. И пока мы выполняем counterFunction, она уничтожит переменную count.

0
Makdia Hussain