İsimler, binding, lifetime ve scope (static vs dynamic)
Değişkenin altı niteliği, binding time tablosu, dört storage kategorisi, scope ile lifetime farkı ve static/dynamic scope altında program çıktısı izleme.
Bu bölümün iki yüzü var. Kısa cevaplı kısım: binding time tablosu ve storage kategorileri, ezberle biter. Uzun kısım: static ve dynamic scope altında program çıktısı. Bu soru vizede neredeyse kesin çıkar ve tablo kurarak çözülür.
Altın kurallar:
- Static scope = sayfaya bak. Değişken yerel değilse fonksiyonun yazıldığı yeri çevreleyen birimde aranır.
- Dynamic scope = davete bak. Değişken yerel değilse çağıran fonksiyonda, sonra onu çağıranda aranır.
- Nonlocal bir değişkene atama yapılıyorsa, bulduğun değişken değişir. Çağırana döndüğünde yeni değeri görürsün.
- Scope ≠ lifetime. Görünürlük metinle, ömür çalışmayla ilgili.
Değişkenin altı niteliği
| Nitelik | Anlamı |
|---|---|
| Name | Değişkeni anan isim. Tasarım soruları: büyük/küçük harf duyarlı mı, uzunluk sınırı, reserved word’ler |
| Address | Bellekteki yeri, l-value. Aynı isim farklı zamanlarda farklı adreste olabilir (özyineleme) |
| Value | Hücredeki içerik, r-value |
| Type | Değer aralığı ve yapılabilecek işlemler |
| Lifetime | Hücreye bağlı kaldığı süre |
| Scope | Görünür olduğu ifadeler aralığı |
İki isim aynı adrese erişiyorsa bunlar alias’tır. Pointer’lar, reference’lar ve C’deki union’lar alias yaratır. Okuyan kişi bir değişkenin değerinin başka bir isimle değiştirildiğini fark etmeyebilir: reliability düşer.
Binding ve binding time
Binding, bir varlık ile bir nitelik arasındaki ilişki: değişken ile tipi, isim ile adresi, sembol ile işlemi. Binding time bu ilişkinin kurulduğu an.
| Binding time | Örnek |
|---|---|
| Language design time | * sembolünü çarpmaya bağlamak, while’ın anlamı |
| Language implementation time | C’de int’in boyu ve aralığı, floating-point temsili |
| Compile time | C veya Java’da değişkeni tipine bağlamak |
| Link time | Kütüphane fonksiyonu çağrısını fonksiyonun koduna bağlamak |
| Load time | C global veya static değişkeni bellek hücresine bağlamak |
| Run time | Static olmayan yerel değişkeni hücreye bağlamak; değişkene değer bağlamak |
Static binding çalışmadan önce olur ve çalışma boyunca değişmez. Dynamic binding çalışma sırasında olur ya da değişebilir.
Type binding
- Explicit declaration:
int count; - Implicit declaration: isim kuralına göre. Fortran’da I-N ile başlayan isim integer, diğerleri real. Perl’de
$ile başlayan skaler,@dizi. - Type inference: tip bağlamdan çıkarılır. C#‘ta
var sum = 0;, ML ve Haskell’de neredeyse her şey. - Dynamic type binding: tip atama anında bağlanır (Python, JavaScript, Ruby, PHP).
let t = 5;
t = "five";
console.log(typeof t);
string
Dynamic type binding esneklik verir (aynı kod farklı tiplerle çalışır) ama derleyici tip hatalarını yakalayamaz ve çalışma zamanında tip kontrolü yapmak pahalıdır; bu dillerin çoğu yorumlanır.
Lifetime ve storage kategorileri
Hücreyi almaya allocation, geri vermeye deallocation denir. Lifetime, ikisi arasındaki süre.
| Kategori | Ne zaman bağlanır | Örnek | Artı / eksi |
|---|---|---|---|
| Static | Çalışmadan önce, program bitene kadar | C global, C static yerel, Java static alan | Hızlı, doğrudan adresleme, geçmişi hatırlar / özyineleme yok |
| Stack-dynamic | Bildirim işlendiğinde (elaboration), tip statik | C/Java metot yerelleri | Özyineleme, bellek paylaşımı / ayırma maliyeti, dolaylı adresleme |
| Explicit heap-dynamic | Açık komutla: new, malloc | Java nesneleri, C++ new | Bağlı liste, ağaç / pointer hataları, yönetim zorluğu |
| Implicit heap-dynamic | Değer atandığında otomatik | JavaScript dizileri, Python değişkenleri | En esnek / verimsiz, hata yakalama kaybı |
TUZAK · pointer ile gösterdiği farklı kategoride.
int *p = malloc(sizeof(int));satırında p’nin kendisi stack-dynamic (yerel değişken), gösterdiği hücre explicit heap-dynamic.
Scope ve lifetime aynı şey değil
void count() {
static int calls = 0;
calls++;
printf("%d ", calls);
}
/* count(); count(); count(); */
1 2 3
calls’un scope’u sadece count’un gövdesi, ama lifetime’ı tüm program: değer çağrılar arasında korunuyor. Tersi de olur: f, g’yi çağırdığında f’nin yerel değişkenleri hâlâ yaşıyor ama (static scope’ta) g’nin içinden görünmüyor.
Scope
Bir değişken bir ifadede kullanılıyor ve o birimde bildirilmemişse nonlocal’dir. Nerede aranacağını scope kuralı belirler.
Static (lexical) scope
Program metnine bakılır. Önce yerel bildirimler, sonra fonksiyonun yazıldığı yeri çevreleyen birim (static parent), sonra onun ebeveyni (static ancestors). Derleme zamanında belirlenir. C, Java, Python, JavaScript, Ada.
İçteki bildirim dıştaki aynı isimli değişkeni gizler (hides). Bloklar { ... } kendi scope’unu açar:
{ var v = 1; { var v = 2; } console.log(v); }
{ let w = 1; { let w = 2; } console.log(w); }
2
1
JavaScript’te var blok scope’u tanımaz, let tanır. Java ve C#‘ta iç blok, aynı metottaki yerel değişkenin adını tekrar bildiremez (derleme hatası: variable x is already defined); C ve C++‘ta bildirebilir ve dıştakini gizler.
Dynamic scope
Çağrı zincirine bakılır. Önce yerel, sonra beni çağıran fonksiyon, sonra onu çağıran… Çalışma zamanında belirlenir. Erken LISP, APL, SNOBOL4 ve Perl’in local bildirimi.
Eleştirisi: bir fonksiyonun yerel değişkenleri çağırdığı her fonksiyona açıktır (reliability düşer), nonlocal referansın ne olduğu çağrı yoluna bağlıdır (readability düşer), statik tip kontrolü yapılamaz, erişim yavaştır. Tek avantajı: çağıranın değişkenleri parametre geçmeden kullanılabilir.
Çözümlü örnek: izleme tablosu
int n = 1;
void a() { n = n * 10; print(n); }
void b() { int n = 2; a(); }
void c() { int n = 3; b(); a(); print(n); }
main() { c(); print(n); }
a’daki n nonlocal. İki kuralı ayrı ayrı izle:
STATIC: a'nın static parent'ı global → a'daki n hep global n
olay global n c.n b.n çıktı
c → b → a 1 → 10 3 2 10
c → a 10 → 100 3 100
c: print(n) 3 3
main 100 100
DYNAMIC: a'da n yok → çağırana bak
olay aranan n bulunduğu yer yeni değer çıktı
c → b → a b.n (2) 20 20
c → a c.n (3) 30 30
c: print(n) c'nin kendi n'i 30 30
main global n (dokunulmadı) 1 1
Static: 10 100 3 100. Dynamic: 20 30 30 1. Bu iki çıktı Perl’de my yerine local kullanarak birebir üretilebilir.
TUZAK · dynamic’te tanım yerine bakmak. Dynamic scope’ta fonksiyonun nerede yazıldığı önemsiz; sadece “beni şu an kim çağırdı?” sorulur. Ve en yakın çağıran kazanır: zincir main → c → b → a ise b’nin n’i c’ninkinden önce bulunur.
Referencing environment
Bir ifadenin referencing environment’ı orada görünen tüm isimlerdir. Static scope’ta: yereller + gizlenmemiş static ata değişkenleri. Dynamic scope’ta: yereller + aktif tüm çağıranların gizlenmemiş değişkenleri.
function main() { var a, b, c;
function sub1() { var b, d;
function sub2() { var c, e; /* point 2 */ } }
function sub3() { var a, d; /* point 3 */ } }
point 2: c, e (sub2) · b, d (sub1) · a (main)
point 3: a, d (sub3) · b, c (main) sub1'in değişkenleri YOK
Sık yapılan hatalar
| Yanlış | Doğrusu |
|---|---|
| Dynamic’te fonksiyonun yazıldığı yere bakmak | Sadece çağrı zinciri |
| Zincirde ilk bulunanı değil en dıştakini almak | En yakın çağıran kazanır |
| Nonlocal atamada yeni yerel değişken yaratmak | Bulunan değişken değişir |
| Scope = lifetime | C static yerel: scope fonksiyon, lifetime program |
p = malloc(...) → p heap-dynamic | p stack-dynamic, gösterdiği hücre heap-dynamic |
| C static değişken → compile time | Load time |
| Kütüphane çağrısı → compile time | Link time |
| Point 3’te kardeş fonksiyonun yerellerini yazmak | Kardeş static ata değildir |
Kendini test et
Sınavdaki gibi İngilizce sorular. Önce kâğıt kalemle çöz, sonra cevabı aç.
What is printed (a) under static scoping and (b) under dynamic scoping?
int x = 5;
void f() { print(x); }
void g() { int x = 7; f(); }
main() { g(); f(); }Cevabı göster
f'deki x yerel değil (nonlocal). Static: f metinde global seviyede yazılmış, static parent'ı global → iki çağrıda da 5. Dynamic: ilk çağrıda zincir main → g → f, f'de x yok, çağıran g'de var: 7. İkinci çağrı main → f: global 5.
What is printed (a) under static scoping and (b) under dynamic scoping?
int n = 1;
void a() { n = n * 10; print(n); }
void b() { int n = 2; a(); }
void c() { int n = 3; b(); a(); print(n); }
main() { c(); print(n); }Cevabı göster
Static: a'daki n her zaman global n. c → b → a: global 1 → 10, yaz 10. c → a: 10 → 100, yaz 100. c kendi n'ini yazar: 3. main global'i yazar: 100.
Dynamic: c → b → a: en yakın n b'ninki (2) → 20, yaz 20. c → a: en yakın n c'ninki (3) → 30, yaz 30. c kendi n'ini yazar: a onu değiştirdi, 30. Global n'e hiç dokunulmadı: 1.
Give the complete output (a) under static scoping and (b) under dynamic scoping.
function main() {
var a = 1, b = 10;
function p() { a = a + b; print(a, b); }
function q() { var b = 20; p(); r(); }
function r() { var a = 100; p(); print(a); }
q();
print(a, b);
}Cevabı göster
Static: p, q, r üçü de main'in içinde yazılmış, p'deki a ve b her zaman main'inkiler. q → p: main.a = 1 + 10 = 11 → "11 10". q → r → p: main.a = 11 + 10 = 21 → "21 10". r kendi a'sını yazar: 100. main: "21 10".
Dynamic: q → p: a'yı ara, q'da yok → main.a; b'yi ara → q.b = 20. main.a = 21 → "21 20". q → r → p: a → r.a (100), b → q.b (20): r.a = 120 → "120 20". r kendi a'sını yazar, değişmişti: 120. main: main.a 21, main.b 10 → "21 10".
Assuming static scoping, which declaration does each name refer to at point 2?
function main() {
var a, b, c;
function sub1() {
var b, d;
function sub2() {
var c, e;
/* point 2 */
}
}
function sub3() {
var a, d;
/* point 3 */
}
}Cevabı göster
Önce yereller (sub2: c, e), sonra static parent sub1 (b, d), sonra main (a; main.b ve main.c gizlenmiş). Point 3'te ise: a, d sub3'ten; b, c main'den. sub1'in değişkenleri sub3'ten görünmez çünkü sub3, sub1'in içinde değil.
Give the binding time of each: (a) the meaning of the reserved word while; (b) the size of long in C; (c) the type of a local variable in Java; (d) the address of a C global variable; (e) the type of a Python variable.
Cevabı göster
Sıra: design → implementation → compile → link → load → run. while'ın anlamını dili tasarlayan belirler. C standardı long'un boyunu sabitlemez, derleyici/platform belirler. Java'da tip bildirimi derleyici tarafından okunur (static type binding). Global değişken program belleğe yüklenirken adres alır. Python'da tip atama anında bağlanır (dynamic type binding).
Classify the storage category of each: (a) a Java static field; (b) a local int inside a Java method; (c) the object created by new int[5] in Java; (d) a JavaScript variable v = [1, 2, 3].
Cevabı göster
Static: çalışmadan önce bağlanır, program bitene kadar yaşar. Stack-dynamic: bildirimi işlendiğinde (elaboration) stack'te ayrılır, metot dönünce gider. Explicit heap-dynamic: programcının açık komutuyla (new) heap'te, referansla erişilir. Implicit heap-dynamic: değer atandığında otomatik ayrılır, tip de o an bağlanır.
In C, the function below is called three times. What is printed, and what are the scope and the lifetime of calls?
void count() {
static int calls = 0;
calls++;
printf("%d ", calls);
}Cevabı göster
static yerel değişken bir kez ayrılır ve başlatılır, çağrılar arasında değerini korur (history sensitive). Adıyla sadece count'un içinden erişilir (scope), ama hücre program bitene kadar yaşar (lifetime). Scope ile lifetime'ın farklı olduğunun klasik örneği.
Does this Java code compile? Would the equivalent C code compile?
public static void main(String[] args) {
int x = 1;
{ int x = 2; System.out.println(x); }
}Cevabı göster
Java ve C#, iç bir bloğun aynı metottaki yerel değişken adını tekrar bildirmesine izin vermez: "variable x is already defined in method main". C ve C++'ta iç blok yeni bir scope açar ve dıştaki x'i gizler (hides).
What do the two console.log calls print in JavaScript?
{ var v = 1; { var v = 2; } console.log(v); }
{ let w = 1; { let w = 2; } console.log(w); }Cevabı göster
var fonksiyon (ya da global) scope'ludur, blok kendi scope'unu açmaz: iki v aynı değişken, son değer 2. let blok scope'ludur: içteki w ayrı bir değişken, dıştaki 1 kalır.
In Fortran's implicit declaration rule, what are the types of KOUNT, TOTAL and NUM?
Cevabı göster
Fortran'da bildirilmemiş bir isim I, J, K, L, M veya N ile başlıyorsa integer, değilse real sayılır. KOUNT (K) ve NUM (N) integer, TOTAL (T) real. Implicit declaration yazmayı kolaylaştırır ama yazım hatalarını gizler (reliability düşer).
In the assignment x = x + 1, what does the x on the left denote and what does the x on the right denote?
Cevabı göster
Atamanın solundaki isim hücrenin adresini (l-value) ister, değer oraya yazılır. Sağdaki isim hücredeki değeri (r-value) okur. Aynı adrese iki farklı isimle erişilebiliyorsa bunlara alias denir.
What is printed in JavaScript? Which kind of type binding does this show?
let t = 5;
t = "five";
console.log(typeof t);Cevabı göster
JavaScript'te değişkenin tipi bildirimle değil atanan değerle, çalışma zamanında bağlanır ve değişebilir. Artısı esneklik; eksisi derleyicinin tip hatası yakalayamaması ve çalışma zamanı tip kontrolünün maliyeti.
Doğrulama: Static scope çıktıları JavaScript'in kendi lexical kuralıyla (node verify/SEN2001/04-static.mjs), dynamic scope çıktıları Perl'de local ile (perl verify/SEN2001/04-dynamic.pl) gerçekten çalıştırıldı. Referencing environment, her bildirime kendi adını değer olarak verip node ile yazdırılarak bulundu. C static yerel değişken örneği Java static alanıyla (java W04.java), Java blok kuralı derleyiciyle (java W04b.java derleme hatası verdi) kontrol edildi.