C Programlama Dili'nde standart dizilerin boyutu sabit olmak zorundadır (derleme zamanında belirlenir). Veri miktarı önceden bilinmediğinde veya program çalışırken değiştiğinde, standart diziler yetersiz kalır. İşte bu noktada dinamik diziler devreye girer. Dinamik bir dizinin temel amacı, eleman ekleme veya çıkarma durumunda belleği otomatik olarak yöneterek, dizi boyutunu ihtiyaca göre ayarlamaktır.
C Programlama Dili'nde doğrudan yerleşik bir "Vector" veri yapısı (C++'taki std::vector gibi dinamik dizi konteyneri) yoktur.
Dinamik dizi, boyutu çalışma anında (runtime) değişebilen ve gerektiğinde otomatik olarak büyüyüp küçülebilen dizi benzeri bir yapıdır. Bu, C++'taki std::vector'ın temel özelliğidir.
C Programa Dili, bu dinamik dizi özelliğini doğrudan sağlamasa da, işaretçiler (pointers) ve bellek yönetim fonksiyonları (malloc, calloc, realloc, free) kullanılarak bu yapı simüle edilebilir.
Temel bileşenler
Bu yapıyı oluşturmak için genellikle bir yapı (struct) kullanılır:
İşleyiş
Aşağıdaki program C Programlama Dili'nde, C++'taki std::vector veya diğer dillerdeki dinamik dizi/liste yapılarına benzer şekilde, dinamik olarak büyüyebilen bir tamsayı dizisi (Vector olarak adlandırılmış) uygulamasını göstermektedir. Böylece, programın çalışması sırasında ihtiyaca göre bellek tahsis etme ve serbest bırakma (dinamik bellek yönetimi) yöntemi ile dizinin boyutu çalışma anında gerektiği gibi büyütüp küçültülür.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <locale.h>
// Dinamik dizi (Vector) yapısını tanımlama
typedef struct {
int* data; // Verilerin tutulacağı işaretçi (dinamik bellek)
size_t capacity; // Ayrılmış olan toplam bellek kapasitesi (kaç eleman alabilir)
size_t size; // Şu anki eleman sayısı
} Vector;
void vector_init(Vector *v, size_t initial_capacity);
void vector_push_back(Vector *v, int element);
int vector_get(Vector *v, size_t index);
void vector_set(Vector *v, size_t index, int element);
void vector_free(Vector *v);
int vector_pop_back(Vector *v);
void vector_insert(Vector *v, size_t index, int element);
int vector_delete(Vector *v, size_t index);
int vector_is_empty(const Vector *v);
void vector_clear(Vector *v);
void vector_shrink_to_fit(Vector *v);
void print_vector(const char *label, const Vector *v);
// Ana program
int main(void)
{
setlocale(LC_ALL, "Turkish");
// 1. Başlangıç: 5 başlangıç kapasiteli bir vektör oluştur
Vector my_vector;
vector_init(&my_vector, 5);
print_vector("Başlangıç durumu", &my_vector);
// 2. Eleman Ekleme (push_back)
for (int i = 0; i < 15; i++) {
vector_push_back(&my_vector, i * 10);
}
print_vector("15 Eleman eklendikten sonra (push_back)", &my_vector);
// 3. vector_pop_back Kullanımı: Son 3 elemanı çıkar
for (int i = 0; i < 3; i++) {
int popped_val = vector_pop_back(&my_vector);
printf("\nÇıkarılan eleman (pop_back): %d", popped_val);
}
printf("\n");
print_vector("Pop_back işleminden sonra", &my_vector);
// 4. vector_insert Kullanımı: İndex 5'e 1000 ve Index 0'a 2000 ekle
vector_insert(&my_vector, 5, 1000);
vector_insert(&my_vector, 0, 2000);
print_vector("Insert işleminden sonra (0'a 2000, 5'e 1000)", &my_vector);
// 5. vector_delete Kullanımı: İndex 1'deki elemanı sil
size_t delete_index = 1;
int deleted_val = vector_delete(&my_vector, delete_index);
printf("\nSilinen eleman (index %Iu): %d\n", delete_index, deleted_val);
print_vector("Delete işleminden sonra", &my_vector);
// 6. vector_is_empty kontrolü
printf("\nVektör boş mu (is_empty): %s\n", vector_is_empty(&my_vector) ? "Evet" : "Hayır");
// 7. vector_clear kullanımı: Tüm elemanları temizle
vector_clear(&my_vector);
print_vector("Clear işleminden sonra", &my_vector);
// 8. vector_is_empty tekrar kontrolü
printf("\nVektör boş mu (clear sonrası is_empty): %s\n", vector_is_empty(&my_vector) ? "Evet" : "Hayır");
// 9. Küçültme (Shrink to Fit)
// Clear işleminden sonra size=0, capacity=32 kalmıştır.
vector_shrink_to_fit(&my_vector);
print_vector("Shrink_to_fit işleminden sonra", &my_vector);
// 10. Belleği serbest bırakma
vector_free(&my_vector);
printf("\n*** Vektor belleği serbest bırakıldı. ***\n");
return 0;
}
// Vektörü başlatma (constructor)
void vector_init(Vector *v, size_t initial_capacity)
{
v->size = 0;
v->capacity = initial_capacity > 0 ? initial_capacity : 1; // Başlangıç kapasitesi en az 1 olmalı
// Başlangıç kapasitesi kadar yer ayırma
v->data = (int*)malloc(v->capacity * sizeof(int));
if (v->data == NULL) {
perror("Bellek tahsisi başarısız oldu!");
exit(EXIT_FAILURE);
}
}
// Vektöre eleman ekleme (push_back benzeri)
void vector_push_back(Vector *v, int element)
{
// Kapasite kontrolü: Eğer eleman sayısı kapasiteye ulaşmışsa, kapasiteyi artır
if (v->size >= v->capacity) {
// Kapasiteyi iki katına çıkar
v->capacity *= 2;
// Yeni kapasite ile belleği yeniden tahsis et
int *new_data = (int*)realloc(v->data, v->capacity * sizeof(int));
if (new_data == NULL) {
perror("Bellek yeniden tahsisi başarısız oldu!");
exit(EXIT_FAILURE);
}
v->data = new_data;
}
// Elemanı dizinin sonuna ekle
v->data[v->size++] = element;
}
// Belirli bir indeksteki elemana erişme
int vector_get(Vector *v, size_t index)
{
if (index >= v->size) {
fprintf(stderr, "Hata: İndeks sınırların dışında (%Iu, boyut: %Iu)\n", index, v->size);
exit(EXIT_FAILURE);
}
return v->data[index];
}
// Belirli bir indeksteki elemanı ayarlama
void vector_set(Vector *v, size_t index, int element)
{
if (index >= v->size) {
fprintf(stderr, "Hata: İndeks sınırların dışında (%Iu, boyut: %Iu)\n", index, v->size);
exit(EXIT_FAILURE);
}
v->data[index] = element;
}
// Vektörün kullandığı belleği serbest bırakma (destructor)
void vector_free(Vector *v)
{
if (v->data != NULL) {
free(v->data); // Ayrılmış belleği serbest bırak
v->data = NULL; // İşaretçiyi NULL yap
}
v->size = 0;
v->capacity = 0;
}
// Vektörün son elemanını çıkarır ve döndürür (Vektör boşsa hata döndürür).
int vector_pop_back(Vector *v)
{
if (v->size == 0) {
fprintf(stderr, "Hata: Boş vektörden eleman çıkarılamaz!\n");
exit(EXIT_FAILURE);
}
// Boyutu azalt ve o konumdaki elemanı döndür
return v->data[--v->size];
}
// Belirli bir indekse eleman ekler
void vector_insert(Vector *v, size_t index, int element)
{
if (index > v->size) { // index == size ise push_back olur, o yüzden > size kontrolü yeterli
fprintf(stderr, "Hata: Ekleme indeksi sınırların dışında (%Iu, boyut: %Iu)\n", index, v->size);
exit(EXIT_FAILURE);
}
// Kapasiteyi kontrol et ve gerekirse artır (push_back mantığının aynısı)
if (v->size >= v->capacity) {
v->capacity *= 2;
if (v->capacity == 0) v->capacity = 1; // 0 kapasite durumunu ele al
int *new_data = (int*)realloc(v->data, v->capacity * sizeof(int));
if (new_data == NULL) {
perror("Bellek yeniden tahsisi başarısız oldu!");
exit(EXIT_FAILURE);
}
v->data = new_data;
}
// Elemanları sağa kaydır:
// Kaydırılacak eleman sayısı: v->size - index
// memmove: Çakışan bellek bölgelerinde güvenli kopyalama yapar.
// Hedef: v->data + index + 1
// Kaynak: v->data + index
// Boyut: (v->size - index) * sizeof(int)
memmove(v->data + index + 1, v->data + index, (v->size - index) * sizeof(int));
// Yeni elemanı hedef indekse yerleştir
v->data[index] = element;
v->size++;
}
// Belirli bir indeksteki elemanı siler ve değerini döndürür.
int vector_delete(Vector *v, size_t index)
{
if (index >= v->size) {
fprintf(stderr, "Hata: Silme indeksi sınırların dışında (%Iu, boyut: %Iu)\n", index, v->size);
exit(EXIT_FAILURE);
}
int deleted_element = v->data[index]; // Silinecek elemanı kaydet
// Eğer silinen eleman son eleman değilse, kalan elemanları sola kaydır:
if (index < v->size - 1) {
// Hedef: v->data + index (silinen elemanın üzerine yazılacak yer)
// Kaynak: v->data + index + 1 (kaydırılacak ilk eleman)
// Boyut: (v->size - 1 - index) * sizeof(int)
memmove(v->data + index, v->data + index + 1, (v->size - 1 - index) * sizeof(int));
}
v->size--; // Boyutu azalt
return deleted_element;
}
// Vektörün boş olup olmadığını kontrol eder. Vektör boşsa 1, doluysa 0 döndürür.
int vector_is_empty(const Vector *v)
{
return v->size == 0;
}
// Vektördeki tüm elemanları siler (size'ı 0 yapar). Kapasiteyi korur.
void vector_clear(Vector *v)
{
v->size = 0;
}
// Kapasiteyi eleman sayısına (size) eşitlemek için küçültür.
void vector_shrink_to_fit(Vector *v)
{
// Eğer size ve capacity eşitse veya size 0 ise küçültmeye gerek yok.
if (v->size == v->capacity || v->size == 0) {
return;
}
// Yeni kapasiteyi eleman sayısına ayarla
size_t new_capacity = v->size;
// realloc ile yeni boyutu tahsis et
int *new_data = (int*)realloc(v->data, new_capacity * sizeof(int));
// realloc NULL döndürürse (hata), mevcut belleği koruruz.
if (new_data == NULL) {
// Bellek hatası olsa bile mevcut veriyi korumak daha iyidir.
// Hata raporlanabilir, ama programı sonlandırmaya gerek yok.
// fprintf(stderr, "Uyarı: Bellek küçültme başarısız oldu.\n");
return;
}
v->data = new_data;
v->capacity = new_capacity;
}
// Yardımcı Yazdırma Fonksiyonu
void print_vector(const char *label, const Vector *v)
{
printf("\n--- %s ---\n", label);
printf("Boyut: %Iu, Kapasite: %Iu, Boş mu: %s\n",
v->size, v->capacity, vector_is_empty(v) ? "Evet" : "Hayır");
printf("Elemanlar: [");
for (size_t i = 0; i < v->size; i++) {
printf("%d%s", v->data[i], (i == v->size - 1) ? "" : ", ");
}
printf("]\n");
}
Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:
--- Başlangıç durumu --- Boyut: 0, Kapasite: 5, Boş mu: Evet Elemanlar: [] --- 15 Eleman eklendikten sonra (push_back) --- Boyut: 15, Kapasite: 20, Boş mu: Hayır Elemanlar: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140] Çıkarılan eleman (pop_back): 140 Çıkarılan eleman (pop_back): 130 Çıkarılan eleman (pop_back): 120 --- Pop_back işleminden sonra --- Boyut: 12, Kapasite: 20, Boş mu: Hayır Elemanlar: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110] --- Insert işleminden sonra (0'a 2000, 5'e 1000) --- Boyut: 14, Kapasite: 20, Boş mu: Hayır Elemanlar: [2000, 0, 10, 20, 30, 40, 1000, 50, 60, 70, 80, 90, 100, 110] Silinen eleman (index 1): 0 --- Delete işleminden sonra --- Boyut: 13, Kapasite: 20, Boş mu: Hayır Elemanlar: [2000, 10, 20, 30, 40, 1000, 50, 60, 70, 80, 90, 100, 110] Vektör boş mu (is_empty): Hayır --- Clear işleminden sonra --- Boyut: 0, Kapasite: 20, Boş mu: Evet Elemanlar: [] Vektör boş mu (clear sonrası is_empty): Evet --- Shrink_to_fit işleminden sonra --- Boyut: 0, Kapasite: 20, Boş mu: Evet Elemanlar: [] *** Vektor belleği serbest bırakıldı. ***
main() fonksiyonunda aşağıdaki sırayla işlemler uygulanır:
Aşağıdaki program, önceki programdaki int değer yerine, daha karmaşık bir kullanıcı tanımlı yapı olan DataItem'ı tutan dinamik bir dizi (Vector) uygulamasıdır.
Artık vektördeki her eleman, bir int id, bir char code, bir float value ve bir char name[50] içeren bir yapıdır.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <locale.h>
// Veri yapısı tanımı
typedef struct {
int id;
char code;
float value;
char name[50]; // Sabit boyutlu bir karakter dizisi (string)
} DataItem;
// Dinamik dizi (Vector) yapısını tanımlama
typedef struct {
DataItem* data;
size_t capacity;
size_t size;
} Vector;
// Fonksiyon prototipleri
void vector_init(Vector *v, size_t initial_capacity);
void vector_push_back(Vector *v, DataItem element);
DataItem vector_get(Vector *v, size_t index);
void vector_set(Vector *v, size_t index, DataItem element);
void vector_free(Vector *v);
DataItem vector_pop_back(Vector *v);
void vector_insert(Vector *v, size_t index, DataItem element);
DataItem vector_delete(Vector *v, size_t index);
int vector_is_empty(const Vector *v);
void vector_clear(Vector *v);
void vector_shrink_to_fit(Vector *v);
void print_vector(const char *label, const Vector *v);
// Ana program
int main(void)
{
setlocale(LC_ALL, "Turkish");
// 1. Başlatma ve eleman ekleme
Vector my_vector;
vector_init(&my_vector, 3);
// 5 eleman ekle, kapasitenin artmasını sağla (3 -> 6)
for (int i = 0; i < 5; i++) {
DataItem item = {.id = 100 + i, .code = 'A' + i, .value = 10.0f * (i + 1)};
char buffer[20];
snprintf(buffer, sizeof(buffer), "Kayit_%d", i + 1);
strcpy(item.name, buffer);
vector_push_back(&my_vector, item);
}
print_vector("Başlangıç ekleme durumu", &my_vector); // Boyut=5, Kapasite=6
// 2. vector_pop_back Kullanımı
DataItem popped_val = vector_pop_back(&my_vector); // Kayit_5 silinir
printf("\nPOP_BACK İŞLEMİ: Çıkarılan ID: %d\n", popped_val.id);
print_vector("Pop_back Sonrası", &my_vector); // Boyut=4
// 3. vector_insert Kullanımı
DataItem ins_item = {.id = 777, .code = 'X', .value = 77.7f};
strcpy(ins_item.name, "INSERTED_KAYIT");
vector_insert(&my_vector, 1, ins_item); // Index 1'e ekle
print_vector("Insert (Index 1) sonrası", &my_vector); // Boyut=5
// 4. vector_delete Kullanımı
DataItem deleted_val = vector_delete(&my_vector, 3); // Index 3'teki elemanı sil
printf("\nDELETE İŞLEMİ: Silinen ID: %d\n", deleted_val.id);
print_vector("Delete (Index 3) sonrası", &my_vector); // Boyut=4
// 5. vector_shrink_to_fit Kullanımı
printf("\n*** vector_shrink_to_fit çalıştırılıyor... ***");
vector_shrink_to_fit(&my_vector);
print_vector("Shrink_to_fit Sonrası", &my_vector); // Boyut=4, Kapasite=4
// 6. vector_clear ve boş kontrolü
vector_clear(&my_vector);
print_vector("Clear işleminden sonra", &my_vector); // Boyut=0
// 7. Belleği serbest bırakma
vector_free(&my_vector);
printf("\n*** Vektor belleği serbest bırakıldı. ***\n");
return 0;
}
void vector_init(Vector *v, size_t initial_capacity)
{
v->size = 0; v->capacity = initial_capacity > 0 ? initial_capacity : 1;
v->data = (DataItem*) malloc(v->capacity * sizeof(DataItem));
if (v->data == NULL) { perror("Bellek tahsisi başarısız oldu!"); exit(EXIT_FAILURE); }
}
void vector_push_back(Vector *v, DataItem element)
{
if (v->size >= v->capacity) {
v->capacity *= 2;
DataItem *new_data = (DataItem*)realloc(v->data, v->capacity * sizeof(DataItem));
if (new_data == NULL) { perror("Bellek yeniden tahsisi başarısız oldu!"); exit(EXIT_FAILURE); }
v->data = new_data;
}
v->data[v->size++] = element;
}
DataItem vector_get(Vector *v, size_t index)
{
if (index >= v->size) { fprintf(stderr, "Hata: İndeks sınırların dışında\n"); exit(EXIT_FAILURE); }
return v->data[index];
}
void vector_set(Vector *v, size_t index, DataItem element)
{
if (index >= v->size) { fprintf(stderr, "Hata: İndeks sınırların dışında\n"); exit(EXIT_FAILURE); }
v->data[index] = element;
}
void vector_free(Vector *v)
{
if (v->data != NULL) { free(v->data); v->data = NULL; }
v->size = 0; v->capacity = 0;
}
DataItem vector_pop_back(Vector *v)
{
if (v->size == 0) { fprintf(stderr, "Hata: Boş vektörden eleman çıkarılamaz!\n"); exit(EXIT_FAILURE); }
return v->data[--v->size];
}
void vector_insert(Vector *v, size_t index, DataItem element)
{
if (index > v->size) { fprintf(stderr, "Hata: Ekleme indeksi sınırların dışında\n"); exit(EXIT_FAILURE); }
if (v->size >= v->capacity) {
v->capacity *= 2;
if (v->capacity == 0) v->capacity = 1;
DataItem *new_data = (DataItem*)realloc(v->data, v->capacity * sizeof(DataItem));
if (new_data == NULL) { perror("Bellek yeniden tahsisi başarısız oldu!"); exit(EXIT_FAILURE); }
v->data = new_data;
}
memmove(v->data + index + 1, v->data + index, (v->size - index) * sizeof(DataItem));
v->data[index] = element;
v->size++;
}
DataItem vector_delete(Vector *v, size_t index)
{
if (index >= v->size) { fprintf(stderr, "Hata: Silme indeksi sınırların dışında\n"); exit(EXIT_FAILURE); }
DataItem deleted_element = v->data[index];
if (index < v->size - 1) {
memmove(v->data + index, v->data + index + 1, (v->size - 1 - index) * sizeof(DataItem));
}
v->size--;
return deleted_element;
}
int vector_is_empty(const Vector *v)
{
return v->size == 0;
}
void vector_clear(Vector *v)
{
v->size = 0;
}
void vector_shrink_to_fit(Vector *v)
{
if (v->size == v->capacity) return;
if (v->size == 0) {
if (v->data != NULL) { free(v->data); v->data = NULL; }
v->capacity = 0;
return;
}
size_t new_capacity = v->size;
DataItem *new_data = (DataItem*)realloc(v->data, new_capacity * sizeof(DataItem));
if (new_data == NULL) return; // Hata durumunda mevcut veriyi koru
v->data = new_data;
v->capacity = new_capacity;
}
void print_vector(const char *label, const Vector *v)
{
printf("\n======================================================\n");
printf("--- %s ---\n", label);
printf("Genel Durum: Boyut: %Iu, Kapasite: %Iu, Boş mu: %s\n",
v->size, v->capacity, vector_is_empty(v) ? "Evet" : "Hayır");
if (v->size == 0) {
printf("Vektörde eleman yok.\n");
} else {
printf("------------------------------------------------------\n");
for (size_t i = 0; i < v->size; i++) {
DataItem item = v->data[i];
printf("Index %Iu: [ID=%d, Code='%c', Value=%.2f, Name='%s']\n",
i, item.id, item.code, item.value, item.name);
}
}
printf("======================================================\n");
}
Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:
====================================================== --- Başlangıç Ekleme Durumu --- Genel Durum: Boyut: 5, Kapasite: 6, Boş mu: Hayır ------------------------------------------------------ Index 0: [ID=100, Code='A', Value=10,00, Name='Kayit_1'] Index 1: [ID=101, Code='B', Value=20,00, Name='Kayit_2'] Index 2: [ID=102, Code='C', Value=30,00, Name='Kayit_3'] Index 3: [ID=103, Code='D', Value=40,00, Name='Kayit_4'] Index 4: [ID=104, Code='E', Value=50,00, Name='Kayit_5'] ====================================================== POP_BACK İŞLEMİ: Çıkarılan ID: 104 ====================================================== --- Pop_back Sonrası --- Genel Durum: Boyut: 4, Kapasite: 6, Boş mu: Hayır ------------------------------------------------------ Index 0: [ID=100, Code='A', Value=10,00, Name='Kayit_1'] Index 1: [ID=101, Code='B', Value=20,00, Name='Kayit_2'] Index 2: [ID=102, Code='C', Value=30,00, Name='Kayit_3'] Index 3: [ID=103, Code='D', Value=40,00, Name='Kayit_4'] ====================================================== ====================================================== --- Insert (Index 1) Sonrası --- Genel Durum: Boyut: 5, Kapasite: 6, Boş mu: Hayır ------------------------------------------------------ Index 0: [ID=100, Code='A', Value=10,00, Name='Kayit_1'] Index 1: [ID=777, Code='X', Value=77,70, Name='INSERTED_KAYIT'] Index 2: [ID=101, Code='B', Value=20,00, Name='Kayit_2'] Index 3: [ID=102, Code='C', Value=30,00, Name='Kayit_3'] Index 4: [ID=103, Code='D', Value=40,00, Name='Kayit_4'] ====================================================== DELETE İŞLEMİ: Silinen ID: 102 ====================================================== --- Delete (Index 3) Sonrası --- Genel Durum: Boyut: 4, Kapasite: 6, Boş mu: Hayır ------------------------------------------------------ Index 0: [ID=100, Code='A', Value=10,00, Name='Kayit_1'] Index 1: [ID=777, Code='X', Value=77,70, Name='INSERTED_KAYIT'] Index 2: [ID=101, Code='B', Value=20,00, Name='Kayit_2'] Index 3: [ID=103, Code='D', Value=40,00, Name='Kayit_4'] ====================================================== *** vector_shrink_to_fit çalıştırılıyor... *** ====================================================== --- Shrink_to_fit Sonrası --- Genel Durum: Boyut: 4, Kapasite: 4, Boş mu: Hayır ------------------------------------------------------ Index 0: [ID=100, Code='A', Value=10,00, Name='Kayit_1'] Index 1: [ID=777, Code='X', Value=77,70, Name='INSERTED_KAYIT'] Index 2: [ID=101, Code='B', Value=20,00, Name='Kayit_2'] Index 3: [ID=103, Code='D', Value=40,00, Name='Kayit_4'] ====================================================== ====================================================== --- Clear İşleminden Sonra --- Genel Durum: Boyut: 0, Kapasite: 4, Boş mu: Evet Vektörde eleman yok. ====================================================== *** Vektor belleği serbest bırakıldı. ***
main() fonksiyonunda aşağıdaki sırayla işlemler uygulanır: