Diziler ve Pointer Arasındaki İlişki
Dizilerin bellekte gösterimi, decay kuralı ve pointer aritmetiği ile erişim.
C++’ta diziler (arrays) ve pointer’lar (göstericiler) birbirleriyle yakından ilişkilidir. Çünkü diziler bellekte ardışık olarak saklanır ve dizinin adı aslında ilk elemanının adresini temsil eder. Bu nedenle bir dizi üzerinde pointer aritmetiği kullanarak dolaşmak mümkündür. Bu makalede dizilerin bellek yapısını, pointer ilişkisini ve pratik kullanım örneklerini öğreneceğiz.
1. Dizi ve Bellek Düzeni
C++’ta bir dizi tanımlandığında, tüm elemanlar bellekte yan yana (ardışık) saklanır. Örneğin:
#include <iostream>
using namespace std;
int main() {
int sayilar[3] = {10, 20, 30};
cout << "1. eleman adresi: " << &sayilar[0] << endl;
cout << "2. eleman adresi: " << &sayilar[1] << endl;
cout << "3. eleman adresi: " << &sayilar[2] << endl;
return 0;
}
Çıktı adresleri art arda gelecektir:
1. eleman adresi: 0x61ff0c
2. eleman adresi: 0x61ff10
3. eleman adresi: 0x61ff14
Her int 4 byte yer kapladığı için adresler 4’er 4’er artar.
2. Dizi Adı Bir Pointer’dır
Dizinin adı (sayilar), aslında ilk elemanın adresini gösterir.
Yani sayilar ve &sayilar[0] aynı adresi ifade eder.
int sayilar[3] = {10, 20, 30};
cout << sayilar << endl; // ilk elemanın adresi
cout << &sayilar[0] << endl; // aynı adres
Bu nedenle dizi üzerinde pointer kullanarak dolaşmak mümkündür.
3. Pointer ile Dizi Elemanlarına Erişim
Bir pointer dizinin ilk elemanını gösterirse, pointer aritmetiği ile diğer elemanlara ulaşılabilir.
#include <iostream>
using namespace std;
int main() {
int sayilar[4] = {5, 10, 15, 20};
int *p = sayilar; // p = &sayilar[0]
cout << *p << endl; // 5
cout << *(p + 1) << endl; // 10
cout << *(p + 2) << endl; // 15
cout << *(p + 3) << endl; // 20
}
*(p + i) ifadesi, sayilar[i] ile tamamen aynıdır.
Bu yapı, pointer aritmetiğinin temelini oluşturur.
4. Pointer Aritmetiği
Pointer’lar, türlerine göre bellek adresinde otomatik olarak uygun miktarda ilerler. Örneğin:
int dizi[3] = {1, 2, 3};
int *ptr = dizi;
cout << ptr << endl; // ilk elemanın adresi
ptr++;
cout << ptr << endl; // bir sonraki elemanın adresi
ptr++ ifadesi aslında adresi 4 byte ileri taşır (int = 4 byte).
Bu, pointer türüne göre otomatik hesaplanır.
double türünde 8 byte, char türünde 1 byte ilerler.
5. Pointer ve Dizi Döngüsü
Dizi elemanlarını pointer ile gezmek için döngüler kullanılabilir.
int sayilar[] = {2, 4, 6, 8, 10};
int *p = sayilar;
for (int i = 0; i < 5; i++) {
cout << *(p + i) << " ";
}
Alternatif olarak pointer doğrudan artırılabilir:
for (int *ptr = sayilar; ptr < sayilar + 5; ptr++) {
cout << *ptr << " ";
}
Her iki yöntem de aynı sonucu üretir:
2 4 6 8 10
6. Dizi Parametreli Fonksiyonlar
Fonksiyonlara dizi gönderildiğinde aslında dizinin adresi gönderilir. Bu sayede fonksiyon diziyi doğrudan değiştirebilir.
#include <iostream>
using namespace std;
void Doldur(int *dizi, int uzunluk) {
for (int i = 0; i < uzunluk; i++)
dizi[i] = (i + 1) * 5;
}
void Yazdir(const int *dizi, int uzunluk) {
for (int i = 0; i < uzunluk; i++)
cout << dizi[i] << " ";
}
int main() {
int sayilar[5];
Doldur(sayilar, 5);
Yazdir(sayilar, 5);
}
Doldur fonksiyonu, diziyi doğrudan bellekte değiştirdiği için
main() fonksiyonundaki dizinin değerleri güncellenir.
7. Çok Boyutlu Diziler ve Pointer İlişkisi
Çok boyutlu diziler de bellekte ardışık olarak tutulur. Ancak pointer aritmetiği biraz daha dikkatli yapılmalıdır.
#include <iostream>
using namespace std;
int main() {
int matris[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
cout << *(*(matris + 0) + 2) << endl; // 3
cout << *(*(matris + 1) + 0) << endl; // 4
}
Burada matris → ilk satırın adresidir.
*(matris + 1) → ikinci satıra gider,
*(*(matris + 1) + 0) → o satırın ilk elemanına ulaşır.
8. Pointer ve Dinamik Diziler
Sabit boyutlu dizilerin aksine, dinamik diziler new anahtar kelimesiyle oluşturulur.
Bu diziler heap bellekte saklanır ve boyutları çalışma zamanında belirlenebilir.
int *dizi = new int[5];
for (int i = 0; i < 5; i++)
dizi[i] = (i + 1) * 10;
for (int i = 0; i < 5; i++)
cout << dizi[i] << " ";
delete[] dizi; // Belleği serbest bırak
Dinamik diziler manuel bellek yönetimi gerektirir.
new ile ayrılan bellek, iş bittikten sonra mutlaka delete[] ile serbest bırakılmalıdır.
9. const Pointer ile Dizi Okuma
Eğer fonksiyonun diziyi değiştirmesini istemiyorsak, pointer parametresi const olarak tanımlanabilir.
void Yazdir(const int *dizi, int uzunluk) {
for (int i = 0; i < uzunluk; i++)
cout << dizi[i] << " ";
}
Bu yöntem, veri bütünlüğünü korur ve dizinin yanlışlıkla değiştirilmesini önler.
10. TL;DR
- Dizinin adı (
sayilar) → ilk elemanın adresidir (&sayilar[0]). *(p + i)↔dizi[i]eşdeğerdir.- Pointer aritmetiği, tür boyutuna göre adresi otomatik ilerletir.
- Diziler fonksiyonlara adres olarak geçer, kopyalanmaz.
- Çok boyutlu diziler pointer zinciriyle erişilebilir (
*(*(matris+i)+j)). - Dinamik diziler
newile oluşturulupdelete[]ile serbest bırakılmalıdır. - Tüm örnekler Visual Studio 2022 veya GCC 11+ derleyicilerinde çalıştırılabilir.