Programlama Temelleri

10 - Struct ve Union’lar

Emre Can Yılmaz

Ondokuz Mayıs Üniversitesi

2024

Struct’lar: Farklı Veri Tiplerini Bir Araya Getirme

  • struct (structure), C dilinde farklı veri tiplerini bir arada tutan bir veri yapısıdır.

  • Bu sayede, birbiriyle ilişkili verileri daha organize ve anlamlı bir şekilde yönetebiliriz.

Ö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 ve Typedef

  • struct tanımladıktan sonra, bu struct türünden değişkenler oluşturabiliriz.
struct Ogrenci ogrenci1;
struct Ogrenci ogrenciler[100]; // 100 öğrencilik bir dizi
  • typedef kullanarak 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 Üyelerine Erişim

  • Struct üyelerine nokta operatörü (.) ile erişiriz.
Ogrenci ogrenci1;

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

printf("Ad: %s, Numara: %d\n", ogrenci1.ad, ogrenci1.numara);
  • Struct’a bir işaretçi aracılığıyla erişiyorsak, ok operatörünü (->) kullanırız.
Ogrenci ogrenci1;
Ogrenci *ptr = &ogrenci1;

strcpy(ptr->ad, "Ali");
printf("Ad: %s\n", ptr->ad);

Struct’lar ve Fonksiyonlar

  • Struct’lar fonksiyonlara parametre olarak değer veya işaretçi olarak geçirilebilir.
  • Fonksiyonlar, struct türünde değer döndürebilir.

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

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

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

void ogrenciYazdir(Ogrenci ogr) {
  printf("Ad: %s, Numara: %d\n", ogr.ad, ogr.numara);
}

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

Örnek: Öğrenci Bilgilerini Değiştiren Fonksiyon (İş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;
}

İç İçe Struct’lar

  • Struct’lar iç içe tanımlanabilir, bu da daha karmaşık veri yapıları oluşturmamızı sağlar.

Örnek: Adres Bilgisi İçeren Öğrenci Struct’ı

#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");
  // ... diğer bilgiler ...
  strcpy(ogr1.adres.cadde, "Çınar Cad.");
  strcpy(ogr1.adres.sehir, "Ankara");
  ogr1.adres.postaKodu = 06000;

  printf("Adres: %s, %s, %d\n", ogr1.adres.cadde, ogr1.adres.sehir, ogr1.adres.postaKodu);

  return 0;
}

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

enum (enumeration), C dilinde birbiriyle ilişkili sabit değerler kümesi oluşturmak için kullanılan bir veri türüdür. Bu sabit değerler, sayısal değerlere sahip adlandırılmış öğelerdir. Enum’lar, kodunuzu daha anlaşılır ve okunabilir hale getirmek, sabit değerlerin yönetimini kolaylaştırmak ve hata yapma riskini azaltmak için kullanılır.

Enum Tanımlama Sözdizimi:

enum enum_adı {
  değer1,
  değer2,
  // ...
};

Örnek: Renkler Enumu

typedef enum {
  KIRMIZI,
  SARI,
  YESIL
} Renk;

Bu kodda, Renk adında bir enum türü tanımladık. KIRMIZI, SARI ve YESIL, varsayılan olarak sırasıyla 0, 1 ve 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;

Veya kısmi olarak:

enum meyve {elma = 5, armut, muz = 20}; // armut = 6

Enum Kullanımı

#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;
}

Örnek

#include <stdio.h>

typedef enum { KIRMIZI, YESIL, MAVI } Renk;

int main() {
    int renkSecimi;

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

    // Kullanıcının girdiği değeri enum türüne cast ediyoruz
    Renk renk = (Renk)renkSecimi;

    // Şimdi renk değişkenini switch-case içinde kullanabiliriz
    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 seçimi!\n");
    }

    return 0;
}

Açıklama:

  1. Kullanıcıdan tam sayı olarak girdi al: Kullanıcıya enum değerlerinin sayısal karşılıklarını (0, 1, 2 gibi) gösteren bir mesaj yazdırılır ve scanf fonksiyonu ile tam sayı olarak girdi alınır.
  2. enum türüne cast et: Alınan tam sayı değeri, (Renk)renkSecimi ifadesi ile Renk enum türüne dönüştürülür (cast edilir). Bu işlem, derleyiciye değişkenin Renk türünde olduğunu ve enum sabitleriyle karşılaştırılabileceğini söyler.
  3. switch-case içinde kullan: Cast edilmiş renk değişkeni, switch-case yapısı içinde kullanılabilir. case etiketlerinde, enum sabitleri (KIRMIZI, YESIL, MAVI) kullanılır.

Bu şekilde, kullanıcıdan alınan sayısal girdi, enum türüne dönüştürülerek switch-case yapısı içinde kullanılabilir.

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

union, tüm üyelerinin aynı bellek alanını paylaştığı bir veri yapısıdır. Herhangi bir anda sadece bir üyesinin değeri saklanabilir. Union’lar, farklı türden verileri aynı bellek alanında saklamak ve bellek kullanımını optimize etmek istediğimiz durumlarda kullanışlıdır.

Union Tanımlama Sözdizimi:

union union_adı {
  veri_tipi üye1;
  veri_tipi üye2;
  // ...
};

Örnek: Sayı Union’ı

#include <stdio.h>

typedef union {
  int tamSayi;
  float ondalikSayi;
  char karakter;
} Sayi;
int main() {
    Sayi birlesikDegisken;

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

    birlesikDegisken.ondalikSayi = 3.14f; // float değer ataması
    printf("ondalikSayi: %.2f\n", birlesikDegisken.ondalikSayi); // Dikkat! tamSayi'nin değeri artık geçerli değil.
    printf("tamSayi: %d\n", birlesikDegisken.tamSayi); // Beklenmedik bir değer yazdırabilir.

    birlesikDegisken.karakter = 'A'; // char değer ataması
    printf("karakter: %c\n", birlesikDegisken.karakter);

    printf("Union'ın boyutu: %zu bayt\n", sizeof(birlesikDegisken));

    return 0;
}

Union Kullanımı ve Hangi Üyenin Aktif Olduğunu Belirleme

Union kullanırken, hangi üyenin aktif olduğunu (yani hangi tür verinin saklandığını) takip etmek önemlidir. Aksi takdirde, yanlış üyeye erişirseniz beklenmedik sonuçlar alabilirsiniz. Bu genellikle ayrı bir değişken veya enum ile yapılır.

Örnek

#include <stdio.h>

typedef enum {
  TAMSAYI,
  ONDALIKLI,
  KARAKTER
} VeriTipi;

typedef union {
  int tamSayi;
  float ondalikSayi;
  char karakter;
} Deger;
typedef struct {
  VeriTipi tip;
  Deger deger;
} SayiBilgisi;
int main() {
  SayiBilgisi sayi;

  // Tamsayı değeri atama
  sayi.tip = TAMSAYI;
  sayi.deger.tamSayi = 10;
  printf("Tam Sayı: %d\n", sayi.deger.tamSayi);

  // Ondalıklı sayı değeri atama
  sayi.tip = ONDALIKLI;
  sayi.deger.ondalikSayi = 3.14;
  printf("Ondalık Sayı: %.2f\n", sayi.deger.ondalikSayi);

  // Karakter değeri atama
  sayi.tip = KARAKTER;
  sayi.deger.karakter = 'A';
  printf("Karakter: %c\n", sayi.deger.karakter);

  if (sayi.tip == TAMSAYI) {
      printf("Aktif değer: %d\n", sayi.deger.tamSayi);
  } else if (sayi.tip == ONDALIKLI) {
      printf("Aktif değer: %.2f\n", sayi.deger.ondalikSayi);
  } else if (sayi.tip == KARAKTER) {
      printf("Aktif değer: %c\n", sayi.deger.karakter);
  }

  return 0;
}

Alıştırmalar

Aşağıdaki alıştırmaları yapın.

  1. Bir Araba struct’ı oluşturun. Araba struct’ı, arabanın markasını, modelini, üretim yılını ve rengini içersin. 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. (İki nokta arasındaki mesafe formülü: √((x2-x1)² + (y2-y1)²))
  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.
  1. Klavyeden girilen bir sayının 1’den o sayıya kadar olan tüm çarpanlarını ekrana yazdırın. for veya while döngülerini kullanabilirsiniz.

Özet

  • Struct: Farklı veri tiplerini bir arada tutan yapılar.
  • Typedef: Veri tiplerine takma isim verme.
  • . operatörü: Struct üyelerine erişim.
  • -> operatörü: Struct işaretçisi ile üyelere erişim.
  • Enum: Sabit değerler kümesi tanımlama.
  • Union: Aynı bellek bölgesini paylaşan üyeler.

Gelecek Hafta

  • Dosya İşlemleri