C dilinde, işaretçiler (pointers) bellekte bulunan verilerin adreslerini saklar. İşaretçi değişkenleri, verilerin bellekteki adreslerini saklar ve bu adreslerle çalışır. İşaretçiler, verileri bellekte direk olarak erişmenizi sağlar ve bu sayede verileri daha hızlı işleyebilirsiniz. Ayrıca, fonksiyonlar arasında veri paylaşımı yaparken de işaretçiler kullanılabilir. Özellikle dinamik bellek yönetiminde işaretçiler önemli bir rol oynamaktadır.
Örneğin, bir tam sayı değişkeni oluşturalım ve bu değişkenin adresini işaretçi değişkenimize atayalım:
int x = 5;
int *ptr = &x;
Burada, x
değişkeni bir tam sayı olarak tanımlanmıştır ve değeri 5’dir. İşaretçi değişkeni olan ptr
, x
değişkeninin bellekteki adresini saklar. &
operatörü kullanılarak x
değişkeninin adresi ptr
değişkenine atanmıştır.
Artık, ptr
işaretçisi aracılığıyla x
değişkenine erişebiliriz:
#include <stdio.h>
#include <string.h>
int main()
{
//işaretçi tanımlama
int x = 5;
int *ptr = &x;
//p değişkeninin değerini yazdırma
printf("p değişkeninin değeri: %d", *ptr);
//p değişkeninin adresini yazdırma
printf("p değişkeninin adresi: %p", ptr);
return 0;
}
Pointer tanımlarken *
operatörü kullanılır. Bu operatör, değişkenin bir işaretçi olduğunu belirtir. Örneğin, int *ptr
ifadesi ptr
isimli bir işaretçi tanımlar ve bu işaretçi int
tipinde verileri gösterebilir.
Aynı şekilde, işaretçi değişkeninin işaret ettiği bellek adresindeki değerleri almak için de *
operatörü kullanılır. Bu operatör, işaretçi tarafından gösterilen bellek adresindeki değeri verir. Örneğin *ptr
ifadesi ptr
işaretçisi tarafından gösterilen bellek adresindeki değeri verir. Bu nedenle, pointer tanımlarken ve değerini alırken aynı operatör olan *
kullanılır.
Şimdi de x değişkeninin değerini işaretçi aracılığıyla değiştirelim.
#include <stdio.h>
#include <string.h>
int main()
{
//işaretçi tanımlama
int x = 5;
int *ptr = &x;
*ptr = 10; // x'in değerini 10 yap
printf("x = %d", x); // x = 10
return 0;
}
İşaretçi ptr
değişkeni, x
değişkeninin bellekteki adresini gösterir. *ptr = 10
ifadesi ile ptr
işaretçisi tarafından gösterilen bellek adresindeki değer olan x
değişkeninin değeri 10 olarak değiştirilir. Bu nedenle, sonunda printf("x = %d\n", x);
ifadesi çalıştığında x’in değeri 10 olacaktır.
Peki iki işaretçiyi birbirine eşitlersek ne olur? Şimdi de buna bakalım
int x = 5; // x değişkeni tanımlanıyor ve 5 değeri atanıyor
int y = 10; // y değişkeni tanımlanıyor ve 10 değeri atanıyor
int *ptr1 = &x; // ptr1 işaretçisi x değişkeninin bellek adresini gösterir
int *ptr2 = &y; // ptr2 işaretçisi y değişkeninin bellek adresini gösterir
ptr1 = ptr2; // ptr1 işaretçisi ptr2 işaretçisi tarafından gösterilen bellek adresi olan y değişkeninin adresini gösterir
Burada, ptr1
işaretçisi x
değişkeninin bellekteki adresini, ptr2
işaretçisi ise y
değişkeninin bellekteki adresini gösterir. ptr1 = ptr2
ifadesi ile ptr1
işaretçisi ptr2
işaretçisi tarafından gösterilen bellek adresi olan y
değişkeninin adresini gösterir. Yani ptr1
artık y
değişkeninin adresini gösterir. Bu işlem sonunda, ptr1
ve ptr2
işaretçileri aynı bellek adresini gösterir ve yapılan her işlemde iki işaretçi aynı veriyi etkileyecektir.
#include <stdio.h>
#include <string.h>
int main()
{
int x = 5;
int y = 10;
int *ptr1 = &x; // ptr1'in adresini x'in adresine eşitle
int *ptr2 = &y; // ptr2'nin adresini y'nin adresine eşitle
ptr1 = ptr2; // ptr1'in adresini ptr2'nin adresine eşitle
*ptr1 = 20; // y değişkeninin değerini 20 yap
printf("y = %d\n", y); // y = 20
return 0;
}
İşaretçileri Fonksiyona Değer Olarak Atama
void setValue(int *ptr, int newValue){
*ptr = newValue; // işaretçi üzerinden bellekteki değişkenin değeri değiştiriliyor
}
int main(){
int x = 5;
int *ptr = &x;
setValue(ptr, 10);
printf("x = %d\n", x); // x = 10
return 0;
}
Bu örnekte, setValue
fonksiyonu işaretçi ve yeni bir değer alır ve işaretçi üzerinden bellekteki değişkenin değerini yeni değere eşitler. main
fonksiyonunda, x
değişkeni tanımlanır ve ptr
işaretçisi x
değişkeninin bellek adresini gösterir. Ardından, setValue
fonksiyonu ptr
işaretçisi ve yeni değer olarak 10’u kullanarak çağrılır. Bu çağrı sonucunda, x
değişkeninin değeri 10 olur ve ekrana “x = 10” yazdırılır.
void swapPointers(int *ptr1, int *ptr2){
int temp = *ptr1;
*ptr1 = *ptr2;
*ptr2 = temp;
}
int main(){
int x = 5;
int y = 10;
int *ptr1 = &x;
int *ptr2 = &y;
swapPointers(ptr1, ptr2);
printf("x = %d, y = %d\n", x, y); // x = 10, y = 5
return 0;
}
Bu örnekte, swapPointers
fonksiyonu iki işaretçi alır ve bu işaretçileri kullanarak işaret ettikleri değişkenlerin değerlerini karşılıklı olarak değiştirir. main
fonksiyonunda, x
ve y
değişkenleri tanımlanır ve ptr1
ve ptr2
işaretçileri bu değişkenlerin bellek adreslerini gösterir. Ardından, swapPointers
fonksiyonu ptr1
ve ptr2
işaretçileri kullanarak çağrılır ve işaret ettikleri değişkenlerin değerleri karşılıklı olarak değiştirilir. Bu çağrı sonucunda, x
değişkeni 10 olur ve y
değişkeni 5 olur ve ekrana “x = 10, y = 5” yazdırılır.
Bu fonksiyonu şu şekilde kullanırsak değişkenlerin değeri değişmez. Sadece adresler değişir
void swapPointers(int *ptr1, int *ptr2){
int *temp = ptr1;
ptr1 = ptr2;
ptr2 = temp;
}
int main(){
int x = 5;
int y = 10;
int *ptr1 = &x;
int *ptr2 = &y;
swapPointers(ptr1, ptr2);
printf("x = %d, y = %d\n", x, y); // x = 5, y = 10
return 0;
}
Char Değişkeni ve Dizilerinin Kullanımı
int main(){
char c = 'A';
char *ptr = &c;
*ptr = 'B';
printf("c = %c\n", c); // c = B
return 0;
}
Bu örnekte, c
değişkeni ‘A’ karakteri ile başlatılır ve ptr
işaretçisi c
değişkeninin bellek adresini gösterir. Ardından, ptr
işaretçisi kullanarak c
değişkeninin değeri ‘B’ karakterine değiştirilir. Bu değişiklik sonucunda, c
değişkeni ‘B’ karakteri olur ve ekrana “c = B” yazdırılır.
#include <stdio.h>
int main() {
char str[] = "Hello, world!";
char *ptr = str;
printf("String: %s\n", str);
printf("Pointer: %s\n", ptr);
*ptr = 'J';
printf("String: %s\n", str);
printf("Pointer: %s\n", ptr);
return 0;
}
Bu örnekte, str
dizisi “Hello, world!” ifadesini içerir ve ptr
işaretçisi bu dizinin ilk karakterine işaret eder. Öncelikle, str
ve ptr
işaretçisi tarafından gösterilen dizi ekrana yazdırılır. Daha sonra, ptr
işaretçisi tarafından işaret edilen ilk karakter “J” olarak değiştirilir ve yeniden str
ve ptr
işaretçisi tarafından gösterilen dizi ekrana yazdırılır. Bu örnekte, ptr
işaretçisi tarafından işaret edilen dizi değiştirildiğinde, str
dizisi de aynı şekilde değişir.
Bir başka versiyon
#include <stdio.h>
int main() {
char *str = "Hello, world!";
printf("String: %s\n", str);
//str[0] = 'J'; // this will cause a segmentation fault
printf("String: %s\n", str);
return 0;
}
Bu örnekte, str
işaretçisi “Hello, world!” ifadesinin ilk karakterine işaret eder ve ekrana yazdırılır. Ancak, str[0] = 'J'
satırını yazınca programın hata vermesi ve segmentation fault hatası vermesi gerekir çünkü read-only bellek bölgesinde yer alır ve bu nedenle işaretçi tarafından gösterilen karakter dizisi değiştirilemez.
Başka bir örnek
#include <stdio.h>
int main() {
int array[] = {10, 20, 30, 40, 50}; // int tipinde bir dizi tanımladık
int *ptr = array; // ptr işaretçisi dizinin ilk elemanına işaret ediyor
printf("%d\n", *ptr); // ptr işaretçisi tarafından gösterilen ilk elemanı yazdırıyoruz (10)
ptr++; // ptr işaretçisi bir sonraki elemana işaret ediyor
printf("%d\n", *ptr); // ptr işaretçisi tarafından gösterilen ikinci elemanı yazdırıyoruz (20)
ptr += 2; // ptr işaretçisi 2 eleman ilerliyor ve üçüncü elemana işaret ediyor
printf("%d\n", *ptr); // ptr işaretçisi tarafından gösterilen üçüncü elemanı yazdırıyoruz (30)
return 0;
}
Bu örnekte, ptr++
ve ptr += 2
gibi operatörler ile işaretçi 2 veya daha fazla eleman ilerletilebilir. Bu işlemlerle işaretçi tarafından gösterilen eleman değişebilir ve dizinin diğer elemanlarına erişmek mümkün olabilir.
Şimdi de char dizisi şeklinde kullanımına bakalım
#include <stdio.h>
int main() {
char *ptr = "dijigo"; // ptr işaretçisi "dijigo" ifadesinin ilk karakterine işaret ediyor
printf("%c\n", *ptr); // ptr işaretçisi tarafından gösterilen ilk karakteri yazdırıyoruz (d)
ptr++; // ptr işaretçisi bir sonraki karaktere işaret ediyor
printf("%c\n", *ptr); // ptr işaretçisi tarafından gösterilen ikinci karakteri yazdırıyoruz (i)
ptr += 2; // ptr işaretçisi 2 karakter ilerliyor ve dördüncü karaktere işaret ediyor
printf("%c\n", *ptr); // ptr işaretçisi tarafından gösterilen dördüncü karakteri yazdırıyoruz (j)
return 0;
}
Bu örnekte, ptr++
ve ptr += 2
gibi operatörler ile işaretçi 1 veya daha fazla karakter ilerletilebilir. Bu işlemlerle işaretçi tarafından gösterilen karakter değişebilir ve “dijigo” ifadesinin diğer karakterlerine erişmek mümkün olabilir. Ancak bu gibi işlemleri yaparken dizi içinde sadece okuma işlemi yapabilirsiniz. Diziyi değiştiremezsiniz.
Eğer dizi karakterlerini değiştirmek istiyorsak
#include <stdio.h>
int main() {
char str[] = "dijigo"; // char tipinde bir dizi tanımladık
char *ptr = str; // ptr işaretçisi dizinin ilk karakterine işaret ediyor
printf("%c\n", *ptr); // ptr işaretçisi tarafından gösterilen ilk karakteri yazdırıyoruz (d)
*ptr = 'D'; // ptr işaretçisi tarafından gösterilen ilk karakteri değiştiriyoruz (D)
printf("%c\n", *ptr); // ptr işaretçisi tarafından gösterilen ilk karakteri yazdırıyoruz (D)
ptr++; // ptr işaretçisi bir sonraki karaktere işaret ediyor
printf("%s\n", str); // dizinin tüm elemanlarını yazdırıyoruz (Dijigo)
return 0;
}
while döngüsü ile kullanımı
#include <stdio.h>
int main() {
char str[] = "dijigo"; // char tipinde bir dizi tanımladık
char *ptr = str; // ptr işaretçisi dizinin ilk karakterine işaret ediyor
while(*ptr != '\0'){ // ptr işaretçisi tarafından gösterilen karakter '\0' olana kadar döngü devam eder
printf("%c ", *ptr); // ptr işaretçisi tarafından gösterilen karakteri yazdırıyoruz
ptr++; // ptr işaretçisi bir sonraki karaktere işaret ediyor
}
return 0;
}
Bu örnekte, ptr
işaretçisi ile dizinin her karakterine erişiyoruz ve döngü içinde printf
ile ekrana yazdırıyoruz. Dizinin sonunda '\0'
karakteri vardır ve bu karakter döngünün sonlandırılmasını sağlar. Bu örnekte dizinin içerisinde yer alan her bir karakteri okuyup yazdırıyoruz.
while(*ptr != ‘\0’) ifadesi while(*ptr) ifadesi ile aynı anlamı taşır. ptr
işaretçisi tarafından gösterilen karakter ‘\0’ olana kadar döngü devam eder. ‘\0’ karakteri C dilinde boolean değer olarak false değerini temsil eder. Bu yüzden while(*ptr) ifadesi ile if(*ptr != ‘\0’) ifadesi arasında bir fark yoktur.
while(*ptr) {
printf("%c ", *ptr);
ptr++;
}
Bu şekilde de while döngüsünü yazabilirsiniz.
char str[] = "dijigo";
char *ptr = str;
while(*++ptr) {
printf("%c ", *ptr);
}
while(*++ptr)
ifadesi ile döngü yazarsanız, işaretçi önce bir sonraki karaktere işaret eder ve sonra karakterin değerini kontrol eder. Bu yüzden ilk satırda ptr işaretçisi bir sonraki karaktere işaret eder ve döngü içerisinde ptr işaretçisi tarafından gösterilen karakteri ekrana yazdırır.
char str[] = "dijigo";
char *ptr = str;
while(*++ptr) {
printf("%c ", *ptr);
}
Bu örnekte, döngü ilk başladığında işaretçi str dizisinin ilk karakterinden bir sonraki karaktere işaret eder, döngü içerisinde ptr işaretçisi tarafından gösterilen karakteri ekrana yazdırır. Bu şekilde dizinin ilk karakteri atlanır.
Bu kod ekrana “ijigo” yazar. ptr
işaretçisi başlangıçta “dijigo” dizinin ilk karakterinden bir sonraki karaktere işaret eder. Döngü içerisinde ptr işaretçisi tarafından gösterilen karakteri ekrana yazdırır. Bu nedenle, dizinin ilk karakteri “d” atlanır ve ekrana “ijigo” yazdırılır.
char str[] = "dijigo";
char *ptr = str;
while(*++ptr) ;
printf("%c ", *ptr);
Bu kod, “dijigo” dizisinde işaretçi ptr ile gezinir ve dizinin sonuna kadar döngüyü sürdürür. Ancak döngü sonunda printf("%c ", *ptr);
ile karakteri ekrana yazdırmaz çünkü while döngüsünün sonunda ptr işaretçisi ‘\0’ karakterine işaret ediyor. Bu yüzden ekrana hiçbir şey yazdırılmaz. eğer while döngüsünün sonunda printf(“%c “, *ptr); yazmak istiyorsanız, while döngüsünün sonunda ptr işaretçisini bir geriye gitmemiz gerekir.
char str[] = "dijigo";
char *ptr = str;
while(*++ptr);
printf("%c ", *--ptr);
#include <stdio.h>
int main() {
char *str1 = "dijigo"; // 6 değeri kadar bellekte yer kaplar.
char *str2 = str1; // 6 değeri kadar bellekte yer kaplar.
//str1 1 artır
++str1;
//ekrana str1 yazdır
printf("%s", str1); //ijigo
//str2 yazdır
printf("%s", str2); //dijigo
printf("%ld", str2-str1); // -1 değeri döner. çünkü str1 1 arttırıldı ve str2 ile karşılaştırıldı.
}
#include <stdio.h>
int main() {
char *str1 = "dijigo";
char *str2 = str1;
while(*++str1); // while(*str1++); // while(*str1); str1++; // while(*str1 != '\0') str1++;
printf("%ld\n", str2-str1); // -6 değeri döner.
}
Bu örnekte, str1 işaretçisi “dijigo” dizinin başlangıcını işaret ederken, str2 işaretçisi de str1 işaretçisini kopyaladığı için aynı diziyi işaret etmektedir.
While döngüsünde, str1 işaretçisi dizinin sonunda kalana kadar her bir karakteri gezmektedir. Döngü bittiğinde str1 işaretçisi dizinin sonunda kalır.
printf() fonksiyonunda, str2 işaretçisi dizinin başlangıcını, str1 işaretçisi ise dizinin sonunu işaret etmektedir. Bu nedenle str2-str1 işlemi, dizinin başlangıcı ile sonu arasındaki adres farkını verir ve bu değer negatif olacaktır. Özellikle str1 işaretçisi dizinin sonunda kalıyorsa str2-str1 işlemi -6 döndürür.
#include <stdio.h>
int main() {
char *str1 = "dijigo"; // 6 değeri kadar bellekte yer kaplar.
char *str2 = str1; // 6 değeri kadar bellekte yer kaplar.
//str1 1 artır
//ekrana str1 yazdır
while(*++str1); // while(*str1++); // while(*str1); str1++; // while(*str1 != '\0') str1++;
printf("%s\n", str1); //boşluk
printf("%s\n", str2); //dijigo
printf("%ld\n", str2-str1); // -6 değeri döner.
}