C Scope ve Lifetime kavramları ne diye düşünüyorsunuz değil mi ? Aslında Bu kavramı şöyle açıklayabiliriz : C yazarken “Az önce vardı, şimdi yok!” dediğiniz değişkenler aslında kaybolmaz; scope (görünürlük alanı) ve lifetime (yaşam süresi) kuralları bittiği için erişilemez hâle gelir. Bu yazıda, C scope ve lifetime ilişkisini netleştirip, en sık görülen hataları (dangling pointer, use-after-free, stack adresini döndürmek vb.) örneklerle göstereceğim.
Sen de şu durumları yaşadın mı?
- Fonksiyon içinde tanımladığın değişken fonksiyondan çıkınca “bozuldu” mu?
- Bir pointer bir süre sonra rastgele değerler mi göstermeye başladı?
- Global değişken her yerden erişiliyor ama “neden bazen tehlikeli?” diye düşündün mü?
Yorumlara kendi örneğini bırak; birlikte teşhis edelim.
1) Scope Nedir? Değişkeni Nereden Görebilirsin?

C Scop ve Lifetime kavramlarını incelemeye başlarken ilk kavramdan yani Scop’dan başlamamız olayın tam mantığını anlamamız için en değerli şeydir.
Scope, bir ismin (değişkenin) kodun hangi bölümünden erişilebilir olduğunu söyler. C’de temel olarak:
- Block scope: Süslü parantez
{ }içi (if/for/while/fonksiyon gövdesi). - Function scope: Özellikle
gotoetiketleri gibi özel durumlar. - File scope: Dosya seviyesinde (global) tanımlananlar.
1.1 Block scope (en yaygını)
Bir blok içinde tanımlanan değişken, blok bittiğinde artık isim olarak görünmez.
// Block scope örneği
#include <stdio.h>
int main(void) {
if (1) {
int x = 42; // x sadece bu blokta görünür
printf("%d\n", x);
}
// printf("%d\n", x); // HATA: x burada scope dışında
return 0;
}
Burada değişken “kaybolmadı”; sadece scope dışında kaldı. Derleyici zaten erişmene izin vermez.
1.2 Shadowing: Aynı isimle değişken “üstünü örter”
Dışarıdaki değişkenin adı, içeride tekrar kullanılırsa içteki değişken dıştakini shadow eder. Bu da “ben bunu değiştirdim sanıyordum” hatalarının kaynağıdır.
// Shadowing örneği
#include <stdio.h>
int main(void) {
int count = 10;
for (int i = 0; i < 1; i++) {
int count = 99; // dıştaki count'u shadow eder
printf("icerde: %d\n", count);
}
printf("disarda: %d\n", count);
return 0;
}
Çıktı: içeride 99, dışarıda 10. Burada lifetime değil, tamamen scope kaynaklı bir sürpriz var.
2) Lifetime Nedir? Değişken Ne Zaman “Yaşar” ve Ne Zaman Ölür?

C Scope ve Lifetime kavramlarının ikincisi olan Lifetime; bir nesnenin bellekte geçerli olduğu zaman aralığıdır. C’de bunu belirleyen şey genelde storage duration (saklama süresi) olur:
- Automatic (stack): Blok boyunca yaşar, blok bitince ölür.
- Static: Program çalıştığı sürece yaşar.
- Dynamic (heap):
mallocile başlar,freeile biter.
2.1 Automatic (stack) lifetime: “Fonksiyondan çıkınca bozuldu”
C’de Fonksiyon içindeki normal yerel değişkenler genelde stack’te tutulur ve blok bitince yaşamları biter.
// Stack değişkeninin lifetime'ı
#include <stdio.h>
void foo(void) {
int a = 5; // automatic storage
printf("foo: %d\n", a);
} // burada a'nin lifetime'ı biter
int main(void) {
foo();
return 0;
}
Bu örnekte sorun yok. Sorun, stack’teki nesnenin adresini dışarı taşıyınca başlar.
2.2 KLASİK HATA: Stack adresini döndürmek (dangling pointer)
Aşağıdaki kod, “değişken kayboldu” hissinin en saf hâlidir. Aslında pointer “var”, ama işaret ettiği nesne öldü.
// YANLIŞ: Yerel değişken adresi döndürmek
#include <stdio.h>
int* bad(void) {
int x = 123; // stack'te
return &x; // x'in lifetime'ı fonksiyon sonunda biter
}
int main(void) {
int *p = bad();
printf("%d\n", *p); // Tanımsız davranış (undefined behavior)
return 0;
}
Bu undefined behavior (tanımsız davranış) üretir: bazen “çalışır gibi” görünür, bazen saçmalar, bazen çöker.
Doğru yaklaşım 1: Heap kullan
// DOĞRU: Heap üstünde değer döndürmek
#include <stdio.h>
#include <stdlib.h>
int* good_heap(void) {
int *p = malloc(sizeof *p);
if (!p) return NULL;
*p = 123;
return p; // lifetime free edilene kadar sürer
}
int main(void) {
int *p = good_heap();
if (p) {
printf("%d\n", *p);
free(p); // lifetime burada biter
}
return 0;
}
Doğru yaklaşım 2: Static değişken kullan
// DOĞRU: static ile program boyunca yaşayan nesne
#include <stdio.h>
int* good_static(void) {
static int x = 123; // static storage: program sonuna kadar yaşar
return &x;
}
int main(void) {
int *p = good_static();
printf("%d\n", *p);
return 0;
}
Not: Static çözüm “tek bir değişken” döndürür; çoklu çağrıda aynı adres kullanılır. Thread-safe olmayabilir; dikkat.
2.3 Dynamic lifetime: use-after-free ve double free
Heap’te nesne yaşar ama sen free edince ölür. Sonra o pointer’ı kullanmak, “değişken bozuldu” gibi görünür ama gerçek sebep bellidir: nesne artık yok.
// use-after-free örneği
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *p = malloc(sizeof *p);
if (!p) return 1;
*p = 77;
free(p); // lifetime bitti
// printf("%d\n", *p); // YANLIŞ: use-after-free (undefined behavior)
p = NULL; // iyi pratik: dangling pointer riskini azaltır
return 0;
}
3) Scope ≠ Lifetime: Biri Görünürlük, Biri Ömür

C Scope ve Lifetime kavramlarını ayırmak bazen kafa karıştırıcı olsada aslında çok kolay ve basit bir şekilde ayrılabiliyor.
En kritik ayrım: Bir değişkenin scope’u bitse bile lifetime’ı devam edebilir (static/global), ya da scope devam ediyor gibi görünse bile lifetime bitmiş olabilir (free edilmiş heap).
3.1 Scope biter, lifetime devam eder: static ve global
// Scope ile lifetime ayrımı (static)
#include <stdio.h>
void counter(void) {
static int c = 0; // yalnızca bu fonksiyon scope'unda görünür
c++; // ama lifetime program boyunca sürer
printf("c=%d\n", c);
}
int main(void) {
counter();
counter();
counter();
return 0;
}
Burada c sadece counter içinde görünür (scope dar), ama her çağrıda kaldığı yerden devam eder (lifetime uzun).
3.2 Scope var, lifetime yok: free sonrası hâlâ pointer elde
Pointer değişkenin scope’u sürüyor olabilir; ama işaret ettiği heap nesnesinin lifetime’ı bitmiş olabilir. Bu ayrımı zihnine oturtursan hataların %70’i çözülür.
4) “Değişken Kayboluyor” Hissinin 6 Yaygın Sebebi

- Dangling pointer: Stack adresini dışarı taşımak veya free sonrası kullanmak.
- Shadowing: İç blokta aynı isimle yeni değişken tanımlamak.
- Uninitialized variable: Değer atanmadan okumak (rastgele gibi görünür).
- Buffer overflow: Dizi taşması komşu değişkenleri bozar.
- Use-after-free / double free: Heap ömrünü yanlış yönetmek.
- Data race: Thread’ler aynı veriye kilitsiz erişince “bazen” bozulur.
4.1 Buffer overflow ile “yanındaki değişken bozuldu” örneği
// DİKKAT: Buffer overflow örneği (tehlikeli kod)
#include <stdio.h>
#include <string.h>
int main(void) {
char name[4];
int pin = 1234;
strcpy(name, "ismet"); // 4 byte'ı aşar, pin'i bozabilir (undefined behavior)
printf("pin=%d\n", pin);
return 0;
}
Bu tür hatalar “değişken kayboldu/bozuldu” diye rapor edilir ama kök sebep bellek taşmasıdır. strncpy, sınır kontrolü ve daha güvenli fonksiyonlar kullan.
5) Debug İpuçları: Scope ve Lifetime Hataları Nasıl Yakalanır?

5.1 Derleyici uyarılarını aç
Eğer C Scope ve Lifetime kavramları ile çalışırken derleyici hatalarını okumak çok kıymetlidir. İşte bunu şöyle yapın;
Günlük C geliştirmede şu bayraklar hayat kurtarır:
gcc -std=c11 -Wall -Wextra -Wshadow -Wconversion -O0 -g main.c -o app-Wshadow: Shadowing yakalar.-g: Debug sembolleri.-O0: Optimizasyonu kapatır; debug daha anlaşılır olur.
5.2 Sanitizer’lar ile undefined behavior avı
gcc -std=c11 -Wall -Wextra -O0 -g -fsanitize=address,undefined main.c -o app
./appAddressSanitizer ve UBSan; use-after-free, stack-use-after-return, buffer overflow gibi “bazen olan” hataları anında raporlar.
5.3 “Benim değişkenim neden değişti?” kontrol listesi
- Bu değişkenin scope’u hangi
{ }içinde? - Bu değişkenin lifetime’ı automatic mi, static mi, heap mi?
- Adresini bir yere kaydettim mi? O adresin işaret ettiği nesne hâlâ yaşıyor mu?
- Yanlışlıkla aynı isimle shadowing yaptım mı?
- Yakında bir dizi/strcpy gibi taşma riski var mı?
Sonuç: C’de “Kaybolan Değişken” Diye Bir Şey Yok, Kural Var
C’de değişkenlerin kaybolması gibi görünen şeyler, çoğunlukla C scope ve lifetime kurallarını ihlal etmekten veya belleği yanlış kullanmaktan doğar. Eğer bir değişkene erişemiyorsan scope bitmiştir; erişebiliyor ama değeri saçmalıyorsa lifetime bitmiş (veya bellek bozulmuş) olabilir.
Sen en çok hangi tür hataya denk geliyorsun: stack adresi döndürmek mi, use-after-free mi, shadowing mi? Yorumlarda kısa bir kod parçası paylaşırsan birlikte analiz edebilirim.
Bu yazıyla birlikte C Scope ve Lifetime kavramlarını oldukça detaylı bir şekilde incelemiş olduk. Burada yaşayabileceğiniz hatalar , kafanızı karıştırabilecek soruları elimden geldikçe detaylandırarak anlatmaya çalıştım. Umarım verimli olmuştur. Bu sayde C Scope ve Lifetime kavramlarınıda bitirmiş olduk .
SSS (FAQ)
1)C Scope ve lifetime aynı şey mi?
Hayır. Scope “nereden erişirim”, lifetime “ne kadar yaşar” sorusunun cevabıdır. Pointer örneklerinde bu fark çok belirgindir.
2) Fonksiyondan yerel değişken adresi döndürmek neden yanlış?
Çünkü yerel değişken genelde stack’te yaşar ve fonksiyon bitince lifetime biter. Pointer kalır ama işaret ettiği nesne ölür (dangling pointer).
3) static değişken ne zaman tercih edilmeli?
Fonksiyon çağrıları arasında durumu korumak istediğinde iş görür. Ama tek bir paylaşılan örnek olduğu için yeniden giriş (reentrancy) ve çoklu thread senaryolarında risklidir.
4) Heap kullanıyorsam sorun bitti mi?
Hayır. Heap’te lifetime’ı sen yönetirsin: malloc ile başlar, free ile biter. Use-after-free ve memory leak en yaygın hatalardır.
5) “Bazen çalışıyor, bazen bozuluyor” hangi sınıfa girer?
Genelde undefined behavior. Stack adresini döndürme, buffer overflow, uninitialized okuma, use-after-free gibi hatalar “bazen” kendini böyle gösterir.
Kaynakça:
- C Standard (ISO/IEC 9899): Storage duration, scope, lifetime tanımları.
- GCC/Clang dokümantasyonu:
-Wall,-Wextra,-Wshadow, sanitizer bayrakları. - AddressSanitizer & UndefinedBehaviorSanitizer: bellek ve UB hata sınıfları.






