İlk Üye Kuralı
Coddy'nin C Journey'sinin Object Oriented Programming bölümünün bir parçası — ders 29 / 61.
Bir yapıyı (struct) başka bir yapının ilk üyesi olarak yerleştirdiğinizde, güçlü bir şey gerçekleşir: dıştaki yapının bellek adresi, ilk üyesinin bellek adresi ile aynıdır. Bu, C standardı tarafından garanti edilir.
Önceki örneğimizi ele alalım:
typedef struct {
int id;
} Parent;
typedef struct {
Parent parent; // İlk üye
int grade;
} Child;Bir Child değişkeni oluşturduğunuzda, Parent üyesi Child'ın belleğinin en başında yer alır. Bu, Child'a işaret eden bir işaretçinin, onun Parent üyesine işaret eden bir işaretçiyle tam olarak aynı adresi gösterdiği anlamına gelir.
Child c;
Child* child_ptr = &c;
Parent* parent_ptr = (Parent*)child_ptr; // Güvenli tür dönüşümü!Her iki işaretçi de aynı adresi tutar. Bu, bir Parent* bekleyen herhangi bir fonksiyona bir Child* geçirmenize olanak tanır:
void print_id(Parent* p) {
printf("ID: %d\n", p->id);
}
// main içinde:
Child c;
c.parent.id = 42;
print_id((Parent*)&c); // Kusursuz çalışırBu teknik, C'deki kalıtımın temelidir. Temel tür (base type) için yazılmış bir fonksiyon, temel tür ilk sırada yerleştirildiği sürece, türetilmiş herhangi bir tür (derived type) üzerinde çalışabilir. Bellek düzenleri mükemmel bir şekilde hizalandığı için tür dönüşümü (cast) güvenlidir.
Görev
KolayBasit bir hayvan hiyerarşisi oluşturarak ilk üye kuralını (first member rule) keşfedelim. Temel bir Animal yapısı (struct) ve türetilmiş bir Dog yapısı oluşturacak, ardından bir Dog işaretçisinin (pointer) güvenli bir şekilde nasıl Animal işaretçisine dönüştürülebileceğini göstereceksiniz.
Uygulayacağınız temel bilgi şudur: Animal, Dog yapısının ilk üyesi olduğunda, her iki yapı da aynı başlangıç bellek adresini paylaşır. Bu, bir işaretçiyi dönüştürerek (casting), Animal* bekleyen herhangi bir fonksiyona Dog* geçirebileceğiniz anlamına gelir.
Kodunuzu üç dosya halinde düzenleyeceksiniz:
animal.h: Her iki yapıyı da include guard'lar ile tanımlayın.Animalyapınız tek bir alana sahip olmalıdır:legs(bacak sayısını temsil eden bir tam sayı).Dogyapınız,Animalyapısını ilk üye olarak içine almalı ve birnamealanı (50 karakterlik bir karakter dizisi) eklemelidir. AyrıcaAnimal*parametresi alan birprint_animalfonksiyonu bildirin.animal.c:print_animalfonksiyonunu uygulayın. Bu fonksiyon temel tiple çalışır; yalnızcaAnimalyapısını bilir ve hayvanın kaç bacağı olduğunu yazdırır.main.c: BirDogdeğişkeni oluşturun ve alanlarını doldurun (hem iç içe geçmiş hayvanın bacak sayısını hem de köpeğin adını). ArdındanDog*işaretçinizi birAnimal*işaretçisine dönüştürüpprint_animalfonksiyonuna göndererek ilk üye kuralını gösterin. Ayrıca resmin tamamını göstermek için köpeğin adını ayrı olarak yazdırın.
İki girdi alacaksınız: köpeğin adı (bir dize) ve bacak sayısı (bir tam sayı).
Çıktınız şu şekilde görünmelidir:
Dog: Buddy
Legs: 4Burada Buddy köpeğin adı ve 4 bacak sayısıdır. Önce main.c içinden köpeğin adını yazdırın, ardından bacakları görüntülemek için dönüştürülmüş işaretçi ile print_animal fonksiyonunu çağırın.
bu, temel tip (Animal) için yazılmış bir fonksiyonun, işaretçi dönüşümü yoluyla türetilmiş bir tiple (Dog) nasıl sorunsuz bir şekilde çalışabileceğini gösterir; bu, C dilinde kalıtımın (inheritance) temelidir.
Kopya kağıdı
Bir struct, başka bir struct'ın ilk üyesi olarak gömüldüğünde, her iki struct da aynı başlangıç bellek adresini paylaşır. Bu, C standardı tarafından garanti edilir.
typedef struct {
int id;
} Parent;
typedef struct {
Parent parent; // İlk üye
int grade;
} Child;Dıştaki struct'a ait bir pointer, güvenli bir şekilde ilk üyesine ait bir pointer'a dönüştürülebilir:
Child c;
Child* child_ptr = &c;
Parent* parent_ptr = (Parent*)child_ptr; // Güvenli dönüştürme - aynı adresBu, temel türü (base type) bekleyen fonksiyonlara türetilmiş tür (derived type) pointer'ı gönderilmesine olanak tanır:
void print_id(Parent* p) {
printf("ID: %d\n", p->id);
}
Child c;
c.parent.id = 42;
print_id((Parent*)&c); // Bellek yerleşimleri hizalandığı için çalışırBu teknik, C'de kalıtımın (inheritance) temelidir ve temel türler için yazılmış fonksiyonların türetilmiş türler üzerinde çalışmasını sağlar.
Kendin dene
#include <stdio.h>
#include <string.h>
#include "animal.h"
int main() {
// Girdiyi oku
char name[50];
int legs;
scanf("%s", name);
scanf("%d", &legs);
// TODO: Bir Dog değişkeni oluşturun
// TODO: strcpy kullanarak köpeğin adını ayarlayın
// TODO: Bacak sayısını ayarlayın (gömülü Animal üzerinden erişin)
// TODO: Köpeğin adını şu formatta yazdırın: "Dog: <name>"
// TODO: Dog* tipini Animal* tipine dönüştürün ve print_animal fonksiyonunu çağırın
// Bu, ilk üye kuralını (first member rule) gösterir!
return 0;
}
Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.
Object Oriented Programming bölümündeki tüm dersler
1Modüler Programlama Temelleri
Başlık DosyalarıInclude Guard'larKaynak DosyalarıStatik FonksiyonlarÖzet: Modüler Hesap Makinesi4Kapsülleme
Opaque Pointer KavramıOpaque Struct TanımlamaGetter ve Setter MetotlarıSetter Metotlarında DoğrulamaÖzet: Gizli Kutu2Nesneler ve Metotlar
Nesne Olarak Struct'lar'Self' İşaretçisiConst Doğruluğuİşaretçi vs DeğerYardımcı MetotlarÖzet: Point Manager5Proje: Basit Banka Hesabı
Proje KurulumuHesabın Oluşturulması3Nesne Yaşam Döngüsü
Constructor DeseniDestructor DeseniStack İlklendirmeDerin KopyalamaÖzet: String Wrapper6Kompozisyon Yoluyla Kalıtım
Struct Gömmeİlk Üye KuralıÜst Üyelere ErişimUpcastingÖzet: Şekil Hiyerarşisi