C'de struct, dinamik bellek ve giriş/çıkış (printf, scanf)
struct ve typedef, struct'ı değerle ve pointer ile geçirmek, -> operatörü, struct dizileri, bağlı liste, malloc/free ve printf/scanf biçim belirteçleri. Dönem sonu C soruları.
Dönemin son konusu, C’de birden fazla bilgiyi bir arada tutmanın yolu: struct. Bir öğrencinin adı ve notu, bir noktanın x ve y’si, bir listenin düğümü. Bunun yanında programın çalışırken bellek istemesini (malloc) ve kullanıcıyla konuşmasını (printf, scanf) da göreceksin. Soru tipleri değişmiyor: struct ve pointer içeren “What is printed?”, printf biçim soruları, bellek bölgesi soruları.
struct
struct point {
int x;
int y;
};
struct point p;
p.x = 7;
p.y = -1;
struct yeni bir tip tanımlar; içindeki değişkenlere üye (member) denir. Üyelere . ile erişilir. Bellekte üyeler art arda durur: LC-3’te struct point iki kelime kaplar, p.x ilk kelimede, p.y ikincide.
Başlatma: struct point p = {3, 4}; (sırayla x = 3, y = 4).
typedef
Her seferinde struct point yazmamak için takma ad verilir:
typedef struct {
char name[12];
int score;
} Student;
Student s = {"Ada", 70};
struct’lar değerle kopyalanır
b = a; tüm üyeleri kopyalar. Bir fonksiyona struct geçirmek de kopyalamaktır. Fonksiyon kopyayı değiştirir, aslı değişmez. (Dizilerin tam tersi: dizi geçirince adres geçer.)
void moveBad(struct point p) { p.x += 10; } /* kopya */
void moveGood(struct point *p) { p->x += 10; } /* asıl */
-> operatörü
Bir struct pointer’ı üzerinden üyeye erişmek için (*p).x yazılır. Bu çok sık kullanıldığı için kısaltması var: p->x.
struct point p;
p.x = 7;
p.y = -1;
struct point *pp = &p;
pp->y = pp->x * 2;
printf("(%d, %d)\n", p.x, p.y);
(7, 14)
TUZAK ·
*p.x..operatörünün önceliği*’dan yüksektir:*p.x=*(p.x), yani “x üyesini pointer say”. Doğrusu(*p).xya dap->x.
struct dizileri
Student s[3] = {{"Ada", 70}, {"Can", 85}, {"Ece", 60}};
s[1].score /* 85 */
s[2].name[0] /* 'E' */
Struct pointer’ı bir artırınca bir struct ilerler (pointer aritmetiği tipin boyutunu bilir).
Bağlı liste (linked list)
Bir struct, kendi tipinden bir pointer içerebilir. Böylece düğümler birbirine bağlanır:
struct node {
int val;
struct node *next;
};
head ──► [10 | •]──► [20 | •]──► [30 | NULL]
Gezinme kalıbı:
for (p = head; p != NULL; p = p->next)
... p->val ...
Diziden farkı: eleman eklemek için kaydırma gerekmez, sadece pointer’lar değişir. Ama i’inci elemana doğrudan atlanamaz, baştan yürümek gerekir.
Dinamik bellek: malloc ve free
Programın çalışırken ne kadar bellek isteyeceği baştan bilinmiyorsa heap’ten yer istenir.
#include <stdlib.h>
int *v = malloc(4 * sizeof(int)); /* 4 int'lik yer */
v[0] = 5; /* dizi gibi kullan */
free(v); /* işin bitince geri ver */
malloc(n)n byte ayırır, başlangıç adresini döndürür (yer yoksa NULL).sizeof(T)bir tipin kaç byte olduğunu verir; elle sayı yazma.freeedilmeyen bellek, program bitene kadar kaybolur (memory leak).
Liste kurmak:
struct node *head = NULL;
int i;
for (i = 1; i <= 3; i++) {
struct node *n = malloc(sizeof(struct node));
n->val = i * 10;
n->next = head; /* başa ekle */
head = n;
}
Liste: 30 → 20 → 10. Her yeni düğüm başa eklendiği için sıra ters.
Gezinip basınca:
struct node *p;
for (p = head; p != NULL; p = p->next)
printf("%d ", p->val);
30 20 10
Adım adım ne oldu:
i=1: n1 = {10, NULL} head → n1
i=2: n2 = {20, n1} head → n2 → n1
i=3: n3 = {30, n2} head → n3 → n2 → n1
Listenin tamamını silmek için de aynı kalıp: while (head) { t = head; head = head->next; free(t); }. Önce next’i kaydetmeden free(head) yaparsan listenin geri kalanına ulaşamazsın.
struct’ın LC-3 bellek düzeni
LC-3’te int bir kelime olduğu için struct point iki kelimedir. struct point pts[3]; x4000’de başlıyorsa:
x4000 pts[0].x x4001 pts[0].y
x4002 pts[1].x x4003 pts[1].y
x4004 pts[2].x x4005 pts[2].y
Genel formül: &pts[i].y = taban + 2i + 1. Derleyici q->y için q’daki adrese 1 ekleyip LDR yapar: LDR R1, R0, #1. Üyelerin struct başından uzaklığı (offset) sabit olduğu için LDR/STR’nin base+offset kipi tam bu iş için uygundur.
Bellek bölgeleri
| Bölge | Ne var? | Ömür |
|---|---|---|
| Kod | program komutları | program boyunca |
| Global veri | global ve static değişkenler | program boyunca |
| Heap | malloc’la alınanlar | free edilene kadar |
| Stack | yerel değişkenler, parametreler, aktivasyon kayıtları | fonksiyon dönene kadar |
LC-3’te yığın yüksek adreslerden aşağı, heap global bölgenin üstünden yukarı büyür.
TUZAK · yerel değişkenin adresini döndürmek.
int *f(void) { int x = 5; return &x; }hatalıdır: f dönünce x’in kaydı yığından kalkar, pointer artık geçersiz bir yeri gösterir. Fonksiyonun ömrünü aşacak veri için malloc kullan.
printf
int age = 20;
double gpa = 3.456;
char grade = 'B';
printf("Age: %d, GPA: %.2f, Grade: %c\n", age, gpa, grade);
Age: 20, GPA: 3.46, Grade: B
| Belirteç | Tip | Örnek | Çıktı |
|---|---|---|---|
%d | int | printf("%d", -7) | -7 |
%c | char | printf("%c", 65) | A |
%f | double | printf("%f", 1.5) | 1.500000 |
%.2f | double, 2 hane | printf("%.2f", 3.14159) | 3.14 |
%s | string | printf("%s", "hi") | hi |
%x / %X | hex | printf("%x", 255) | ff |
%o | sekizlik | printf("%o", 8) | 10 |
%5d | genişlik 5, sağa | printf("[%5d]", 42) | [ 42] |
%-5d | sola yaslı | printf("[%-5d]", 42) | [42 ] |
%05d | sıfırla doldur | printf("[%05d]", 42) | [00042] |
%% | yüzde işareti | printf("%%") | % |
Kaçış dizileri: \n satır sonu, \t sekme, \\ ters bölü, \" tırnak.
printf, yazdığı karakter sayısını döndürür: printf("abc\n") 4 döndürür.
scanf
int a, b;
int n = scanf("%d %d", &a, &b);
- Değişkenin adresi verilir (
&a). scanf okuduğunu o adrese yazar. - String okurken
&konmaz, çünkü dizi adı zaten adrestir:scanf("%9s", word);. Genişlik (9) taşmayı önler. - Dönüş değeri başarıyla okunan öğe sayısıdır. Kullanıcı
12 30yazarsa n = 2. %dboşlukları ve satır sonlarını atlar.
LC-3 düzeyinde printf ve scanf, sonunda TRAP x21 (OUT) ve TRAP x20 (GETC) gibi işletim sistemi rutinlerini çağırır. Biçimlendirme (sayıyı ASCII rakamlara çevirmek) kütüphanenin işidir; 10. haftada x30 ekleyerek yaptığın şeyin genel hali.
Sık yapılan hatalar
| Yanlış düşünce | Doğrusu |
|---|---|
| struct’ı fonksiyona geçirmek aslı değiştirir | Kopya geçer; pointer geçir |
p.x pointer ile de çalışır | Pointer’da p->x |
*p.x = (*p).x | *(p.x); parantez gerekir |
b = a struct’ta ikisini bağlar | Bağımsız kopya |
| malloc bellek otomatik geri verilir | free gerekir |
| Yerel değişkenin adresini döndür | Fonksiyon dönünce geçersiz |
scanf("%d", x) | &x |
%5d sola yaslar | Sağa yaslar; sola için %-5d |
Kendini test et
Sınavdaki gibi İngilizce sorular. Önce kâğıt kalemle çöz, sonra cevabı aç.
What is printed?
struct point { int x; int y; };
/* in main: */
struct point p = {3, 4};
p.x += 2;
printf("%d %d\n", p.x, p.y);Cevabı göster
Başlatıcı listesi üyeleri sırayla doldurur: x = 3, y = 4. p.x += 2 → 5.
What is printed?
void moveBad(struct point p) { p.x += 10; }
void moveGood(struct point *p) { p->x += 10; }
/* in main: */
struct point p = {1, 2};
moveBad(p); printf("%d ", p.x);
moveGood(&p); printf("%d\n", p.x);Cevabı göster
struct değerle geçince tüm üyeleri kopyalanır; moveBad kopyayı değiştirir, p.x 1 kalır. moveGood adresi alır; p->x, (*p).x demektir ve asıl struct'ı değiştirir: 11.
What is printed?
struct point a = {5, 6};
struct point b = a;
b.y = 0;
printf("%d %d\n", a.y, b.y);Cevabı göster
struct ataması tüm üyeleri kopyalar. b bağımsız bir kopyadır; b.y'yi değiştirmek a'yı etkilemez. (Diziler böyle atanamaz, struct'lar atanabilir.)
What is printed?
typedef struct { char name[12]; int score; } Student;
/* in main: */
Student s[3] = {{"Ada", 70}, {"Can", 85}, {"Ece", 60}};
int i, best = 0, sum = 0;
for (i = 0; i < 3; i++) {
sum += s[i].score;
if (s[i].score > s[best].score) best = i;
}
printf("%s %d\n", s[best].name, sum / 3);Cevabı göster
En yüksek puan 85 (Can, indeks 1). Toplam 70 + 85 + 60 = 215, 215 / 3 = 71 (tam sayı bölmesi).
What is printed?
struct node { int val; struct node *next; };
/* in main: */
struct node c = {30, NULL}, b = {20, &c}, a = {10, &b};
struct node *p;
int s = 0;
for (p = &a; p != NULL; p = p->next)
s += p->val;
printf("%d %d\n", s, a.next->next->val);Cevabı göster
Liste: a → b → c → NULL. Döngü her düğümü ziyaret eder: 10 + 20 + 30 = 60. a.next = &b, b.next = &c, c.val = 30.
What is printed?
printf("[%5d][%-5d][%05d]\n", 42, 42, 42);Cevabı göster
%5d: en az 5 karakter, sağa yaslı (soluna 3 boşluk). %-5d: sola yaslı (sağına boşluk). %05d: boşluk yerine 0 ile doldur.
What is printed?
printf("%.2f %8.3f|\n", 3.14159, 3.14159);Cevabı göster
%.2f: virgülden sonra 2 hane, yuvarlanır: 3.14. %8.3f: 3 ondalık hane (3.142), toplam genişlik 8, sağa yaslı: önüne 3 boşluk.
What is printed?
printf("%x %X %o %c\n", 255, 255, 8, 65);Cevabı göster
%x küçük harfle hex (255 = 0xff), %X büyük harfle, %o sekizlik (8 = 10 sekizlik), %c ASCII karakter (65 = 'A').
The user types 12 30 and presses Enter. What is printed?
int a, b;
int n = scanf("%d %d", &a, &b);
printf("%d %d %d\n", n, a, b);Cevabı göster
scanf, başarıyla okuyup atadığı değer sayısını döndürür: 2. Değerler adreslere (&a, &b) yazılır. & unutulursa scanf rastgele bir adrese yazar.
What is printed?
int *v = malloc(4 * sizeof(int));
int i;
for (i = 0; i < 4; i++) v[i] = i * i;
printf("%d %d\n", v[3], v[1] + v[2]);
free(v);Cevabı göster
malloc, heap'ten 4 int'lik yer ayırır ve başlangıç adresini döndürür. v bir dizi gibi kullanılır: 0, 1, 4, 9. v[3] = 9, v[1] + v[2] = 1 + 4 = 5. free ile yer geri verilir.
What is printed?
struct point pts[2] = {{1, 2}, {3, 4}};
struct point *q = pts;
q++;
printf("%d %d\n", q->x, (*q).y + pts[0].x);Cevabı göster
q++ bir STRUCT ilerler (iki int birden): q = &pts[1]. q->x = 3. (*q).y = 4, + pts[0].x (1) = 5. -> ve (*q). aynı şeydir.
Where in memory do these live: a local variable, a global variable, and a block returned by malloc?
Cevabı göster
Yerel değişkenler fonksiyonun aktivasyon kaydında, yığında. Global ve static değişkenler global veri bölgesinde. malloc ile alınan bloklar heap'te; fonksiyon dönünce kaybolmazlar, free edilene kadar yaşarlar. Yerel bir dizinin adresini döndürmek hatadır, malloc'lu bloğun adresini döndürmek doğrudur.
Doğrulama: Tüm kod parçaları C99 olarak Zig cc (clang) ile derlenip çalıştırıldı; scanf soruları verify/SEN2201/c/w14.in dosyasındaki girdiyle beslendi; çıktılar birebir kopyalandı (verify/SEN2201/c/w14.c ve w14b.c, node verify/SEN2201/c/run.mjs w14).