Method Argüman Yönleri - input, output, inout ve ref Farkı
Gün 2: Method Argüman Yönleri | Yönlerin farkları
Bu derste bir task veya function'a argüman geçerken kullanılan dört yönü (input, output, inout, ref) ve özellikle sınıf handle'ları söz konusu olduğunda aralarındaki davranış farklarını öğreneceğiz. Bu farkları kavramak, doğrulama ortamında nesnelerin beklenmedik şekilde değişmesini (veya değişmemesini) anlamak için kritiktir.
Argüman Yönleri ve Kopyalama Mantığı
Bir handle (sınıf değişkeni) aslında bir nesneye işaret eden adrestir. Bir metoda handle geçtiğinizde, yönün ne olduğu adresin mi yoksa nesnenin mi etkileneceğini belirler. Burada iki ayrı kavramı birbirinden ayırmak gerekir: handle'ın işaret ettiği nesnenin içeriğini değiştirmek (d.value = 10) ile handle'ı başka bir nesneye yönlendirmek (d = new(...)).
input: Dışarıdakimy_objadresini içeriye kopyalar. İçerided.value = 10dediğinizde asıl nesneyi değiştirirsiniz (çünkü iki handle aynı adrese bakar). Amad = new(99)dediğinizde yalnızca içerideki kopya etiketin yönünü değiştirmiş olursunuz; task bitince bu kopya silinir ve dışarıdakimy_objeski nesneye bakmaya devam eder.output: İçeriye hiçbir bilgi almaz; argüman task'a girerkennull'dır. İçeride yeni bir nesne yaratılır (new) ve task bittiği anda bu yeni adres dışarıdakimy_obj'ye yazılır.inout:inputveoutput'un birleşimidir. Dışarıdaki adresi alır, içeride kullanmanıza izin verir. İçeride handle'ı başka bir nesneye yönlendirirseniz (new(30)), task bittiği an bu yeni adres dışarıya kopyalanır.ref: Kopya yoktur. İçeridekidile dışarıdakimy_objkelimenin tam anlamıyla aynı değişkendir. İçeride handle'anew()atadığınız an, dışarıdaki değişken de anında o yeni nesneye bakmaya başlar (task'ın bitmesini bile beklemez). Adres kopyalama bile yapılmadığı için en hızlısıdır.
Benzetme: Nesne bir ev, handle ise evin adresinin yazılı olduğu kâğıt olsun.
input: adresin fotokopisini veririm; eve gidip içini değiştirebilirsin ama fotokopiye başka adres yazman benim kâğıdımı etkilemez.output: boş bir kâğıt verirsin, ben üzerine yeni bir adres yazıp geri veririm.inout: fotokopimi verirsin, ben üstünü silip yeni adres yazarım ve geri verdiğimde sen de o adresi kullanırsın.ref: bana kâğıdın kendisini verirsin; üzerine yazdığım anda elindeki değişmiş olur.
| Yön | Girişte dışarıdaki handle görülür mü? | Nesne içeriği değişebilir mi? | d = new() dışarıya yansır mı? |
Ne zaman yansır? |
|---|---|---|---|---|
input |
Evet (kopya) | Evet | Hayır | — |
output |
Hayır (null başlar) |
— | Evet | Metot bitiminde |
inout |
Evet (kopya) | Evet | Evet | Metot bitiminde |
ref |
Evet (aynı değişken) | Evet | Evet | Anında |
Kaynak Kod
// =============================================================================
// GUN 2 Konu 2.1 - ARGÜMAN YÖNLERİ: input vs output vs inout vs ref
// =============================================================================
class Dummy;
int value;
// Kurucu Metot (Constructor)
function new(int v = 0);
value = v;
endfunction
endclass
module arg_directions_demo;
// 1. INPUT: İçeri giren handle'ı (işaretçiyi) kopyalar.
// Çağrıldığı yerdeki asıl handle'ın işaret ettiği yeri değiştiremez,
// ancak nesnenin İÇERİĞİNİ değiştirebilir.
task automatic test_input(input Dummy d);
// Nesnenin içeriğini değiştirmek dışarıdan görünür
d.value = 10;
// Handle'ın kendisini değiştirmek dışarıdan GÖRÜNMEZ
d = new(99);
endtask
// 2. OUTPUT: Başlangıçta null'dır. Task bittiğinde handle'ı DIŞARI kopyalar.
task automatic test_output(output Dummy d);
// Task bittikten sonra dışarıdaki handle bu yeni nesneyi işaret edecektir
d = new(20);
endtask
// 3. INOUT: Handle'ı İÇERİ kopyalar ve task bittiğinde DIŞARI kopyalar.
task automatic test_inout(inout Dummy d);
// Gelen nesneyi değiştirir
d.value = 35;
// Yeni bir nesneye işaret eder, task bittikten sonra dışarısı bu yeni adresi görecektir
d = new(30);
endtask
// 4. REF: Handle'ın GERÇEK hafıza referansını iletir (kopyalama yapılmaz).
// Handle üzerindeki herhangi bir değişiklik anında dışarıdan görünür.
task automatic test_ref(ref Dummy d);
// Dışarıdaki handle anında bu yeni nesneyi işaret etmeye başlar
d = new(40);
endtask
initial begin
Dummy my_obj;
$display("=== Argüman Yönleri Demosu ===\n");
// --- INPUT ---
my_obj = new(1);
test_input(my_obj);
// my_obj.value 99 DEĞİL, 10 olacaktır. 'new(99)' task bittiğinde silindi.
$display("[INPUT] test_input sonrasi : my_obj.value = %0d", my_obj.value);
// --- OUTPUT ---
// my_obj adresi tamamen üzerine yazılır.
test_output(my_obj);
$display("[OUTPUT] test_output sonrasi: my_obj.value = %0d", my_obj.value);
// --- INOUT ---
test_inout(my_obj);
// my_obj artık test_inout içinde oluşturulan yeni nesneyi işaret ediyor.
$display("[INOUT] test_inout sonrasi : my_obj.value = %0d", my_obj.value);
// --- REF ---
test_ref(my_obj);
// my_obj anında doğrudan değiştirildi.
$display("[REF] test_ref sonrasi : my_obj.value = %0d", my_obj.value);
$finish;
end
endmodule
Kodun Açıklaması
Dummysınıfı: Tek birvalueözelliği ve onu başlatannew(int v = 0)constructor'ı içerir. Yön farklarını gözlemlemek için sade bir taşıyıcı olarak kullanılır.test_input:d.value = 10satırı gelen nesnenin içeriğini değiştirir ve bu dışarıdan görünür. Ancakd = new(99)yalnızca içerideki yerel handle'ı yeni bir nesneye yönlendirir; task bitince bu kopya kaybolur. Bu yüzden çıktıdamy_obj.value99 değil 10'dur.test_output: Argüman task'a girerkennullkabul edilir; içeride hiçbir giriş bilgisi kullanılmaz.d = new(20)ile oluşturulan yeni nesne, task bittiğinde dışarıdakimy_obj'ye kopyalanır. Sonuçmy_obj.value = 20olur.test_inout: Hem girişte hem çıkışta kopyalama yapar.d.value = 35gelen nesneyi değiştirir, ama sonrad = new(30)ile yeni bir nesneye geçilir. Task bittiğinde dışarı kopyalanan bu son handle olduğundanmy_obj.value = 30olur.test_ref:refile kopya yapılmaz; içeridekidile dışarıdakimy_objaynı değişkendir.d = new(40)çağrıldığı andamy_objda yeni nesneyi işaret etmeye başlar; task'ın bitmesini beklemez. Sonuçmy_obj.value = 40olur.automaticanahtar kelimesi: Tüm task'larautomaticolarak tanımlanmıştır; bu, her çağrıda argümanların ve yerel değişkenlerin kendi kopyasının oluşturulmasını sağlar (özyineleme ve yeniden giriş güvenliği için gereklidir).
Önemli Noktalar
inputile gelen bir handle üzerinden nesnenin içeriğini değiştirebilirsiniz, fakat handle'ı yeni bir nesneye yönlendirmeniz dışarıyı etkilemez.outputargümanına metoda girerken güvenmeyin; başlangıçtanullkabul edilir ve yalnızca metot bitiminde dışarı yazılır.inoutveoutput, değeri metot bittiğinde dışarı kopyalar; bu nedenle değişiklik metot sırasında değil, dönüşte görünür.refen hızlısıdır (adres kopyalama bile yapılmaz) ve değişiklikler anında dışarıya yansır; ancak bu güç, istenmeyen yan etkilere de yol açabileceği için dikkatli kullanılmalıdır.- Yalnızca okumak için bir nesneyi geçiyorsanız ve onu değiştirmeyecekseniz
const reftercih edilebilir; bu hem performans sağlar hem de kazara değişikliği önler. refargümanlar yalnızcaautomaticmetodlarda kullanılabilir; statik bir modül task'ındarefyazmak derleme hatasıdır. Koddaki tüm task'larınautomaticolmasının bir nedeni de budur.- Bu kurallar yalnızca handle'lar için değil, büyük diziler ve kuyruklar için de geçerlidir: 10.000 elemanlı bir kuyruğu
inputile geçmek tüm kuyruğu kopyalar;ref(veyaconst ref) ile geçmek kopyasızdır. Performans farkı büyük testbench'lerde belirgindir.
Sık Yapılan Hatalar
inputile gelen handle'anewatayıp dışarının değişmesini beklemek: En klasik hata. Dışarı etkilemek istiyorsanızinoutveyarefkullanın ya da nesneyireturnedin.outputargümanı içeride okumak:outputargümanı giriştenull'dır;d.valueokumaya çalışmak null-handle hatası verir.refile geçilen kuyruğu döngü içinde değiştirmek:refanlık yansıdığı için, çağıran taraf aynı kuyruğu paralel bir süreçte geziyorsa indeksler kayar.const refile niyetinizi netleştirin.- Varsayılan yönü unutmak: Yön yazılmazsa argüman
input'tur.task t(Dummy d)iletask t(input Dummy d)aynıdır.
Kendinizi Deneyin
test_inputiçindekid = new(99);satırından önced.value = 10;yerine sonra yazın.my_obj.valuene oldu? Neden?test_outputtask'ındad = new(20);satırından önce$display("%0d", d.value);eklemeyi deneyin. Hangi hatayı aldınız?task automatic fill(ref int q[$]); q.push_back(1); endtaskyazıp bir kuyrukla çağırın; ardındanref'iinputyapıp farkı gözlemleyin.automaticanahtar kelimesinitest_ref'ten silin ve derleyicinin ne dediğini not alın.
Hızlı Kontrol: input Dummy d ile çağrılan bir task içinde d.value = 10 dışarıyı etkiler mi?
Evet. input handle'ın kopyasını taşır ama kopya da aynı nesneyi gösterir; nesnenin içeriğini değiştirmek dışarıdan görülür. Etkilemeyen şey, d = new(...) ile handle'ı başka bir nesneye yönlendirmektir.