SEN2001 · 5. hafta · Data Types

Veri tipleri II: record, union, pointer ve tip eşdeğerliği

Record ve tuple, free ve discriminated union, pointer/reference, dangling pointer ve memory leak, heap yönetimi, type checking, strong typing ve name/structure type equivalence.

recordfieldtupleunionfree uniondiscriminated uniontagpointerreference typedereferencingpointer arithmeticdangling pointerlost heap-dynamic variablememory leaktombstonelocks-and-keysreference countingmark-sweepgarbage collectiontype checkingtype errorstrongly typedname type equivalencestructure type equivalence

Önceki sayfa tek tek değerleri tutan tiplerle ilgiliydi. Bu sayfa değerleri bir araya getiren tipler (record, union), başka değerleri gösteren tipler (pointer, reference) ve tiplerin birbiriyle uyumunu (type checking, type equivalence) kapsar. Sınavda en çok sorulanlar: dangling pointer ile memory leak farkı, free ve discriminated union, name ve structure equivalence.

Altın kurallar:

  1. Dangling pointer: silinmiş hücreyi gösteren pointer. Leak: kimsenin göstermediği hücre.
  2. Free union kontrol edilmez, discriminated union bir tag taşır.
  3. Name equivalence = aynı ad, structure equivalence = aynı yapı.
  4. Reference counting döngüleri toplayamaz.

Record

Record, heterojen elemanların (field) adlarıyla erişildiği topluluktur. Dizinin aksine elemanlar farklı tiplerde olabilir; erişim indisle değil adla olur: emp.name. COBOL ve C struct, Java’da sınıflar ve record.

record P(int x, int y) {}
P p = new P(1, 2), q = new P(1, 2);
System.out.println(p.equals(q) + " " + (p == q));
System.out.println(p);
true false
P[x=1, y=2]

Alan erişimi derleme zamanında sabit bir ofsete çevrilir, bu yüzden hızlıdır. Tuple record’a benzer ama alanların adı yoktur (Python (3, "a"), ML); erişim konumla olur.

Union

Union, bir değişkenin çalışma boyunca farklı zamanlarda farklı tiplerde değer tutabildiği tiptir. Tüm alanlar aynı belleği paylaşır.

  • Free union (C, C++): hangi alanın geçerli olduğunu takip eden bir şey yok. Tip kontrolü yapılmaz.
  • Discriminated union (Ada, ML, Haskell): bir tag (discriminant) alanı hangi tipin geçerli olduğunu söyler; yanlış alan okunursa hata.
union { int i; float f; } u;
u.i = 1;
printf("%g", u.f);   /* int'in bitleri float olarak okunur */

Aynı bitlerin float yorumu Java ile gösterilebilir:

System.out.println(Float.intBitsToFloat(1));
System.out.println(Float.intBitsToFloat(1065353216));
System.out.println(Float.floatToIntBits(1.0f));
1.4E-45
1.0
1065353216

1 tam sayısı float olarak 1.4E-45’tir; 1.0f’nin bitleri ise int olarak 1065353216’dır. Free union bu karışıklığı sessizce yapar. Java ve C# union’ı hiç almamıştır; reliability için kalıtım (inheritance) kullanılır.

Pointer ve reference

Pointer bir bellek adresi ya da özel değer nil (null) tutar. İki kullanımı var: dolaylı adresleme ve heap’te dinamik bellek yönetimi.

İşlemC/C++Anlamı
Atamap = &x;p, x’in adresini tutar
Dereference*pp’nin gösterdiği hücredeki değer
Aritmetikp + 1Bir sonraki eleman; adres sizeof(tip) kadar artar
Alan erişimip->x(*p).x
int a[5] = {10, 20, 30, 40, 50};
int *p = a;     /* p = &a[0] */
p += 2;         /* p = &a[2] */
/* *p + 1 = 31,  *(p + 1) = 40,  p - a = 2 */

Reference tipi (C++ int&, Java ve C# sınıf değişkenleri) bir nesneyi gösterir ama aritmetik yapılmaz. Java’da reference’lar hiçbir zaman serbest bırakılmış belleği göstermez, çünkü serbest bırakma garbage collector’ın işidir. Bu yüzden Java’da dangling reference yoktur.

Pointer problemleri

Dangling pointer

Serbest bırakılmış (deallocate edilmiş) bir heap hücresinin adresini hâlâ tutan pointer.

int *p = new int(5);
int *q = p;
delete p;      // p ve q artık serbest bırakılmış hücreyi gösteriyor

p’yi nullptr yapabilirsin, ama q’yu unutursun. O hücre daha sonra başka bir değişkene verilirse, q üzerinden yazmak başka bir verinin üzerine yazar.

Lost heap-dynamic variable (memory leak)

Ayrılmış ama artık hiçbir pointer’ın erişemediği heap hücresi. Bunlara garbage denir.

int *p = new int;   // hücre 1
p = new int;        // hücre 2; hücre 1 KAYBOLDU
int *q = p;         // q hücre 2'yi gösteriyor
p = new int;        // hücre 3; hücre 2 q sayesinde kurtuldu

Kayıp hücre sayısı 1. Sınavda her new’ü numaralandır, sonda her hücreyi hangi pointer’ın gösterdiğini yaz. Kimse göstermiyorsa leak, gösteren var ama hücre silinmişse dangling.

Dangling pointer çözümleri

  • Tombstone: her heap nesnesi için ek bir hücre; pointer’lar bu hücreyi gösterir. Nesne silinince tombstone nil olur. Bedel: zaman ve bellek.
  • Locks-and-keys: pointer bir (key, adres) çifti; heap hücresi bir lock taşır. Erişimde key == lock kontrol edilir; silinen hücrenin lock’u değişir.
  • En iyisi: programcıya silme yetkisi vermemek (Java, C#, LISP: garbage collection).

Heap yönetimi: garbage collection

YöntemNasılArtıEksi
Reference counting (eager)Her hücre ona kaç pointer olduğunu sayar, 0 olunca serbestToplama anlık, duraklama yokSayaçlar için bellek ve zaman; döngüleri toplayamaz
Mark-sweep (lazy)Bellek bitince: önce herkesi garbage say, kökten erişilenleri işaretle, işaretsizleri toplaDöngüleri toplarProgram toplama sırasında durur
A.next = B;  B.next = A;  root = A;  root = null;
reference count: A = 1 (B'den), B = 1 (A'dan)  → hiçbiri 0 değil, toplanmaz
mark-sweep: kökten A'ya yol yok → ikisi de işaretsiz → toplanır

Type checking ve strong typing

Type checking, bir operatörün operandlarının uyumlu tiplerde olduğunun kontrolüdür. Uyumlu tip: ya doğrudan geçerli ya da dilin kurallarına göre otomatik dönüştürülebilir (coercion). Uygunsuz tipte operand kullanmak type error’dür.

Tip bağlamaları statikse kontrol neredeyse tamamen derleme zamanında yapılabilir; dinamik tip bağlamada (JavaScript, Python) kontrol çalışma zamanında yapılmak zorundadır. Hatayı ne kadar erken yakalarsan düzeltmesi o kadar ucuz.

Strongly typed dil: tip hataları her zaman yakalanır. ML ve F# strongly typed. Java ve C# neredeyse (açık cast hata üretebilir ama çalışmada yakalanır). C ve C++ değil: free union’lar ve pointer dönüşümleri kontrol dışı. Fazla coercion strong typing’in faydasını azaltır.

Type equivalence

İki tip ne zaman “aynı” sayılır?

Name type equivalenceStructure type equivalence
KuralAynı tip adıyla bildirildiyseTiplerin yapısı aynıysa
UygulamaKolayZor (iç içe yapılar, sıralama, dizi sınırları)
EsneklikKısıtlayıcıEsnek
GüvenlikAyrı adlı tipler karışmazAyrılmak istenen tipler karışabilir
KullananAda, Java sınıflarıC (struct ve union hariç)
type Celsius = float;
type Fahrenheit = float;
c: Celsius;  f: Fahrenheit;
f := c;       name equivalence: HATA     structure equivalence: geçerli

TUZAK · Ada anonim tipleri. A, B : array (1..10) of Integer; satırında A ve B eşdeğer değil: anonim tip ifadesi her değişken için ayrı bir tip üretir. Eşdeğer olmaları için tipe önce bir ad verilmeli.

Sık yapılan hatalar

YanlışDoğrusu
Dangling = kimsenin göstermediği hücreO leak; dangling silinmiş hücreyi gösterir
delete p sonrası sadece p tehlikeliq gibi aynı adresi tutan her pointer dangling
p + 1 adresi 1 byte artırırsizeof(tip) kadar artar
C union’ı kontrol edilirFree union, kontrol yok
Java’da union varYok
Reference counting her şeyi toplarDöngüleri toplayamaz
Celsius = Fahrenheit her zamanName equivalence altında değil
Java strongly typed değil çünkü cast varCast hataları çalışmada yakalanır; neredeyse strongly typed

Kendini test et

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

Soru 1kolay

What is printed in Java?

class Point { int x, y; Point(int x, int y) { this.x = x; this.y = y; } }
Point a = new Point(1, 2);
Point b = a;
b.x = 5;
System.out.println(a.x);
Cevabı göster
5

Java'da sınıf tipli değişkenler reference'tır. b = a nesneyi kopyalamaz, aynı nesneye ikinci bir isim (alias) yaratır. b üzerinden yapılan değişiklik a'dan görünür.

Soru 2orta

What is printed in Java (16+)?

record P(int x, int y) {}
P p = new P(1, 2), q = new P(1, 2);
System.out.println(p.equals(q) + " " + (p == q));
Cevabı göster
true false

record, alanları değişmez (immutable) bir kayıt tipidir; derleyici equals'ı alan alan karşılaştıracak şekilde üretir. == ise iki ayrı nesnenin adresini karşılaştırır: false.

Soru 3orta

Consider the C++ code. After the last line, which pointers are dangling?

int *p = new int(5);
int *q = p;
delete p;
Cevabı göster
both p and q (q is the hidden one)

delete p hücreyi serbest bırakır ama p'nin ve q'nun içindeki adres değişmez. İkisi de serbest bırakılmış bir hücreyi gösterir: dangling pointer. p'yi nullptr yapmak q'yu kurtarmaz; programcı q'nun varlığını unutmuş olabilir. Tehlike: o hücre başka bir değişkene verilirse q üzerinden onun verisi bozulur.

Soru 4orta

How many heap cells are lost (memory leak) after this C++ code?

int *p = new int;
p = new int;
int *q = p;
p = new int;
Cevabı göster
1

1. hücre: p'nin ilk gösterdiği, ikinci new ile p başka yere yönelince hiçbir pointer onu göstermiyor → kayıp. 2. hücre: q hâlâ gösteriyor. 3. hücre: p gösteriyor. Toplam 1 lost heap-dynamic variable (garbage).

Soru 5orta

In C, int a[5] = {10, 20, 30, 40, 50}; int *p = a; p += 2; What are *p + 1 and *(p + 1)?

Cevabı göster
31 and 40

p += 2 pointer'ı iki eleman ileri taşır (adres 2 · sizeof(int) = 8 byte artar), p artık a[2]'yi gösterir. *p + 1: önce dereference (30), sonra +1 = 31. *(p + 1): bir sonraki eleman, a[3] = 40.

Soru 6kolay

A pointer p to int holds address 1000 and int is 4 bytes. What address does p + 3 hold? What if p were a pointer to double (8 bytes)?

Cevabı göster
1012; 1024

Pointer aritmetiği gösterilen tipin boyuna göre ölçeklenir: p + k = adres + k · sizeof(tip). int: 1000 + 12. double: 1000 + 24.

Soru 7zor

In C, union { int i; float f; } u; u.i = 1; and then u.f is read. What kind of union is this and what is the problem?

Cevabı göster
free union; no type checking, the int bits are reinterpreted as a float (1.4E-45)

C ve C++ union'ları free union'dır: hangi alanın son yazıldığını tutan bir etiket (tag) yok, derleyici kontrol etmez. 1'in bitleri float olarak okunduğunda çok küçük bir sayı çıkar (Java'da Float.intBitsToFloat(1) = 1.4E-45). Ada ve ML'deki discriminated union bir tag taşır ve yanlış alan okumayı yakalar. Java ve C#'ta union yoktur.

Soru 8orta

Two types are declared as below. Under name type equivalence and under structure type equivalence, may a variable of type Celsius be assigned to a variable of type Fahrenheit?

type Celsius = float;
type Fahrenheit = float;
Cevabı göster
name: no; structure: yes

Name type equivalence: tip adları aynıysa eşdeğer; Celsius ≠ Fahrenheit. Güvenli (sıcaklıklar karıştırılamaz) ama kısıtlayıcı. Structure type equivalence: yapı aynıysa eşdeğer; ikisi de float. Esnek ama programcının ayırmak istediği tipleri ayıramaz ve uygulaması zordur.

Soru 9orta

Which heap management technique fails to reclaim the two objects that point to each other below, after root is set to null?

A.next = B;  B.next = A;
root = A;
root = null;
Cevabı göster
reference counting

Reference counting her hücrede ona kaç pointer olduğunu tutar, sayaç 0 olunca serbest bırakır. root gidince A'nın sayacı 1 (B'den), B'nin sayacı 1 (A'dan) kalır: hiçbiri sıfırlanmaz ama ikisine de programdan erişilemez. Mark-sweep kökten erişilebilen her şeyi işaretler, işaretsizleri toplar; döngüyü toplar.

Soru 10kolay

Which dangling-pointer solution adds an extra heap cell between every pointer and its target, and sets it to nil when the target is deallocated?

Cevabı göster
tombstones

Tombstone yönteminde pointer'lar doğrudan nesneyi değil bir mezar taşı hücresini gösterir. Nesne silinince mezar taşı nil olur, kimse silinmiş belleğe erişemez. Bedeli: fazladan bellek ve her erişimde ek bir dolaylılık. Locks-and-keys: pointer bir (key, adres) çifti, hücre bir lock taşır; erişimde key ile lock karşılaştırılır.

Soru 11kolay

Which of these languages are strongly typed or nearly so: C, C++, Java, ML? Give one reason why C is not.

Cevabı göster
Java (nearly) and ML; C is not because unions are not type checked

Strongly typed: tip hataları her zaman (derlemede ya da çalışmada) yakalanır. ML ve F# strongly typed. Java ve C# neredeyse (açık cast'ler hata üretebilir ama çalışmada yakalanır). C ve C++ değil: free union'lar ve kontrolsüz pointer dönüşümleri tip hatasını kaçırır. Coercion kuralları da strong typing'i zayıflatır.

Soru 12zor

In Ada, A, B : array (1..10) of Integer; declares two variables with an anonymous array type. Are A and B type equivalent?

Cevabı göster
No

Ada'da anonim bir tip ifadesi her değişken için ayrı, benzersiz bir tip yaratır; bu yüzden A ve B eşdeğer sayılmaz (A := B derlenmez). Eşdeğer olmaları için önce isimli bir tip tanımlanır: type List is array (1..10) of Integer; A, B : List;. Ada name equivalence'ı ciddiye alır.

Doğrulama: Java çıktıları JDK ile çalıştırıldı (java verify/SEN2001/W05b.java); C union'ının bit yeniden yorumlaması Java'nın Float.intBitsToFloat fonksiyonuyla aynı bitler üzerinden gösterildi. C pointer aritmetiği, new/delete sonrası kayıp ve dangling sayıları ile reference counting döngüsü node'da küçük bir bellek benzetimiyle hesaplandı (verify/SEN2001/05b.mjs). C derleyicisi kurulu olmadığı için C kodları doğrudan çalıştırılmadı.

Deftere not kartı

record: heterojen, alan ADI ile erişim (p.x). tuple: alan adsız (Python) free union (C/C++): tag yok, kontrol yok → güvensiz. discriminated (Ada, ML): tag var Java, C#: union YOK pointer: adres + nil. *p dereference. p + k = adres + k * sizeof(tip) reference (Java, C++ &): aritmetik yok, daha güvenli dangling: serbest bırakılmış hücreyi gösteren (delete p sonrası q) lost heap-dynamic (leak): kimsenin göstermediği ayrılmış hücre tombstone: araya hücre, silinince nil. locks-and-keys: key == lock kontrolü reference counting: anlık, ama DÖNGÜYÜ toplayamaz mark-sweep: kökten işaretle, işaretsizi topla; döngüyü toplar, duraklama yapar strongly typed: tip hatası her zaman yakalanır. ML evet, Java neredeyse, C hayır name equivalence: aynı tip ADI. structure equivalence: aynı YAPI Ada anonim dizi tipi: her değişken ayrı tip