Veri Yapıları ve Programlama

3 - Struct, Enum ve Union’lar

Emre Can Yılmaz

Ondokuz Mayıs Üniversitesi

2026

Struct’lar: Farklı Veri Tiplerini Bir Araya Getirme

  • struct (structure), C dilinde farklı veri tiplerini bir arada tutan bir veri yapısıdır.
  • Birbiriyle ilişkili verileri daha organize ve anlamlı şekilde yönetmemizi sağlar.

Struct: Neyi Çözer?

  • Aynı varlığa ait alanları tek bir isim altında toplar (kayıt/record mantığı).
  • Fonksiyonlara veriyi parça parça değil, tek paket olarak taşımayı kolaylaştırır.
  • Çok sayıda kaydı diziyle yönetmeyi sağlar: Ogrenci ogrenciler[100];

Örnek: Öğrenci Bilgileri

Öğrenci bilgilerini (ad, soyad, numara, not ortalaması) saklamak için ayrı ayrı değişkenler tanımlamak yerine, bir struct kullanabiliriz:

struct Ogrenci {
  char ad[50];
  char soyad[50];
  int numara;
  float notOrtalamasi;
};

Struct Değişkenleri Oluşturma

  • struct tanımladıktan sonra, bu türden değişkenler oluşturabiliriz:
struct Ogrenci ogrenci1;
struct Ogrenci ogrenciler[100]; // 100 öğrencilik bir dizi

İki Stil: struct Etiketi ve typedef

Aynı fikri iki farklı biçimde ifade edebiliriz. Projelerde genellikle tek bir stil seçip tutarlı gitmek daha uygundur.

struct OgrenciA { char ad[50]; int numara; };
struct OgrenciA a;

typedef struct { char ad[50]; int numara; } OgrenciB;
OgrenciB b;

typedef ile Daha Okunur Kod

  • typedef ile struct türüne yeni bir isim verebiliriz:
typedef struct {
    // ... üyeler ...
} YeniAd;
  • typedef ile kod daha okunabilir hale gelir:
typedef struct {
  char ad[50];
  char soyad[50];
  int numara;
  float notOrtalamasi;
} Ogrenci;

Ogrenci ogrenci1; // struct Ogrenci ogrenci1; yerine

Struct’ları İlklendirme

  • Struct değişkenlerini tanımlarken değerleri ile birlikte başlatabiliriz:
Ogrenci ogrenci1 = {"Ali", "Veli", 12345, 3.50};

Struct Kopyalama ve Atama

Struct değişkenleri doğrudan atanabilir. Bu, alan alan kopyalama anlamına gelir.

Ogrenci a = {"Ali", "Veli", 1, 3.20};
Ogrenci b = a;     // a'nın alanları b'ye kopyalanır

b.numara = 2;

// a.numara hâlâ 1, b.numara 2

Bu bilgi, “fonksiyona değer ile geçiş” davranışını anlamayı da kolaylaştırır: değer ile geçişte çoğu durumda kopya taşınır.

Struct’ların Bellekte Yerleşimi: Hizalama ve Padding

  • Bir struct’ın bellekte kapladığı alan, üyelerin boyutlarının basit toplamı olmak zorunda değildir.
  • Derleyici, bazı mimarilerde daha hızlı erişim için aralara boşluk (padding) ekleyebilir.
  • Bu nedenle güvenilir bilgi kaynağı sizeof çıktısıdır (derleyici + mimariye bağlıdır).
#include <stdio.h>

typedef struct {
  char c1;   // 1 bayt
  int  i;    // çoğunlukla 4 bayt
  char c2;   // 1 bayt
} S1;

typedef struct {
  int  i;
  char c1;
  char c2;
} S2;

int main() {
  printf("sizeof(S1) = %zu\n", sizeof(S1));
  printf("sizeof(S2) = %zu\n", sizeof(S2));
  return 0;
}

Not: Üyelerin sırası değişince, sizeof sonucu da değişebilir.

Struct Üyelerine Erişim (.)

  • Struct üyelerine nokta operatörü (.) ile erişiriz.
#include <stdio.h>
#include <string.h>

typedef struct {
  char ad[50];
  char soyad[50];
  int numara;
} Ogrenci;

int main() {
  Ogrenci ogrenci1;

  strcpy(ogrenci1.ad, "Ali");
  ogrenci1.numara = 12345;

  printf("Ad: %s, Numara: %d\n", ogrenci1.ad, ogrenci1.numara);
  return 0;
}

Struct İşaretçisi ile Üyelere Erişim (->)

  • Struct’a bir işaretçi aracılığıyla erişiyorsak, ok operatörünü (->) kullanırız.
#include <stdio.h>
#include <string.h>

typedef struct {
  char ad[50];
  char soyad[50];
  int numara;
} Ogrenci;

int main() {
  Ogrenci ogrenci1;
  Ogrenci *ptr = &ogrenci1;

  strcpy(ptr->ad, "Ali");
  ptr->numara = 12345;

  printf("Ad: %s, Numara: %d\n", ptr->ad, ptr->numara);
  return 0;
}

Struct’lar ve Fonksiyonlar: Değer mi, İşaretçi mi?

  • Struct’lar fonksiyonlara:

    • değer olarak (kopya taşınır),
    • işaretçi olarak (orijinal veri üzerinde işlem yapılabilir) geçirilebilir.
  • Fonksiyonlar struct türünde değer döndürebilir.

Örnek: Öğrenci Bilgilerini Yazdırma (Değer ile geçiş)

#include <stdio.h>

typedef struct {
  char ad[50];
  int numara;
} Ogrenci;

void ogrenciYazdir(Ogrenci ogr) {
  // ogr burada bir kopyadır
  printf("Ad: %s, Numara: %d\n", ogr.ad, ogr.numara);
}

int main() {
  Ogrenci ogrenci1 = {"Ali", 12345};
  ogrenciYazdir(ogrenci1);
  return 0;
}

Örnek: Öğrenci Bilgilerini Yazdırma (İşaretçi + const)

Bu sürüm, “bu fonksiyon veriyi değiştirmeyecek” niyetini açıkça gösterir ve gereksiz kopyayı azaltır.

#include <stdio.h>

typedef struct {
  char ad[50];
  int numara;
} Ogrenci;

void ogrenciYazdirPtr(const Ogrenci *ogr) {
  printf("Ad: %s, Numara: %d\n", ogr->ad, ogr->numara);
}

int main() {
  Ogrenci ogrenci1 = {"Ali", 12345};
  ogrenciYazdirPtr(&ogrenci1);
  return 0;
}

Örnek: Öğrenci Bilgilerini Değiştirme (İşaretçi ile geçiş)

#include <stdio.h>
#include <string.h>

typedef struct {
  char ad[50];
  int numara;
} Ogrenci;

void ogrenciDegistir(Ogrenci *ogr) {
  strcpy(ogr->ad, "Veli");
  ogr->numara = 67890;
}

int main() {
  Ogrenci ogrenci1 = {"Ali", 12345};
  ogrenciDegistir(&ogrenci1);
  printf("Ad: %s, Numara: %d\n", ogrenci1.ad, ogrenci1.numara);
  return 0;
}

Örnek: Struct Döndüren Fonksiyon

#include <stdio.h>

typedef struct {
  int x;
  int y;
} Nokta;

Nokta noktaOlustur(int x, int y) {
  Nokta yeniNokta = {x, y};
  return yeniNokta;
}

int main() {
  Nokta n = noktaOlustur(10, 20);
  printf("x: %d, y: %d\n", n.x, n.y);
  return 0;
}

Kendi Kendini Referans Alan Struct (Linked List’e Hazırlık)

  • Bir struct, kendi türünden bir işaretçi alanı barındırabilir.
  • Bu yapı, bağlı liste düğümü (node) gibi veri yapılarının temelidir.
struct Dugum {
  int veri;
  struct Dugum *sonraki;  // aynı türden işaretçi
};

Typedef ile daha okunur bir kullanım:

typedef struct Dugum {
  int veri;
  struct Dugum *sonraki;
} Dugum;

İç İçe Struct’lar

  • Üyelere erişim, . ile zincirleme yapılır: ogr1.adres.sehir gibi.
#include <stdio.h>
#include <string.h>

typedef struct {
  char cadde[50];
  char sehir[50];
  int postaKodu;
} Adres;

typedef struct {
  char ad[50];
  char soyad[50];
  int numara;
  float notOrtalamasi;
  Adres adres;
} Ogrenci;
int main() {
  Ogrenci ogr1;

  strcpy(ogr1.ad, "Ali");
  strcpy(ogr1.soyad, "Veli");
  ogr1.numara = 12345;

  strcpy(ogr1.adres.cadde, "Çınar Cad.");
  strcpy(ogr1.adres.sehir, "Ankara");
  ogr1.adres.postaKodu = 06000;

  printf("%s %s - %s/%s (%d)\n",
         ogr1.ad, ogr1.soyad,
         ogr1.adres.cadde, ogr1.adres.sehir,
         ogr1.adres.postaKodu);

  return 0;
}

Enum: Sabit Değerler Kümesi Tanımlama

  • enum (enumeration), birbiriyle ilişkili sabit değerler kümesi oluşturmak için kullanılır.
  • Okunabilirliği artırır; “sihirli sayılar” yerine anlamlı isimler kullanılmasını sağlar.

Enum Tanımlama Sözdizimi:

enum enum_adi {
  DEGER1,
  DEGER2
};

Örnek: Renkler Enumu

typedef enum {
  KIRMIZI,
  SARI,
  YESIL
} Renk;

Bu tanımda, KIRMIZI, SARI, YESIL varsayılan olarak sırasıyla 0, 1, 2 değerlerini alır.

Enum Değerlerini Belirleme

Sayısal değerleri elle de belirleyebilirsiniz.

typedef enum {
  KIRMIZI = 5,
  SARI = 10,
  YESIL = 20
} Renk;

Kısmi atama örneği:

typedef enum {
  ELMA = 5,
  ARMUT,     // 6
  MUZ = 20
} Meyve;

Enum Kullanımı (Haftanın Günleri)

#include <stdio.h>

typedef enum {
  PAZARTESI,
  SALI,
  CARSAMBA,
  PERSEMBE,
  CUMA,
  CUMARTESI,
  PAZAR
} Gunler;

int main() {
  Gunler bugun = SALI;

  if (bugun == PAZARTESI) {
    printf("Haftanın ilk günü.\n");
  } else if (bugun == CUMA) {
    printf("Haftanın son iş günü.\n");
  } else if (bugun == CUMARTESI || bugun == PAZAR) {
    printf("Hafta sonu.\n");
  } else {
    printf("Hafta içi.\n");
  }
  return 0;
}

Enum ile Girdi: Cast Zorunlu Değildir, Niyet Belirtir

  • Standart C’de enum’lar çoğunlukla tamsayı gibi işlendiği için (Renk)secim zorunlu değildir.

  • Buna rağmen cast:

    • okunabilirliği artırabilir,
    • “bu değer bir Renk olarak yorumlanacak” niyetini gösterebilir.
  • Önemli olan: Aralık kontrolü ve default gibi güvenlik kontrolleridir.

Örnek

#include <stdio.h>

typedef enum { KIRMIZI, YESIL, MAVI } Renk;

int main() {
  int secim;
  printf("Bir renk seçin (0-KIRMIZI, 1-YESIL, 2-MAVI): ");
  scanf("%d", &secim);

  if (secim < 0 || secim > 2) {
    printf("Geçersiz seçim!\n");
    return 0;
  }

  Renk renk = (Renk)secim;

  switch (renk) {
    case KIRMIZI: printf("Kırmızı seçildi.\n"); break;
    case YESIL:   printf("Yeşil seçildi.\n"); break;
    case MAVI:    printf("Mavi seçildi.\n"); break;
    default:      printf("Geçersiz renk!\n");  break;
  }
  return 0;
}

Union’lar: Belleği Paylaşan Üyeler

  • union, tüm üyelerinin aynı bellek alanını paylaştığı bir veri yapısıdır.
  • Aynı anda yalnızca bir alanın değeri “anlamlı” kabul edilir (genellikle en son yazılan alan).

Not: Union’da “başka bir alana yazdıktan sonra önceki alanı okumak” tanımsız davranış üretebilir.

Union Tanımlama Sözdizimi:

union union_adi {
  veri_tipi uye1;
  veri_tipi uye2;
};

Örnek: Sayı Union’ı

#include <stdio.h>

typedef union {
  int tamSayi;
  float ondalikSayi;
  char karakter;
} Sayi;

int main() {
  Sayi u;

  u.tamSayi = 10;
  printf("tamSayi: %d\n", u.tamSayi);

  u.ondalikSayi = 3.14f;
  printf("ondalikSayi: %.2f\n", u.ondalikSayi);

  u.karakter = 'A';
  printf("karakter: %c\n", u.karakter);

  printf("Union'ın boyutu: %zu bayt\n", sizeof(u));
  return 0;
}

Etiketli Union: Union’ı Güvenli Kullanma Fikri

Union çoğu durumda bir “tür etiketi” ile birlikte anlam kazanır: Hangi alanın geçerli olduğunu ayrıca tutarız.

#include <stdio.h>

typedef enum { TUR_INT, TUR_FLOAT, TUR_CHAR } Tur;

typedef struct {
  Tur tur;
  union {
    int i;
    float f;
    char c;
  } deger;
} Degisken;

int main() {
  Degisken x;

  x.tur = TUR_FLOAT;
  x.deger.f = 2.5f;

  if (x.tur == TUR_FLOAT) {
    printf("float: %.2f\n", x.deger.f);
  }

  return 0;
}

Struct ve Union: Boyut ve Bellek Fikri

  • struct: Üyeler ardışık yerleşir; toplam + padding oluşabilir.
  • union: Tüm üyeler aynı bellek alanını paylaşır; boyut çoğunlukla en büyük üyenin boyutu kadardır.
#include <stdio.h>

typedef struct {
  int i;
  float f;
  char c;
} S;

typedef union {
  int i;
  float f;
  char c;
} U;

int main() {
  printf("sizeof(S) = %zu\n", sizeof(S));
  printf("sizeof(U) = %zu\n", sizeof(U));
  return 0;
}

Basit şema (kavramsal):

  • struct S: [ i ][ f ][ c ]
  • union U: [ i / f / c ] (aynı alan, tek “aktif değer”)

Alıştırmalar

  1. Bir Araba struct’ı oluşturun. Araba struct’ı, arabanın markasını, modelini, üretim yılını ve rengini içersin. Renk için bir enum tanımlayın. Kullanıcıdan 3 arabanın bilgilerini alıp bu bilgileri Araba struct’ı dizisinde saklayan ve ekrana yazdıran bir program yazın.
  1. Bir Nokta struct’ı oluşturun. Nokta struct’ı, noktanın x ve y koordinatlarını içersin. Kullanıcıdan iki noktanın koordinatlarını alıp bu noktalar arasındaki mesafeyi hesaplayan bir program yazın. Mesafe: \(\sqrt{(x_2-x_1)^2 + (y_2-y_1)^2}\)
  1. Bir Tarih struct’ı oluşturun. Tarih struct’ı günü, ayı ve yılı içersin. Kullanıcıdan bir tarih alıp ekrana yazdıran bir program yazın.
  1. Bir Ogrenci struct’ı ve içinde Adres struct’ı olsun. Ogrenci struct’ında öğrencinin adı, soyadı, numarası, Adres struct’ında ise adres bilgileri (cadde, şehir, posta kodu) olsun. Kullanıcıdan 5 öğrencinin bilgilerini alıp bu bilgileri Ogrenci struct’ı dizisinde saklayan ve ekrana yazdıran bir program yazın. Ek olarak, yazdırma işini void ogrencileriYazdir(const Ogrenci dizi[], int n); benzeri bir fonksiyona taşıyın.

Özet

  • Struct: İlişkili alanları tek bir yapı altında toplar; dizilerle çok sayıda kaydı yönetmeyi kolaylaştırır.
  • Typedef: Türlere daha kısa/okunur isim vermeyi sağlar.
  • Hizalama/Padding: sizeof sonucu, üyelerin toplamından büyük olabilir; üye sırası sonucu etkileyebilir.
  • Self-referential struct: Bağlı liste gibi veri yapıları için temel “düğüm” modelidir.
  • . operatörü: Struct üyelerine erişim.
  • -> operatörü: Struct işaretçisi ile üyelere erişim.
  • Enum: İlişkili sabit değerleri isimlendirerek kodu okunur kılar.
  • Union: Aynı bellek alanını paylaşan alanlar; en son yazılan alan geçerlidir.
  • Etiketli Union: Union’ın hangi alanının geçerli olduğunu ayrıca tutarak kullanım niyetini netleştirir.