SEN2001 · 4. hafta · Names, Bindings, Type Checking and Scopes

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

namevariablel-valuer-valuealiasbindingbinding timestatic bindingdynamic bindingtype inferencelifetimestatic variablestack-dynamicexplicit heap-dynamicimplicit heap-dynamicscopestatic scopedynamic scopestatic parentreferencing environmentblock

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:

  1. Static scope = sayfaya bak. Değişken yerel değilse fonksiyonun yazıldığı yeri çevreleyen birimde aranır.
  2. Dynamic scope = davete bak. Değişken yerel değilse çağıran fonksiyonda, sonra onu çağıranda aranır.
  3. 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.
  4. Scope ≠ lifetime. Görünürlük metinle, ömür çalışmayla ilgili.

Değişkenin altı niteliği

NitelikAnlamı
NameDeğişkeni anan isim. Tasarım soruları: büyük/küçük harf duyarlı mı, uzunluk sınırı, reserved word’ler
AddressBellekteki yeri, l-value. Aynı isim farklı zamanlarda farklı adreste olabilir (özyineleme)
ValueHücredeki içerik, r-value
TypeDeğer aralığı ve yapılabilecek işlemler
LifetimeHücreye bağlı kaldığı süre
ScopeGö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 timeC’de int’in boyu ve aralığı, floating-point temsili
Compile timeC veya Java’da değişkeni tipine bağlamak
Link timeKütüphane fonksiyonu çağrısını fonksiyonun koduna bağlamak
Load timeC global veya static değişkeni bellek hücresine bağlamak
Run timeStatic 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.

KategoriNe zaman bağlanırÖrnekArtı / eksi
StaticÇalışmadan önce, program bitene kadarC global, C static yerel, Java static alanHızlı, doğrudan adresleme, geçmişi hatırlar / özyineleme yok
Stack-dynamicBildirim işlendiğinde (elaboration), tip statikC/Java metot yerelleriÖzyineleme, bellek paylaşımı / ayırma maliyeti, dolaylı adresleme
Explicit heap-dynamicAçık komutla: new, mallocJava nesneleri, C++ newBağlı liste, ağaç / pointer hataları, yönetim zorluğu
Implicit heap-dynamicDeğer atandığında otomatikJavaScript dizileri, Python değişkenleriEn 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 bakmakSadece çağrı zinciri
Zincirde ilk bulunanı değil en dıştakini almakEn yakın çağıran kazanır
Nonlocal atamada yeni yerel değişken yaratmakBulunan değişken değişir
Scope = lifetimeC static yerel: scope fonksiyon, lifetime program
p = malloc(...) → p heap-dynamicp stack-dynamic, gösterdiği hücre heap-dynamic
C static değişken → compile timeLoad time
Kütüphane çağrısı → compile timeLink time
Point 3’te kardeş fonksiyonun yerellerini yazmakKardeş static ata değildir

Kendini test et

Sınavdaki gibi İngilizce sorular. Önce kâğıt kalemle çöz, sonra cevabı aç.

Soru 1kolay

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
(a) 5 5 (b) 7 5

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.

Soru 2orta

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
(a) 10 100 3 100 (b) 20 30 30 1

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.

Soru 3zor

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
(a) 11 10 / 21 10 / 100 / 21 10 (b) 21 20 / 120 20 / 120 / 21 10

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

Soru 4orta

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
a: main, b: sub1, c: sub2, d: sub1, e: sub2

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

Soru 5orta

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
(a) language design (b) language implementation (c) compile time (d) load time (e) run time

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

Soru 6orta

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
(a) static (b) stack-dynamic (c) explicit heap-dynamic (d) implicit heap-dynamic

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.

Soru 7orta

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
1 2 3; scope: the body of count; lifetime: the whole program run

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.

Soru 8orta

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: no (x is already defined); C: yes, the inner x hides the outer one

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

Soru 9orta

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
2 and 1

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.

Soru 10kolay

In Fortran's implicit declaration rule, what are the types of KOUNT, TOTAL and NUM?

Cevabı göster
KOUNT: integer, TOTAL: real, NUM: integer

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

Soru 11kolay

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
left: the l-value (address); right: the r-value (value)

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.

Soru 12kolay

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
string; dynamic type binding

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.

Deftere not kartı

değişkenin 6 niteliği: name, address (l-value), value (r-value), type, lifetime, scope alias: aynı adrese iki isim (reliability düşer) binding time: design → implementation → compile → link → load → run * = çarpma: design | C int boyu: impl | Java tip: compile | kütüphane: link C static/global hücre: load | yerel hücre, değer: run type binding: explicit / implicit (Fortran I-N) / inference (var, ML) / dynamic (Python, JS) storage: static | stack-dynamic | explicit heap (new) | implicit heap (atamada) scope ≠ lifetime: C static yerel → scope fonksiyon, lifetime program STATIC scope = metne bak: yerel → yazıldığı yeri çevreleyen (static parent) → ... DYNAMIC scope = çağrı zincirine bak: yerel → beni çağıran → onu çağıran ... nonlocal'e atama: bulduğun değişkeni değiştirir, çağırana dönünce görünür Java/C#: iç blok aynı yerel adı tekrar bildiremez; C/C++ gizler JS var = fonksiyon scope, let = blok scope