&ile adres almak ve*ile işaretçinin gösterdiği değeri okuyup değiştirmek- Bir fonksiyona işaretçi aktararak çağıran tarafın değişkenlerini değiştirmek
- İşaretçi aritmetiğini ve
arr[i]ile*(arr + i)ifadelerinin neden aynı olduğunu açıklamak NULLve ilk değeri verilmemiş işaretçilerin tehlikesini tanımak
Bir arkadaşına nerede oturduğunu göstermek için evini taşımazsın; sadece adresi verirsin: “Bakü, Nizami Caddesi, 25”. Adresi bilen biri evi bulabilir, içeri girebilir ve hatta orada bir şeyi değiştirebilir. Bilgisayarın belleği de böyledir: her değişken belirli bir adreste yaşar. Bir adresi saklayan değişkene işaretçi (pointer) denir. İşaretçiler C'nin en güçlü ve en çok korkulan konusudur ama fikri bu adres kadar basittir.
Adresler ve işaretçiler
Değeri başka bir değişkenin bellekteki adresi olan değişken. int *p;, “p bir int değerin adresini saklar” demektir.
| Yazım | Anlamı |
|---|---|
int *p; | bir int'e işaretçi tanımlar |
&x | x değişkeninin adresi |
p = &x; | p artık x'i gösterir |
*p | p'deki adreste bulunan değer (başvuru çözme, dereference) |
NULL | hiçbir şeyi göstermeyen işaretçi |
#include <stdio.h>
int main(void) {
int x = 10;
int *p = &x;
printf("x = %d\n", x);
printf("*p = %d\n", *p);
*p = 25;
printf("x = %d\n", x);
printf("Same address? %d\n", p == &x);
return 0;
}x = 10 *p = 10 x = 25 Same address? 1
*p = 25;, adını hiç anmadan x'i değiştirdi; çünkü p onun adresini tutuyor.Adresin kendisini %p belirteciyle yazdırabilirsin. Genellikle 0x7ffd… gibi on altılık bir sayı olarak görünür ve program her çalıştırıldığında farklı olabilir:
int x = 10;
int *p = &x;
printf("Address of x: %p\n", (void *) &x);
printf("Value of p: %p\n", (void *) p);Fonksiyonlara işaretçi aktarmak: swap
Hatırla: C'de bir fonksiyon argümanlarının kopyasını alır; bu yüzden sıradan parametrelerle iki değişkenin değerini takas edemezsin. Çözüm, fonksiyona değişkenlerin adreslerini vermektir. Fonksiyon adresler aracılığıyla asıl değişkenlere ulaşır. scanf("%d", &age) tam da bu yüzden & ister.
#include <stdio.h>
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main(void) {
int x = 3, y = 8;
printf("Before: x = %d, y = %d\n", x, y);
swap(&x, &y);
printf("After: x = %d, y = %d\n", x, y);
return 0;
}Before: x = 3, y = 8 After: x = 8, y = 3
İşaretçiler bir fonksiyonun birden fazla sonuç “döndürmesini” de sağlar: return yalnızca tek bir değer döndürür ama fonksiyon sonuçlarını kendisine verilen adreslere yazabilir. Aşağıdaki fonksiyon Bakü'de ölçülen birkaç sıcaklığın en düşüğünü ve en yükseğini bulur:
#include <stdio.h>
void min_max(const int *arr, int n, int *min, int *max) {
*min = arr[0];
*max = arr[0];
for (int i = 1; i < n; i++) {
if (arr[i] < *min) *min = arr[i];
if (arr[i] > *max) *max = arr[i];
}
}
int main(void) {
int temps[5] = {18, 25, 21, 30, 16};
int low, high;
min_max(temps, 5, &low, &high);
printf("Min: %d, max: %d\n", low, high);
return 0;
}Min: 16, max: 30
İşaretçi aritmetiği ve diziler
İfadelerde bir dizinin adı ilk elemanının adresine dönüşür; bu yüzden int *p = arr; yazılabilir. Bir işaretçiye 1 eklediğinde 1 bayt değil, bir eleman ilerler: int * için genellikle 4 bayt. Yani *(p + 2) üçüncü elemandır; arr[i] yazımı da *(arr + i) ifadesinin kısaltmasından ibarettir. Bir diziyi fonksiyona aktardığında aslında ilk elemanının işaretçisi aktarılır; fonksiyonun asıl diziyi değiştirebilmesinin ve sizeof hilesinin parametrede çalışmamasının nedeni budur.
#include <stdio.h>
int main(void) {
int arr[4] = {10, 20, 30, 40};
int *p = arr;
printf("%d\n", *p);
printf("%d\n", *(p + 2));
p++;
printf("%d\n", *p);
printf("%d\n", arr[3] == *(arr + 3));
int sum = 0;
for (int *q = arr; q < arr + 4; q++) {
sum += *q;
}
printf("Sum: %d\n", sum);
return 0;
}10 30 20 1 Sum: 100
p++ işaretçiyi bir sonraki elemana taşıdı. Son döngü diziyi indeks yerine bir işaretçiyle dolaşıyor.Tehlikeli işaretçiler
- İlk değeri verilmemiş bir işaretçi rastgele bir adres tutar. Onun üzerinden yazmak başka verileri bozabilir. İşaretçiye her zaman bir değişkenin adresini ya da
NULLver. - **
NULLbir işaretçinin başvurusunu çözmek** (pNULLiken*p) genellikle programı hemen çökertir. Kullanmadan önce denetle:if (p != NULL). - Sarkan işaretçi (dangling pointer) artık var olmayan bir değişkeni gösterir. Örneğin bir fonksiyon kendi yerel değişkeninin adresini döndürürse, fonksiyon bitince o değişken yok olur.
int *make_number(void) {
int local = 5;
return &local;
}local fonksiyon bitince yok olur, döndürülen adres ise “boşluğu” gösterir. Sonuç öngörülemez.Önemli noktalar
- Bir işaretçi adres saklar:
int *p = &x;ilep,x'in adresini tutar. &adresi verir,*o adresteki değeri okur ya da değiştirir.- İşaretçi alan bir fonksiyon çağıran tarafın değişkenlerini değiştirebilir (
swap,scanf). p + 1bir eleman ileri gider;arr[i],*(arr + i)demektir.- İşaretçilere her zaman ilk değer ver,
NULLkontrolü yap ve yerel bir değişkenin adresini asla döndürme.
Kendini test et
10 soru. Her doğru cevap XP kazandırır.
int x = 4; int *p = &x; *p = 9; satırlarından sonra x kaçtır?