Jenerik Yer Değiştirme
Coddy'nin C Journey'sinin Object Oriented Programming bölümünün bir parçası — ders 51 / 61.
Programlamada yaygın bir işlem, iki değerin yerini değiştirmektir. Normalde, her tür için ayrı bir takas fonksiyonu yazarsınız — biri int için, diğeri double için vb. Ancak void* ve memcpy ile, herhangi iki değişkeni takas eden tek bir fonksiyon yazabiliriz.
Temel içgörü, takas işleminin (swapping) sadece baytları yer değiştirmekten ibaret olduğudur. Verinin boyutunu biliyorsanız, gerçek türü bilmenize gerek kalmadan baytları kopyalayabilirsiniz. Fonksiyon imzası şu şekildedir:
void generic_swap(void* a, void* b, size_t size);size parametresi fonksiyona kaç bayt taşınacağını söyler. İçeride, baytları yer değiştirmek için geçici bir tampon ve <string.h> kütüphanesinden memcpy kullanıyoruz:
void generic_swap(void* a, void* b, size_t size) {
char temp[size]; // geçici tampon
memcpy(temp, a, size); // temp = a
memcpy(a, b, size); // a = b
memcpy(b, temp, size); // b = temp
}Artık bu tek fonksiyon her tür için çalışıyor:
int x = 5, y = 10;
generic_swap(&x, &y, sizeof(int));
// x artık 10, y artık 5double p = 3.14, q = 2.71;
generic_swap(&p, &q, sizeof(double));
// p artık 2.71, q artık 3.14Bu desen — void* ile size_t kullanımı — C'nin standart kütüphanesinin qsort ve bsearch gibi genel fonksiyonları nasıl uyguladığıdır.
Görev
Kolayvoid* işaretçilerinin ve memcpy fonksiyonunun gücünü kullanarak, türleri ne olursa olsun herhangi iki değişkenin değerlerini değiştirebilen genel bir takas (swap) aracı oluşturalım.
Kodunuzu üç dosya halinde düzenleyeceksiniz:
swap.h: İki adetvoid*işaretçisi ve takas edilecek verinin boyutunu belirten birsize_tparametresi alangeneric_swapfonksiyonunuzu bildirin. Include guard (ekleme korumaları) kullanmayı unutmayın!swap.c:generic_swapfonksiyonunu uygulayın. İki bellek konumu arasında baytları taşımak için geçici bir tampon (buffer) vememcpykullanın. Üç adımlı takas modelini hatırlayın: ilk değeri geçiciye kopyala, ikinciyi ilke kopyala, geçiciyi ikinciye kopyala.main.c: Genel takas işleminizin farklı veri türleriyle çalıştığını gösterin. Bir tür göstergesi (tam sayılar içini, double değerler içind) ve ardından iki değer okuyun. Uygun türde iki değişken oluşturun, bunlarıgeneric_swapfonksiyonunuzu kullanarak takas edin ve takas işleminden sonra her iki değeri de yazdırın.
Programınız üç girdi alacaktır:
- Bir tür göstergesi: tam sayı için
iveya double içind - İlk değer
- İkinci değer
Takas işleminden sonra, her iki değeri de ayrı satırlara yazdırın. Double değerler için 2 ondalık basamak kullanın.
Girdiler i, 5 ve 10 olduğunda örnek çıktı:
10
5Girdiler d, 3.14 ve 2.71 olduğunda örnek çıktı:
2.71
3.14Girdiler i, -7 ve 42 olduğunda örnek çıktı:
42
-7Girdiler d, 99.99 ve 0.01 olduğunda örnek çıktı:
0.01
99.99Bu yaklaşımın güzelliği, generic_swap fonksiyonunuzun hangi türleri takas ettiğine dair hiçbir fikrinin olmamasıdır; o sadece baytları taşır. Aynı fonksiyon tam sayılar, double değerler, struct'lar veya diğer herhangi bir veri türü için çalışır. memcpy için <string.h> ve size_t için <stddef.h> veya <stdlib.h> kütüphanelerini eklemeniz gerekecektir.
Kopya kağıdı
Genel bir takas (swap) fonksiyonu, gerçek türü bilmeden baytları taşıyarak herhangi bir türdeki değerleri takas etmek için void* işaretçilerini ve memcpy fonksiyonunu kullanır.
Fonksiyon imzası:
void generic_swap(void* a, void* b, size_t size);Geçici bir tampon (buffer) ve memcpy kullanarak uygulama:
void generic_swap(void* a, void* b, size_t size) {
char temp[size]; // geçici tampon
memcpy(temp, a, size); // temp = a
memcpy(a, b, size); // a = b
memcpy(b, temp, size); // b = temp
}Farklı türlerle kullanım:
int x = 5, y = 10;
generic_swap(&x, &y, sizeof(int));
// x artık 10, y artık 5
double p = 3.14, q = 2.71;
generic_swap(&p, &q, sizeof(double));
// p artık 2.71, q artık 3.14Gerekli başlık dosyaları: memcpy için <string.h> ve size_t için <stddef.h> veya <stdlib.h>.
Kendin dene
#include <stdio.h>
#include "swap.h"
int main() {
char type;
scanf("%c", &type);
if (type == 'i') {
int a, b;
scanf("%d", &a);
scanf("%d", &b);
// TODO: a ve b değerlerini takas etmek için generic_swap fonksiyonunu çağırın
// Hint: &a, &b ve sizeof(int) kullanın
printf("%d\n", a);
printf("%d\n", b);
} else if (type == 'd') {
double a, b;
scanf("%lf", &a);
scanf("%lf", &b);
// TODO: a ve b değerlerini takas etmek için generic_swap fonksiyonunu çağırın
// Hint: &a, &b ve sizeof(double) kullanın
printf("%.2f\n", a);
printf("%.2f\n", b);
}
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 Kutu7Fonksiyon İşaretçileri
Fonksiyon İşaretçilerini TanımlamaFonksiyon İşaretçilerini ÇağırmaFonksiyon İşaretçileri için TypedefFonksiyonları Argüman Olarak GeçirmeÖzet: Hesap Makinesi Yönlendirme10Jenerik Kapsayıcılar
Void İşaretçiler ÖzetiJenerik SarmalayıcıJenerik Yer DeğiştirmeJenerik KarşılaştırmaÖzet: Jenerik Dizi2Nesneler 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