Co dzieje się z zadeklarowaną, niezainicjalizowaną zmienną w C? Czy to ma jakąś wartość?
Jeśli w C napiszę:
int num;
Zanim przypisam cokolwiek do num
, czy wartość num
jest nieokreślona?
10 answers
Zmienne statyczne (zakres pliku i funkcja statyczna) są inicjalizowane na zero:
int x; // zero
int y = 0; // also zero
void foo() {
static int x; // also zero
}
Zmienne Niestatyczne (zmienne lokalne) są nieokreślone . Odczytanie ich przed przypisaniem wartości skutkuje niezdefiniowanym zachowaniem.
void foo() {
int x;
printf("%d", x); // the compiler is free to crash here
}
W praktyce mają tendencję do po prostu mieć jakąś nonsensowną wartość w tam początkowo - niektóre Kompilatory mogą nawet umieścić w określonych, stałych wartości, aby to oczywiste, gdy patrząc w debuggerze - ale ściśle mówiąc, kompilator jest wolny, aby zrobić wszystko od rozbijania się do przywoływania demonów przez wasze nosy.
Jeśli chodzi o to, dlaczego jest to nieokreślone zachowanie zamiast po prostu "niezdefiniowana / dowolna wartość", istnieje wiele architektur CPU, które mają dodatkowe bity znaczników w swojej reprezentacji dla różnych typów. Współczesnym przykładem może być Itanium, który ma bit " nic " w swoich rejestrach ; oczywiście, Projektory w standardzie C rozważały niektóre starsze architektury.
[[2]}Próba pracy z wartość z ustawionymi bitami znaczników może spowodować wyjątek procesora w operacji, która naprawdę nie powinna zawieść (np. dodanie liczby całkowitej lub przypisanie do innej zmiennej). A jeśli odejdziesz i zostawisz zmienną niezinicjalizowaną, kompilator może wychwycić jakieś losowe śmieci z ustawionymi bitami flagi - co oznacza, że dotknięcie tej niezinicjalizowanej zmiennej może być śmiertelne.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-21 05:40:28
0 jeśli statyczny lub globalny, nieokreślony, jeśli klasa pamięci jest auto
C zawsze bardzo dokładnie określał początkowe wartości obiektów. Jeśli globalny lub static
, zostaną wyzerowane. Jeśli auto
, wartość jest nieokreślona .
Tak było w kompilatorach pre-C89 i tak zostało określone przez K & R oraz w oryginalnym raporcie DMR C.
Tak było w C89, patrz sekcja6.5.7 Inicjalizacja .
Jeśli obiekt, który ma automatyczne przechowywanie czas trwania nie jest inicjowany explicitely, its value is nieokreślony. Jeśli obiekt, który ma statyczny czas przechowywania nie jest zainicjowany, jest inicjowane implicially jak gdyby każdy członkiem, który ma typ arytmetyczny były przypisane 0 i każdy członek, który ma typowi wskaźnika przypisano null stała wskaźnika.
Tak było w C99, patrz sekcja 6.7.8 Inicjalizacja .
Jeśli obiekt, który ma automatyczne czas przechowywania wynosi nie zainicjowany jawnie, jego wartość jest nieokreślony. Jeśli obiekt, który ma statyczny czas przechowywania nie jest zainicjalizowana jawnie, wtedy:
- jeśli posiada typ wskaźnika, jest inicjowany na wskaźnik null;
- jeśli ma arytmetykę type, it is initialized to (positive or unsigned) zero;
- jeśli jest zbiorcze, każdy członek jest inicjowany (rekurencyjnie) zgodnie z tymi rules;
- jeśli jest to związek, pierwszy nazwa członka jest inicjalizowana (rekurencyjnie) zgodnie z tymi Zasady.
Co dokładnie nieokreślony oznacza, nie jestem pewien Dla C89, C99 mówi:
3.17.2
wartość nieokreślona
albo nieokreślona wartość albo pułapka reprezentacja
Ale niezależnie od tego, co mówią standardy, w prawdziwym życiu, każda strona stosu faktycznie zaczyna się od zera, ale kiedy twój program patrzy na dowolną wartość klasy pamięci auto
, widzi to, co zostało pozostawione przez twój własny program ostatnio używałem tych adresów. Jeśli przydzielisz wiele tablic auto
, zobaczysz, że w końcu zaczną się porządnie od zer.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:19
Zależy to od czasu przechowywania zmiennej. Zmienna o statycznym czasie przechowywania jest zawsze domyślnie inicjalizowana przez zero.
Jeśli chodzi o zmienne automatyczne (lokalne), zmienna niezainicjalizowana ma wartość nieokreśloną . Nieokreślona wartość, między innymi, oznacza, że niezależnie od " wartości "można" zobaczyć " w tej zmiennej jest nie tylko nieprzewidywalne, to nawet nie jest gwarantowana stabilny . Przykładowo, w praktyce (tj. ignorując UB przez chwilę) to kod
int num;
int a = num;
int b = num;
Nie gwarantuje, że zmienne a
i b
otrzymają identyczne wartości. Co ciekawe, nie jest to jakaś pedantyczna koncepcja teoretyczna, łatwo dzieje się to w praktyce jako konsekwencja optymalizacji.
Ogólnie więc, popularna odpowiedź, że "jest zainicjowana tym, co było w pamięci", nie jest nawet w najmniejszym stopniu poprawna. Uninitialized zachowanie zmiennej różni się od zachowania zmiennej zainicjalizowanej ze śmieciami.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-22 18:27:38
Ubuntu 15.10, Kernel 4.2.0, x86-64, GCC 5.2.1 przykład
Dość standardów, spójrzmy na implementację: -)
Zmienna lokalna
Standardy: nieokreślone zachowanie.
Implementacja: program przydziela przestrzeń stosu i nigdy nie przenosi niczego na ten adres, więc to, co było wcześniej, jest używane.
#include <stdio.h>
int main() {
int i;
printf("%d\n", i);
}
Kompiluj z:
gcc -O0 -std=c99 a.c
Wyjścia:
0
I dekompile z:
objdump -dr a.out
Do:
0000000000400536 <main>:
400536: 55 push %rbp
400537: 48 89 e5 mov %rsp,%rbp
40053a: 48 83 ec 10 sub $0x10,%rsp
40053e: 8b 45 fc mov -0x4(%rbp),%eax
400541: 89 c6 mov %eax,%esi
400543: bf e4 05 40 00 mov $0x4005e4,%edi
400548: b8 00 00 00 00 mov $0x0,%eax
40054d: e8 be fe ff ff callq 400410 <printf@plt>
400552: b8 00 00 00 00 mov $0x0,%eax
400557: c9 leaveq
400558: c3 retq
Z naszej wiedzy o x86-64 wywołujących konwencje:
%rdi
jest pierwszym argumentem printf, a więc łańcuch"%d\n"
pod adresem0x4005e4
-
%rsi
jest drugim argumentem printf, zatemi
.Pochodzi od
-0x4(%rbp)
, która jest pierwszą 4-bajtową zmienną lokalną.W tym momencie,
rbp
jest na pierwszej stronie stosu został przydzielony przez jądro, więc aby zrozumieć, że wartość chcielibyśmy zajrzeć do kodu jądra i dowiedzieć się na co to ustawia.TODO czy jądro ustawia tę pamięć na coś przed ponownym użyciem jej dla innych procesów, gdy proces umiera? Jeśli nie, nowy proces byłby w stanie odczytać pamięć innych gotowych programów, wyciekając dane. Zobacz: czy niezinicjalizowane wartości są zagrożeniem bezpieczeństwa?
Możemy wtedy również grać z własnymi modyfikacjami stosu i pisać zabawne rzeczy, takie jak:]}
#include <assert.h>
int f() {
int i = 13;
return i;
}
int g() {
int i;
return i;
}
int main() {
f();
assert(g() == 13);
}
Global zmienne
Standardy: 0
Realizacja: .bss
sekcja.
#include <stdio.h>
int i;
int main() {
printf("%d\n", i);
}
gcc -00 -std=c99 a.c
Kompiluje do:
0000000000400536 <main>:
400536: 55 push %rbp
400537: 48 89 e5 mov %rsp,%rbp
40053a: 8b 05 04 0b 20 00 mov 0x200b04(%rip),%eax # 601044 <i>
400540: 89 c6 mov %eax,%esi
400542: bf e4 05 40 00 mov $0x4005e4,%edi
400547: b8 00 00 00 00 mov $0x0,%eax
40054c: e8 bf fe ff ff callq 400410 <printf@plt>
400551: b8 00 00 00 00 mov $0x0,%eax
400556: 5d pop %rbp
400557: c3 retq
400558: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
40055f: 00
# 601044 <i>
mówi, że i
jest pod adresem 0x601044
i:
readelf -SW a.out
Zawiera:
[25] .bss NOBITS 0000000000601040 001040 000008 00 WA 0 0 4
, który mówi 0x601044
znajduje się w środku .bss
sekcji, która zaczyna się od 0x601040
i ma długość 8 bajtów.
Standard ELF gwarantuje, że sekcja o nazwie .bss
jest całkowicie wypełniona zerami:
.bss
Ta sekcja zawiera niezainicjalizowane dane, które przyczyniają się do obraz pamięci programu. Z definicji system inicjuje dane z zerami, gdy program zaczyna działać. Sekcja occu- Pie nie ma miejsca na pliki, jak wskazuje typ sekcji,SHT_NOBITS
.
Ponadto Typ SHT_NOBITS
jest wydajny i nie zajmuje miejsca na pliku wykonywalnym:
sh_size
ten element podaje Rozmiar sekcji w bajtach. Chyba że sec- Typ tion toSHT_NOBITS
, sekcja zajmujesh_size
bajtów w pliku. Sekcja typuSHT_NOBITS
może mieć niezerową rozmiar, ale nie zajmuje miejsca w pliku.
Wtedy to do jądra Linuksa należy zerowanie tego regionu pamięci podczas ładowania programu do pamięci po uruchomieniu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:16
To zależy. Jeśli ta definicja jest globalna (poza jakąkolwiek Funkcją), to num
zostanie zainicjalizowana na zero. Jeśli jest lokalna (wewnątrz funkcji), to jej wartość jest nieokreślona. Teoretycznie, nawet próba odczytania wartości Ma nieokreślone zachowanie -- C pozwala na możliwość bitów, które nie przyczyniają się do wartości, ale muszą być ustawione w określony sposób, aby uzyskać zdefiniowane wyniki z odczytu zmiennej.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-20 21:28:39
Podstawowa odpowiedź brzmi: tak, jest niezdefiniowana.
Jeśli widzisz dziwne zachowanie z tego powodu, może zależeć od tego, gdzie jest zadeklarowane. Jeśli wewnątrz funkcji na stosie, zawartość będzie bardziej niż prawdopodobne, że będzie inna za każdym razem, gdy funkcja zostanie wywołana. Jeśli jest to zakres statyczny lub modułowy, jest niezdefiniowany, ale nie ulegnie zmianie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-20 21:30:14
Jeśli Klasa storage jest statyczna lub globalna, to podczas ładowania BSS inicjalizuje zmienną lub lokalizację pamięci(ML) na 0, chyba że zmiennej wstępnie przypisano jakąś wartość. W przypadku zmiennych lokalnych niezainicjalizowanych reprezentacja pułapki jest przypisana do lokalizacji pamięci. Więc jeśli któryś z Twoich rejestrów zawierających ważne informacje zostanie nadpisany przez kompilator, program może się zawiesić.
Ale niektóre Kompilatory mogą mieć mechanizm pozwalający uniknąć takiego problemu.
Pracowałam z seria nec v850, kiedy zdałem sobie sprawę, że istnieje reprezentacja pułapki, która ma wzorce bitowe, które reprezentują niezdefiniowane wartości dla typów danych z wyjątkiem char. Kiedy wziąłem niezaliczalny znak, otrzymałem zero domyślnej wartości ze względu na reprezentację pułapki. Może to być przydatne dla any1 przy użyciu necv850es
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-30 10:29:53
Ponieważ komputery mają ograniczoną pojemność pamięci masowej, zmienne automatyczne są zazwyczaj przechowywane w elementach pamięci (rejestrach lub pamięci RAM), które wcześniej były używane do innych dowolnych celów. Jeśli taka zmienna zostanie użyta przed przypisaniem do niej wartości, magazyn może przechowywać to, co przechowywała wcześniej, a więc zawartość zmiennej będzie nieprzewidywalna.
Jako dodatkowy błąd, wiele kompilatorów może przechowywać zmienne w rejestrach, które są większe niż powiązane typy. Chociaż kompilator musiałby zapewnić, że każda wartość, która jest zapisana do zmiennej i odczytana z powrotem, zostanie obcięta i / lub rozszerzona do jej właściwego rozmiaru, Wiele kompilatorów wykona takie obcinanie, gdy zmienne są zapisywane i oczekuje, że zostanie ono wykonane przed odczytaniem zmiennej. Na takich kompilatorach coś w stylu:
uint16_t hey(uint32_t x, uint32_t mode)
{ uint16_t q;
if (mode==1) q=2;
if (mode==3) q=4;
return q; }
uint32_t wow(uint32_t mode) {
return hey(1234567, mode);
}
Może bardzo dobrze spowodować wow()
zapisanie wartości 1234567 w rejestrach
Odpowiednio 0 i 1 oraz wywołanie foo()
. Ponieważ {[4] }nie jest potrzebna wewnątrz
"foo", a ponieważ funkcje mają umieszczać wartość zwracaną na
rejestr 0, kompilator może przydzielić rejestr 0 do q
. Jeżeli mode
jest 1 lub
3, rejestr 0 zostanie załadowany odpowiednio 2 lub 4, ale jeśli jest jakiś
inną wartość, funkcja może zwrócić to, co było w rejestrze 0 (tzn.
wartość 1234567), nawet jeśli wartość ta nie mieści się w zakresie uint16_t.
Aby uniknąć konieczności wykonywania dodatkowej pracy przez kompilatory, aby zapewnić, że niewtajemniczeni zmienne nigdy nie przechowują wartości poza swoją domeną i unikają konieczności aby określić nieokreślone zachowania w nadmiernych szczegółach, Standard mówi użycie niezainicjowanych zmiennych automatycznych jest niezdefiniowanym zachowaniem. W w niektórych przypadkach konsekwencje tego mogą być nawet bardziej zaskakujące niż wartość jest poza zakresem swojego typu. Na przykład:
void moo(int mode)
{
if (mode < 5)
launch_nukes();
hey(0, mode);
}
Kompilator może to wywnioskować, ponieważ wywołanie moo()
z trybem, który jest
większa niż 3 nieuchronnie prowadzić do programu wywołującego Undefined
Zachowania, kompilator może pominąć dowolny kod, który byłby tylko istotny
Jeśli mode
jest 4 lub większa, np. kod, który normalnie uniemożliwia
uruchomienie Atomówek w takich przypadkach. Zauważ, że ani Standard, ani
współczesnej filozofii kompilatora, zależy by na tym, że wartość zwracana
z "Hej" jest ignorowany-akt próby zwrócenia daje kompilator
nieograniczona licencja na generowanie dowolnego kodu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-06 15:28:36
Wartość num będzie jakąś wartością śmieci z pamięci głównej (RAM). lepiej, jeśli zainicjujesz zmienną zaraz po utworzeniu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-25 11:57:22
O ile przeszedłem to zależy głównie od kompilatora, ale ogólnie w większości przypadków wartość jest wstępnie przyjmowana jako 0 przez kompilatory.
Mam wartość śmieci w przypadku VC++ podczas gdy TC dał wartość jako 0.
Drukuję jak poniżej
int i;
printf('%d',i);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-27 19:34:39