Що означає "статичний" в С?


1135

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



15
Яка причина видалити "в програмі С" з кінця назви @Lundin? Він є надлишковим у присутності тегу c , але він дозволяє мені швидше бачити категоризацію, не перевіряючи теги. Це надлишок дуже зручний, коли я дійшов до питання з напрямку, яке також може містити питання щодо інших мов, наприклад, статичного пошуку або пошуку в Google.
Палець

5
@Palec Існує політика SO, згідно з якою елементи, що знаходяться у списку тегів, є зайвими у заголовку. Сайт автоматично додає C до фактичного веб-сайту. Google для "C static" дає цю відповідь як найкращий удар. Причина, чому це було змінено, полягає в тому, що це питання зараз є частиною поширених питань мови SO C, а всі додані публікації трохи відполіруються.
Лундін

1
@Lundin Я вважаю за краще зберігати "C" у заголовку, тому що SO додає лише один тег до заголовка (найпоширеніший?). Що робити, якщо в якийсь день "синтаксис" стикається з більшою кількістю запитань, ніж на C (оскільки це мова на мові)? Я вважаю за краще використовувати явну поведінку :-) Редагувати: а, але є мета-питання, яке говорить інакше: meta.stackexchange.com/questions/19190/…
Ciro Santilli 冠状 病毒 审查 六四 事件 法轮功

1
Це пояснення, яке я знайшов у Quora. Однозначно варто прочитати!
nalzok

Відповіді:


1519
  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 використовується статичний з глобальними змінними та функціями, щоб встановити їх обсяг на файл, що містить. У локальних змінних статична використовується для зберігання змінної в статично виділеній пам'яті замість автоматично виділеної пам'яті. Хоча мова не диктує реалізацію будь-якого типу пам'яті, статично розподілена пам'ять, як правило, зарезервована в сегменті даних програми під час компіляції, тоді як автоматично виділена пам'ять зазвичай реалізується як перехідний стек викликів.

І щоб відповісти на ваше друге запитання, це не так, як у C #.

У C ++, однак, staticтакож використовується для визначення атрибутів класу (спільних між усіма об'єктами одного класу) та методів. У C класів немає, тому ця особливість не має значення.


179
Пакс, ОП не знає про статичне, тож ви пропонуєте занурити його в різницю між одиницями компіляції та файлами? :-)
Елі Бендерський

138
Блок компіляції - це один файл, який бачить компілятор. Ваш файл .c може включати інші файли .c, але після сортування препроцесора включає компілятор, зрештою, бачить лише один "компіляційний блок".
Елі Бендерський

81
@robUK: компілятор навіть не знає про .h файли - вони об'єднуються у файли .c попереднього процесора. Так, так, ви можете сказати, що .c файл із усіма заголовками, що входять до нього, є єдиним блоком компіляції.
Елі Бендерський

6
@TonyD, можливо, це бентежить, але так працює компіляція. Зазвичай це може бути один .cі безліч файлів заголовків, але чорт завжди в тому, що не є типовим.
петерф

7
@TonyD Компілятор робить компіляцію. Препроцесор здійснює попередню обробку. Виклик ланцюжка інструментів "компілятором" не змінює того, що він є, або що він робить.
Майлз Рут

231

Тут є ще одне використання, яке не охоплено тут, і це є частиною декларації типу масиву як аргументу функції:

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

У цьому контексті це вказує, що аргументи, передані цій функції, повинні мати тип масиву, що charмістить принаймні 10 елементів. Для отримання додаткової інформації дивіться моє запитання тут .


3
Я не думав, що C має аргументи масиву? Лінус Торвальдс гнівно регоче про людей, які роблять це.
suprjami

13
@jamieb: C не має аргументів масиву, але цей специфічний синтаксис означає, що функція очікує, arg[0]що arg[9]до має значення (що також означає, що функція не приймає нульовий покажчик). Компілятори можуть якось використовувати цю інформацію для оптимізації, а статичні аналізатори можуть використовувати цю інформацію, щоб гарантувати, що функція ніколи не має нульового вказівника (або, якщо це можливо, масив із меншою кількістю елементів, ніж зазначено).
dreamlax

19
@Qix - Це було нове перевантажене значення, яке надано staticв C99. Це вже більше півтора десятиліть, але не всі автори компілятора прийняли всі функції C99 - тому C99 в цілому багато в чому залишається невідомим.
Happy Green Kid Naps

@suprjami Я не на 100% впевнений, що ви маєте на увазі під "аргументами масиву" , але якщо ви маєте на увазі int arr[n];, то це VLA (масив змінної довжини) , який було додано в C99. Це ви мали на увазі?
RastaJedi

170

Коротка відповідь ... це залежить.

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

  2. Статичні глобальні змінні не видно за межами файлу C, в якому вони визначені.

  3. Статичні функції не видно поза файлом C, в якому вони визначені.


8
Тож "статична функція" і "приватна функція" означають одне і те ж? Так само "статичні глобальні змінні" та "приватні глобальні змінні" те саме?
користувач1599964

40
Йдеться про C. Немає приватного / публічного в C.
chris

19
@ user1599964 хоча його немає privateв C, ваша аналогія хороша: статичний робить речі "приватними" для заданого файлу. А файли на C часто позначаються на класах на C ++.
Ciro Santilli 冠状 病毒 审查 六四 事件 法轮功

66

Приклад багатофакторної змінної області застосування

Тут я проілюструю, як статичне впливає на область визначення функцій для декількох файлів.

змінного струму

#include <stdio.h>

/*
Undefined behavior: already defined in main.
Binutils 2.24 gives an error and refuses to link.
/programming/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змінні представляють приватні статичні члени класу.

Які стандарти про це говорять

У проекті 6.7.1 " Специфікатори класу зберігання" C99 N1256 говориться, що 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 ++ ви можете використовувати анонімні простори імен замість статичних, що досягає аналогічного ефекту, але додатково приховує визначення типів: Без назви / анонімні простори імен проти статичних функцій


39

Це залежить:

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

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

ac:

static int foo()
{
}

Це означає, що ця функція має область застосування лише в цьому файлі. Отже, ac і bc можуть мати різні foo()s, а foo не піддається впливу спільних об'єктів. Отже, якщо ви визначили foo in ac, ви не змогли отримати доступ до нього b.cз будь-якого іншого місця.

У більшості бібліотек С усі "приватні" функції є статичними, а більшість "публічних" - ні.


18
+1 для згадування x не у стеці чи купі. Це на просторі статичної пам'яті.
Gob00st

1
@ Простір статичної пам’яті Gob00st? ви мали на увазі "Сегмент даних" ...?
Yousha Aleayoub

24

Люди продовжують говорити, що "статичний" в С має два значення. Я пропоную альтернативний спосіб його перегляду, який надає йому єдиного значення:

  • Застосування "статичного" до елемента примушує цей предмет мати дві властивості: (а) він не видно за межами поточної області; (b) Це стійко.

Причина, як здається, має два значення в тому, що в C кожен елемент, до якого може бути застосований "статичний", вже має одне з цих двох властивостей , тому, здається , що саме таке використання включає лише інше.

Наприклад, розглянемо змінні. Змінні, оголошені поза функціями, вже мають стійкість (у сегменті даних), тому застосування "статичних" може лише зробити їх непомітними за межами поточної області (одиниця компіляції). Навпаки, змінні, задекларовані всередині функцій, вже не мають видимості поза поточною областю (функцією), тому застосування "статичного" може лише зробити їх стійкими.

Застосовувати "static" до функцій так само, як застосовувати його до глобальних змінних - код обов'язково є стійким (принаймні в межах мови), тому лише видимість може бути змінена.

ПРИМІТКА. Ці коментарі стосуються лише C. У програмі C ++ застосування "статичних" методів класів по-справжньому надає ключовому слову інше значення. Аналогічно і для розширення аргументу масиву C99.


Ваш (а) зайвий у кращому випадку. Жодна змінна, яка б не була видима за її межами. Це просто визначення обсягу. То, що ви маєте на увазі, називається зв'язок у Стандарті C. staticнадає внутрішній зв'язок ідентифікатору.
Єнс

16

З Вікіпедії:

У мові програмування на C використовується статичний з глобальними змінними та функціями, щоб встановити їх обсяг на файл, що містить. У локальних змінних статична використовується для зберігання змінної в статично виділеній пам'яті замість автоматично виділеної пам'яті. Хоча мова не диктує реалізацію будь-якого типу пам'яті, статично розподілена пам'ять, як правило, зарезервована у сегменті даних програми під час компіляції, тоді як автоматично виділена пам'ять зазвичай реалізується як перехідний стек викликів.


16

static означає різні речі в різних контекстах.

  1. Ви можете оголосити статичну змінну у функції C. Ця змінна видима лише у функції, проте вона поводиться як глобальна, оскільки вона ініціалізується лише один раз і зберігає своє значення. У цьому прикладі кожен раз, коли ви foo()його зателефонуєте, буде друкувати все більше число. Статична змінна ініціалізується лише один раз.

    void foo ()
    {
    static int i = 0;
    printf("%d", i); i++
    }
  2. Інше використання статики - це коли ви реалізуєте функцію або глобальну змінну у файлі .c, але не бажаєте, щоб її символ був видно поза .objгенерованим файлом. напр

    static void foo() { ... }

8

Якщо ви оголошуєте змінну в статичній функції, її значення не зберігатиметься у стеку функціональних викликів і все ще буде доступне при повторному виклику функції.

Якщо ви оголосите статичну глобальну змінну, її область буде обмежена у файлі, в якому ви її оголосили. Це трохи безпечніше, ніж звичайний глобальний, який можна прочитати та змінити у всій вашій програмі.


8

Я ненавиджу відповідати на старе запитання, але я не думаю, що ніхто не згадував, як K&R пояснює це в розділі A4.1 "Мова програмування на C".

Коротше кажучи, слово static використовується з двома значеннями:

  1. Статичний - один з двох класів зберігання (інший - автоматичний). Статичний об'єкт зберігає своє значення між викликами. Об'єкти, оголошені поза всіма блоками, завжди є статичними і не можуть бути автоматичними.
  2. Але коли static ключове слово (великий акцент на тому, що воно використовується в коді як ключове слово) використовується з декларацією, воно надає цьому об'єкту внутрішнє посилання, тому воно може використовуватися лише в межах одиниці перекладу. Але якщо ключове слово використовується у функції, воно змінює клас зберігання об’єкта (об’єкт буде видно лише в межах цієї функції). Протилежністю статичному є externключове слово, яке надає об'єкту зовнішню зв'язок.

Пітер Ван Дер Лінден дає два значення в програмі "Експерт C":

  • Всередині функції зберігає своє значення між дзвінками.
  • На рівні функції, видно лише в цьому файлі.

Там в третій клас зберігання, регістр . Деякі люди також складають справи для четвертого класу зберігання, призначеного для зберігання, поверненого malloc та друзями.
Єнс

@ Регістр @Jens - лише підказка для компілятора; зберігання реєстру не може бути застосоване з джерела C. Тому я б не вважав це класом зберігання.
GermanNerd

1
@GermanNerd Боюся НЕ згоден ISO C Standard з вашої точки зору, так як він явно робить registerна зберігання класу специфікатор (C99 6.7.1 зберігання класу специфікатор). І це більше, ніж просто натяк, наприклад, ви не можете застосувати адресу оператора &до об'єкта із класом зберігання registerнезалежно від того, виділяє компілятор реєстр чи ні.
Єнс

@ Дженс Дякую, що нагадали про &. Я, можливо, зробив занадто багато C ++ ..... Так чи інакше, хоча 'register' є специфікатором класу зберігання, насправді компілятор, ймовірно, створить той же машинний код для (марного) специфікатора 'auto', що і для 'register 'специфікатор. Тож єдине, що залишається - обмеження рівня вихідного коду - неможливість прийняти адресу. До речі, ця маленька дискусія привела мене до пошуку помилки в Netbeans; з мого останнього оновлення, він за замовчуванням застосовує ланцюжок інструментів g ++ у нових проектах C!
GermanNerd

6

У C статика має два значення, залежно від сфери її використання. У глобальному масштабі, коли об’єкт оголошується на рівні файлу, це означає, що цей об’єкт видно лише у цьому файлі.

У будь-якому іншому обсязі він оголошує об'єкт, який зберігатиме своє значення між різними періодами, коли вводиться конкретна область застосування. Наприклад, якщо в процедурі розміщено int:

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

   i++;
}

значення 'i' ініціалізується до нуля під час першого виклику процедури, і значення зберігається кожного наступного разу, коли процедура викликається. якби було надруковано 'я', воно виведе послідовність 0, 1, 2, 3, ...


5

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

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


5

Якщо ви декларуєте це у mytest.cфайлі:

static int my_variable;

Тоді цю змінну можна побачити лише з цього файлу. Змінна не може бути експортована більше ніде.

Якщо ви декларуєте всередині функції, значення змінної буде зберігати її значення кожного разу, коли функція викликається.

Статичну функцію неможливо експортувати ззовні файлу. Отже, у *.cфайлі ви ховаєте функції та змінні, якщо оголошуєте їх статичними.


4

Статичні змінні в C мають тривалість роботи програми.

Якщо вони визначені у функції, вони мають локальну область, тобто доступ до них можна отримати лише всередині цих функцій. Значення статичних змінних зберігається між викликами функцій.

Наприклад:

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

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

У вищеописаній програмі varзберігається в сегменті даних. Її термін експлуатації - вся програма C.

Після виклику функції 1 varстає 2. Після виклику функції 2 varстає 3.

Значення varне знищується між викликами функцій.

Якби varбуло між нестатичною та локальною змінною, вона зберігалася б у сегменті стека програми C. Оскільки кадр стека функції знищується після повернення функції, значення varтакож знищується.

Ініціалізовані статичні змінні зберігаються у сегменті даних програми C, тоді як неініціалізовані - у сегменті BSS.

Ще одна інформація про статичну: Якщо змінна є глобальною та статичною, вона має час роботи програми C, але має область файлу. Це видно лише в тому файлі.

Щоб спробувати це:

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

Я розумію, що дані є стійкими, це означає, що вони не втрачаються після кожного виклику функції, але чому не static int var = 1;змінюється значення кожного разу на кожне
Eames

3

Статична змінна - це спеціальна змінна, яку ви можете використовувати у функції, і вона зберігає дані між дзвінками, і не видаляє їх між дзвінками. Наприклад:

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, ...


Ви можете замінити printf("%d, ", count); count++;на `printf ("% d ", count ++) (неважливо: P).
RastaJedi

2

Значення статичної змінної зберігається між різними викликами функцій, і її область обмежується локальним блоком, статичний var завжди ініціалізується зі значенням 0


2

Є 2 випадки:

(1) Задекларовані локальні змінні static: Виділено в сегменті даних замість стека. Його значення зберігається при повторному виклику функції.

(2) Задекларовані глобальні змінні чи функції static: Невидимий зовнішній компіляційний блок (тобто є локальними символами в таблиці символів під час посилання).


1

Статичні змінні мають властивість зберігати своє значення навіть після того, як вони не виходять за межі їх дії. Отже, статичні змінні зберігають своє попереднє значення в попередньому обсязі і знову не ініціалізуються в новій області.

Подивіться на це, наприклад - статична змінна int залишається в пам'яті під час роботи програми. Нормальна або автоматична змінна знищується, коли виклик функції, де оголошена змінна, закінчується.

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

int main() 
{ 
  printf("%d ", fun()); 
  printf("%d ", fun()); 
  return 0; 
}

Вийде: 1 2

Як 1 залишається в пам'яті, як це було оголошено статичним

Статичні змінні (як глобальні змінні) ініціалізуються як 0, якщо не ініціалізуються явно. Наприклад, у нижченаведеній програмі значення x друкується як 0, тоді як значення y - це щось сміття. Дивіться це для більш детальної інформації.

#include <stdio.h> 
int main() 
{ 
    static int x; 
    int y; 
    printf("%d \n %d", x, y); 
}

Це виведе: 0 [some_garbage_value]

Це основні, з яких я виявив, що не були пояснені вище для новачка!


-1

У програмуванні на 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; 
}

Наведена вище програма дасть нам такий результат:

First Counter Output = 1 
Second Counter Output = 1 

Тому що, як тільки ми викликаємо функцію, вона ініціалізує count = 0. І поки ми виконуємо його, counterFunctionвін знищить змінну count.


2
> Наведена вище програма дасть нам такий вихід: Перший вихідний вивід = 1 Другий вихідний вивід = 1 <Не вірно. Статичні змінні ініціалізуються лише один раз. Тож вихід буде 1, потім 2 тощо.
GermanNerd
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.