Я бачив слово, яке static
вживається в різних місцях у коді С; це як статична функція / клас у C # (де реалізація поділяється між об'єктами)?
Я бачив слово, яке static
вживається в різних місцях у коді С; це як статична функція / клас у C # (де реалізація поділяється між об'єктами)?
Відповіді:
(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 класів немає, тому ця особливість не має значення.
.c
і безліч файлів заголовків, але чорт завжди в тому, що не є типовим.
Тут є ще одне використання, яке не охоплено тут, і це є частиною декларації типу масиву як аргументу функції:
int someFunction(char arg[static 10])
{
...
}
У цьому контексті це вказує, що аргументи, передані цій функції, повинні мати тип масиву, що char
містить принаймні 10 елементів. Для отримання додаткової інформації дивіться моє запитання тут .
arg[0]
що arg[9]
до має значення (що також означає, що функція не приймає нульовий покажчик). Компілятори можуть якось використовувати цю інформацію для оптимізації, а статичні аналізатори можуть використовувати цю інформацію, щоб гарантувати, що функція ніколи не має нульового вказівника (або, якщо це можливо, масив із меншою кількістю елементів, ніж зазначено).
static
в C99. Це вже більше півтора десятиліть, але не всі автори компілятора прийняли всі функції C99 - тому C99 в цілому багато в чому залишається невідомим.
int arr[n];
, то це VLA (масив змінної довжини) , який було додано в C99. Це ви мали на увазі?
Коротка відповідь ... це залежить.
Статичні локальні змінні не втрачають значення між викликами функцій. Іншими словами, вони є глобальними змінними, але припадають на локальну функцію, в якій вони визначені.
Статичні глобальні змінні не видно за межами файлу C, в якому вони визначені.
Статичні функції не видно поза файлом C, в якому вони визначені.
private
в C, ваша аналогія хороша: статичний робить речі "приватними" для заданого файлу. А файли на C часто позначаються на класах на C ++.
Приклад багатофакторної змінної області застосування
Тут я проілюструю, як статичне впливає на область визначення функцій для декількох файлів.
змінного струму
#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;
}
Складіть і запустіть:
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, чому взагалі зберігати статичні змінні в таблиці символів, коли немає оптимізації? Чи можна їх використовувати для чого-небудь? Можливо для налагодження.
Дивись також
static
функцій: https://stackoverflow.com/a/30319812/895245static
з extern
, що робить "навпаки": Як я можу використовувати extern для обміну змінними між вихідними файлами?C ++ анонімні простори імен
У C ++ ви можете використовувати анонімні простори імен замість статичних, що досягає аналогічного ефекту, але додатково приховує визначення типів: Без назви / анонімні простори імен проти статичних функцій
Це залежить:
int foo()
{
static int x;
return ++x;
}
Функція повертає 1, 2, 3 і т.д. --- змінна не знаходиться в стеці.
static int foo()
{
}
Це означає, що ця функція має область застосування лише в цьому файлі. Отже, ac і bc можуть мати різні foo()
s, а foo не піддається впливу спільних об'єктів. Отже, якщо ви визначили foo in ac, ви не змогли отримати доступ до нього b.c
з будь-якого іншого місця.
У більшості бібліотек С усі "приватні" функції є статичними, а більшість "публічних" - ні.
Люди продовжують говорити, що "статичний" в С має два значення. Я пропоную альтернативний спосіб його перегляду, який надає йому єдиного значення:
Причина, як здається, має два значення в тому, що в C кожен елемент, до якого може бути застосований "статичний", вже має одне з цих двох властивостей , тому, здається , що саме таке використання включає лише інше.
Наприклад, розглянемо змінні. Змінні, оголошені поза функціями, вже мають стійкість (у сегменті даних), тому застосування "статичних" може лише зробити їх непомітними за межами поточної області (одиниця компіляції). Навпаки, змінні, задекларовані всередині функцій, вже не мають видимості поза поточною областю (функцією), тому застосування "статичного" може лише зробити їх стійкими.
Застосовувати "static" до функцій так само, як застосовувати його до глобальних змінних - код обов'язково є стійким (принаймні в межах мови), тому лише видимість може бути змінена.
ПРИМІТКА. Ці коментарі стосуються лише C. У програмі C ++ застосування "статичних" методів класів по-справжньому надає ключовому слову інше значення. Аналогічно і для розширення аргументу масиву C99.
static
надає внутрішній зв'язок ідентифікатору.
З Вікіпедії:
У мові програмування на C використовується статичний з глобальними змінними та функціями, щоб встановити їх обсяг на файл, що містить. У локальних змінних статична використовується для зберігання змінної в статично виділеній пам'яті замість автоматично виділеної пам'яті. Хоча мова не диктує реалізацію будь-якого типу пам'яті, статично розподілена пам'ять, як правило, зарезервована у сегменті даних програми під час компіляції, тоді як автоматично виділена пам'ять зазвичай реалізується як перехідний стек викликів.
static
означає різні речі в різних контекстах.
Ви можете оголосити статичну змінну у функції C. Ця змінна видима лише у функції, проте вона поводиться як глобальна, оскільки вона ініціалізується лише один раз і зберігає своє значення. У цьому прикладі кожен раз, коли ви foo()
його зателефонуєте, буде друкувати все більше число. Статична змінна ініціалізується лише один раз.
void foo ()
{
static int i = 0;
printf("%d", i); i++
}
Інше використання статики - це коли ви реалізуєте функцію або глобальну змінну у файлі .c, але не бажаєте, щоб її символ був видно поза .obj
генерованим файлом. напр
static void foo() { ... }
Якщо ви оголошуєте змінну в статичній функції, її значення не зберігатиметься у стеку функціональних викликів і все ще буде доступне при повторному виклику функції.
Якщо ви оголосите статичну глобальну змінну, її область буде обмежена у файлі, в якому ви її оголосили. Це трохи безпечніше, ніж звичайний глобальний, який можна прочитати та змінити у всій вашій програмі.
Я ненавиджу відповідати на старе запитання, але я не думаю, що ніхто не згадував, як K&R пояснює це в розділі A4.1 "Мова програмування на C".
Коротше кажучи, слово static використовується з двома значеннями:
static
ключове слово (великий акцент на тому, що воно використовується в коді як ключове слово) використовується з декларацією, воно надає цьому об'єкту внутрішнє посилання, тому воно може використовуватися лише в межах одиниці перекладу. Але якщо ключове слово використовується у функції, воно змінює клас зберігання об’єкта (об’єкт буде видно лише в межах цієї функції). Протилежністю статичному є extern
ключове слово, яке надає об'єкту зовнішню зв'язок.Пітер Ван Дер Лінден дає два значення в програмі "Експерт C":
register
на зберігання класу специфікатор (C99 6.7.1 зберігання класу специфікатор). І це більше, ніж просто натяк, наприклад, ви не можете застосувати адресу оператора &
до об'єкта із класом зберігання register
незалежно від того, виділяє компілятор реєстр чи ні.
У C статика має два значення, залежно від сфери її використання. У глобальному масштабі, коли об’єкт оголошується на рівні файлу, це означає, що цей об’єкт видно лише у цьому файлі.
У будь-якому іншому обсязі він оголошує об'єкт, який зберігатиме своє значення між різними періодами, коли вводиться конкретна область застосування. Наприклад, якщо в процедурі розміщено int:
void procedure(void)
{
static int i = 0;
i++;
}
значення 'i' ініціалізується до нуля під час першого виклику процедури, і значення зберігається кожного наступного разу, коли процедура викликається. якби було надруковано 'я', воно виведе послідовність 0, 1, 2, 3, ...
Важливо зазначити, що статичні змінні у функціях ініціалізуються при першому введенні в цю функцію та зберігаються навіть після завершення їх виклику; у випадку рекурсивних функцій статична змінна стає ініціалізованою лише один раз і зберігається також під час усіх рекурсивних викликів і навіть після завершення виклику функції.
Якщо змінна була створена поза функцією, це означає, що програміст може використовувати змінну лише у вихідному файлі, про яку була оголошена змінна.
Якщо ви декларуєте це у mytest.c
файлі:
static int my_variable;
Тоді цю змінну можна побачити лише з цього файлу. Змінна не може бути експортована більше ніде.
Якщо ви декларуєте всередині функції, значення змінної буде зберігати її значення кожного разу, коли функція викликається.
Статичну функцію неможливо експортувати ззовні файлу. Отже, у *.c
файлі ви ховаєте функції та змінні, якщо оголошуєте їх статичними.
Статичні змінні в 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, але має область файлу. Це видно лише в тому файлі.
Щоб спробувати це:
static int x;
int main()
{
printf("Accessing in same file%d", x):
}
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.
Список літератури:
static int var = 1;
змінюється значення кожного разу на кожне
Статична змінна - це спеціальна змінна, яку ви можете використовувати у функції, і вона зберігає дані між дзвінками, і не видаляє їх між дзвінками. Наприклад:
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).
Є 2 випадки:
(1) Задекларовані локальні змінні static
: Виділено в сегменті даних замість стека. Його значення зберігається при повторному виклику функції.
(2) Задекларовані глобальні змінні чи функції static
: Невидимий зовнішній компіляційний блок (тобто є локальними символами в таблиці символів під час посилання).
Статичні змінні мають властивість зберігати своє значення навіть після того, як вони не виходять за межі їх дії. Отже, статичні змінні зберігають своє попереднє значення в попередньому обсязі і знову не ініціалізуються в новій області.
Подивіться на це, наприклад - статична змінна 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]
Це основні, з яких я виявив, що не були пояснені вище для новачка!
У програмуванні на 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.