SEN2201 · 14. hafta · Programming in C

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ı.

structmemberdot operator (.)arrow operator (->)typedefarray of structslinked listnodedynamic memory allocationmallocfreeheapprintfscanfformat specifierfield widthprecision

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).x ya da p->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.
  • free edilmeyen 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ölgeNe var?Ömür
Kodprogram komutlarıprogram boyunca
Global veriglobal ve static değişkenlerprogram boyunca
Heapmalloc’la alınanlarfree edilene kadar
Stackyerel 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ı
%dintprintf("%d", -7)-7
%ccharprintf("%c", 65)A
%fdoubleprintf("%f", 1.5)1.500000
%.2fdouble, 2 haneprintf("%.2f", 3.14159)3.14
%sstringprintf("%s", "hi")hi
%x / %Xhexprintf("%x", 255)ff
%osekizlikprintf("%o", 8)10
%5dgenişlik 5, sağaprintf("[%5d]", 42)[ 42]
%-5dsola yaslıprintf("[%-5d]", 42)[42 ]
%05dsıfırla doldurprintf("[%05d]", 42)[00042]
%%yüzde işaretiprintf("%%")%

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 30 yazarsa n = 2.
  • %d boş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üşünceDoğrusu
struct’ı fonksiyona geçirmek aslı değiştirirKopya geçer; pointer geçir
p.x pointer ile de çalışırPointer’da p->x
*p.x = (*p).x*(p.x); parantez gerekir
b = a struct’ta ikisini bağlarBağımsız kopya
malloc bellek otomatik geri verilirfree gerekir
Yerel değişkenin adresini döndürFonksiyon dönünce geçersiz
scanf("%d", x)&x
%5d sola yaslarSağa yaslar; sola için %-5d

Kendini test et

Sınavdaki gibi İngilizce sorular. Önce kâğıt kalemle çöz, sonra cevabı aç.

Soru 1kolay

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
5 4

Başlatıcı listesi üyeleri sırayla doldurur: x = 3, y = 4. p.x += 2 → 5.

Soru 2orta

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
1 11

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.

Soru 3kolay

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
6 0

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.)

Soru 4orta

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
Can 71

En yüksek puan 85 (Can, indeks 1). Toplam 70 + 85 + 60 = 215, 215 / 3 = 71 (tam sayı bölmesi).

Soru 5orta

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
60 30

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.

Soru 6orta

What is printed?

printf("[%5d][%-5d][%05d]\n", 42, 42, 42);
Cevabı göster
[ 42][42 ][00042]

%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.

Soru 7orta

What is printed?

printf("%.2f %8.3f|\n", 3.14159, 3.14159);
Cevabı göster
3.14 3.142|

%.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.

Soru 8kolay

What is printed?

printf("%x %X %o %c\n", 255, 255, 8, 65);
Cevabı göster
ff FF 10 A

%x küçük harfle hex (255 = 0xff), %X büyük harfle, %o sekizlik (8 = 10 sekizlik), %c ASCII karakter (65 = 'A').

Soru 9orta

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
2 12 30

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.

Soru 10orta

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
9 5

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.

Soru 11zor

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
3 5

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.

Soru 12zor

Where in memory do these live: a local variable, a global variable, and a block returned by malloc?

Cevabı göster
Stack, global data area, heap

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).

Deftere not kartı

struct point { int x; int y; }; | struct point p = {3, 4}; | p.x typedef struct {...} Student; -> Student s; struct atama/geçirme = TÜM ÜYELERİ KOPYALA (değerle) | değiştirmek için pointer geçir p->x == (*p).x | . değişkende, -> pointer'da struct dizisi: s[i].score | q++ bir struct ilerler Bağlı liste: struct node { int val; struct node *next; }; for (p = head; p; p = p->next) malloc(n * sizeof(T)) heap'ten yer, adres döndürür | free(p) geri ver Bellek: local -> stack | global/static -> global bölge | malloc -> heap printf: %d %c %f %s %x %X %o | %5d sağa | %-5d sola | %05d sıfır | %.2f | %8.3f scanf("%d", &x) adres ister, okunan öğe sayısını döndürür | %s'de & yok (dizi adı adres) printf yazdığı karakter sayısını döndürür