> Recursive Functions : Özyineleme (recursion) doğada da karşımıza çıkan bir kavramdır. Bir olgunun kendisine benzer bir olguyu içermesi anlamına gelmektedir. Örneğin tohumdan ağaç olur, ağaç da yeniden tohum vermektedir. Matruşkalar da özyineleme içermektedir. Programlamada özyineleme bazı özel algoritmalarda karşımıza çıkmaktadır. Biz algoritmalar dünyasında bir problemi çözmek için yola çıktığımızda belli bir yol kat ettikten sonra ilk duruma benzer bir durumla karşılaşırsak muhtemelen özyinelemeli bir algoritmik problem söz konusu olmaktadır. Örneğin bir dizin listesini alt dizinlerle dolaşmak isteyelim. Bunun için kök dizindeki dizin girişlerini elde ederiz. Eğer o dizin girişlerinin biri de bir dizine ilişkinse o dizine geçtiğimizde yine ilk işe başladığımız duruma benzer bir durum içerisinde oluruz. O zaman dizin ağacının dolaşılması özyienelemeli bir algoritma içermektedir. Bir algoritmik problemin özyineleme olmadan çözümüne "iteratif çözüm" de denilmektedir. O halde algoritmalar bu bakımdan üçe ayrılabilir: -> Yalnızca iteratif olarak çözülen algoritmalar -> Hem iteratif hem de özyinemelei biçimde çözülebilen algoritmalar -> Yalnızca özyinelemeli olarak çözülebilen algoritmalar Bazı algoritmaların özyinelemeyle bir bağı yoktur. Bunlar döngüler yoluyla klasik yöntemlerle çzöülmektedir. Ancak bazı algoritmalar hem özyineleme ile hem de klasik iteratif yöntemlerle çözülebilmektedir. Ancak bazı algoritmaların iteratif çözümü ya yoktur ya da makul değildir. Özyinelemeli karaktere sahip olan algoritmalar tipik olarak kendi kendini çağıran fonksiyonlar yoluyla gerçekleştirilmektedir. Aslında özyinelemeli algoritmalar yapay stack kullanılarak sanki iteratifmiş gibi de çözülebilmektedir. Ancak bunların en etkin çözümü kendi kendini çağıran fonksiyonlarla yapılmaktadır. Kendi kendini çağıran fonksiyonlara "özyinelemeli fonksiyonlar (recursive functions)" da denilmektedir. Bir algoritma hem iteratif yöntemle hem de özyinelemeli yöntemle çözülebiliyorsa genellikle (ancak her zaman değil) iteratif yöntem daha etkin bir gerçekleştirim sunmaktadır. Yani bu tür durumlarda genellikle iteratif yöntem tercih edilmelidir. Ancak yukarıda da belirtitğimiz gibi bazı algoritmaların iteratif çözümleri ya yaoktur ya da makul değildir. Bu durumda biz mecburen özyinelemeli çözümü uygularız. Genellikle bir fonksiyonun kendini çağırması insanlar tarafından tuhaf ve karmaşık olarak algılanmaktadır. Aslında bir fonksiyonun kendisini çağırması ile başka bir fonksiyonu çağırması arasında hiçbir farklılık yoktur. Nasıl bir fonksiyon başka bir fonksiyonu çağırdığında çağrılan fonksiyon bittiğinde akış çağırma noktasından sonraki deyimle devam ediyorsa benzer biçimde bir fonksiyon kendisini çağırdığında o çağrı bitince yine akış çağrılan noktadan sonraki deyimle devam edecektir. Ancak özyinelemede önemli bir problem "sonsuz döngü" oluşabilmesidir. Yani bir fonksiyon kendisini kontrolsüz bir biçimde çağırırsa sonsuz döngü oluşur. Örneğin: void foo(void) { printf("foo\n"); foo(); } Burada foo fonksiyonu çağrıldığında fonksiyon hep kendisini yeniden çağıracağı için sonsuz döngü oluşacaktır. Tabii aslında fonksiyonun hiç yerel değişkeni olmasa bile CALL işlemi sonucunda geri dönüş adresi stack'te kaydedildiği için bir stack taşması olaşacak ve Windows, Linux, macOS sistemlerinde program çökecektir. Özyinelemeli fonksiyonlarda anahtar noktalardan biri fonksiyon her kendini çağırdığında fonksiyonun yerel değişkenlerinin yeniden yeni kopyasının yaratılmasıdır. Örneğin: void foo(void) { int a, b; a = 10; b = 20; foo(); ... } Burada foo kendisini çağırdığında stack'te yeni bir a ve b nesneleri yaratılacaktır. Her çağrının a ve b nesneleri farklı olacaktır. Fonksiyonun parametre değişkenleri de fonksiyonunun her kendini çağırmasında yeniden yaratılmaktadır. Bir fonksiyon kendini kontrolsüz bir biçimde çağırmamalıdır. Bir noktaya kadar kendi kendini çağırmalı sonra çıkış sürecine girmelidir. Örneğin: void foo(int n) { if (n == 0) return; printf("n before call: %d\n", n); foo(n - 1); printf("n after call: %d\n", n); } ... foo(3); Burada her foo çağrısında n isimli o çağrıya özgü yeni bir parametre değişkeni yaratılmaktadır. Bu örnekte fonksiyon üç kere kendisini çağırmış sonra çıkış sürecine girmiştir. n == 0 durumunda fonksiyon return ile sonlandırılınca bir önceki çağırdan çalışma devam edeceğine dikkat ediniz. Aşağıda bu kullanıma ilişkin bir örnek verilmiştir: * Örnek 1, #include void foo(int n) { if (n == 0) return; printf("n before call: %d\n", n); foo(n - 1); printf("n after call: %d\n", n); } int main(void) { foo(3); return 0; } Şimdi de bir takım örneklerle konuyu pekiştirmeye çalışalım: * Örnek 1.0, Aşağıda faktöriyel hesabı yapan iteratif bir fonksiyon örneği veilmiştir. #include unsigned long long factorial(int n) { unsigned long long total = 1; for (int i = 2; i <= n; ++i) total *= i; return total; } int main(void) { unsigned long long result; result = factorial(10); printf("%llu\n", result); return 0; } * Örnek 1.1, Şimdi faktörüyel hesabını aşağıdaki gibi özyineleme ile yapalım: unsigned long long factorial(int n) { unsigned long long temp; if (n == 0) return 1; temp = n * factorial(n - 1); return temp; } Aslında burada temp ara değişkeninin kullanılmasına hiç gerek yoktur. Ancak özyinelemenin daha iyi anlaşılması için böyle bir ara değişken kullandık. Şimdi fonksiyonun aşağıdaki gibi çağrıldığını düşünelim: factorial(4); Burada özyinemeli çağırmalarda şöyle bir durum oluşacaktır: factorial(4) ************ temp = 4 * factorial(3); factorial(3) ************ temp = 3 * factorial(2); factorial(2) ************ temp = 2 * factorial(1); factorial(1) ************ temp = 1 * factorial(0); factorial(0) ************ return 1; En sonunda factorial(0) çağrısı 1 ile geri dçnünce çıkış sürecine girilmektedir. Tabii aslında buradaki temp değişkeninin kullanılmasına gerek yoktur: unsigned long long factorial(int n) { if (n == 0) return 1; return n * factorial(n - 1);; } Aşağıda bu konuya ilişkin örnek verilmiştir: #include unsigned long long factorial(int n) { if (n == 0) return 1; return n * factorial(n - 1);; } int main(void) { unsigned long long result; result = factorial(4); printf("%llu\n", result); return 0; } * Örnek 2.0.0, Bir yazıyı tersten yazdıran bir fonksiyon iteratif biçimde aşağıdaki şöyle yazılabilir. #include void putsrev(const char *str) { size_t i; for (i = 0; str[i] != '\0'; ++i) ; while (i-- > 0) putchar(str[i]); putchar('\n'); } int main(void) { char s[] = "ankara"; putsrev(s); return 0; } * Örnek 2.0.1, Şimdi de bu hesabı özyienelemeli olarak yapalım: Genel olarak düzden yapılan bir işlemi tersten yapmak için özyineleme kullanılabilir. Fonksiyon kendini çağırarak ilerler. Sona gelindiğinde çıkış sürecinde işlemler yaptırılır. Şöyleki; void putsrev(const char *str) { if (*str == '\0') return; putsrev(str + 1); putchar(*str); } Burada biz putsrev fonksiyonunu putsrev("ali") biçiminde çağırmış olalım. İlk çağrıda str göstericisi "ali" yazısını gösteriyor olur. İkinci çağrıda "li" yazısını, üçüncü çağrıda "i" yazısını ve son çağrıda null karakteri gösteriyor olur. Bir sonraki çağırmadan çıkıldığında bir önceki çağırmanın str göstericisini kullanıyor olmaktayız. Burada aslında bir str değişkeninin olmadığına her özyinelemede yeni bir str değişkeninin yaratıldığına dikkat ediniz. Tabii biz burada yalnızca özyineleme çalışması yapıyoruz. Yoksa bu problemin iteratif çözümü çok daha etkindir. Aşağıda bu konuya ilişkin örnek verilmiştir: #include void putsrev(const char *str) { if (*str == '\0') return; putsrev(str + 1); putchar(*str); } int main(void) { putsrev("ankara"); putchar('\n'); return 0; } * Örnek 2.1.0, Bir yazıyı iteratif yolla in-place biçimde ters çevirmek isteyelim. Klasik yöntem yazının sonuna kadar gidip baştan ve sondan karşılıklı elemanları yazının uzunluğunun yarısı kadar yer değiştirmektir. #include void revstr(char *str) { size_t n; char temp; for (n = 0; str[n] != '\0'; ++n) ; for (size_t k = 0; k < n / 2; ++k) { temp = str[k]; str[k] = str[n - k - 1]; str[n - k - 1] = temp; } } int main(void) { char s[] = "ankara"; revstr(s); puts(s); return 0; } * Örnek 2.1.1, Yukarıdaki fonksiyonun iteratif olarak başka bir yazım biçimi de aşağıdaki gibi olabilir. Burada fonksiyon ilk ve son karakterlerin indeks numaralarıyla çağrılmaktadır. #include #include void revstr(char *str, size_t left, size_t right) { char temp; while (left < right) { temp = str[left]; str[left] = str[right]; str[right] = temp; ++left, --right; } } int main(void) { char s[] = "ankara"; revstr(s, 0, strlen(s) - 1); puts(s); return 0; } * Örnek 2.2, Şimdi yukarıdaki fonksiyonu özyinelemeli hale dönüştürelim: void revstr(char *str, size_t left, size_t right) { char temp; if (right <= left) return; temp = str[left]; str[left] = str[right]; str[right] = temp; revstr(str, left + 1, right - 1); } Burada hger defasında left bir artırılarak, right ise bir eksiltilerek özyineleme yapılmıştır. Her özyinelemde yazının bir karakteri yer değiştirilmektedir. Tabii biz burada yalnızca özyineleme çalışması yapıyoruz. Yoksa bu problemin iteratif çözümü çok daha etkindir. Aşağıda bu konuya ilişkin örnek verilmiştir. #include #include void revstr(char *str, size_t left, size_t right) { char temp; if (right <= left) return; temp = str[left]; str[left] = str[right]; str[right] = temp; revstr(str, left + 1, right - 1); } int main(void) { char s[] = "ankara"; revstr(s, 0, strlen(s) - 1); puts(s); return 0; } * Örnek 3, Bazen özyinelemeli fonksiyonun parametrik yapısı kolay kullanıma izin vermeyebilir. Bu durumda programcı bir sarma fonksiyon (wrapper function) yazarak özyinelemeli fonksiyonu çağırır. #include #include void revstr_recur(char *str, size_t left, size_t right) { char temp; if (right <= left) return; temp = str[left]; str[left] = str[right]; str[right] = temp; revstr_recur(str, left + 1, right - 1); } void revstr(char *s) { size_t n; for (n = 0; s[n] != '\0'; ++n) ; revstr_recur(s, 0, n ? n - 1 : 0); } int main(void) { char s[] = "ankara"; revstr(s); puts(s); return 0; } * Örnek 4.0, Bir int sayıyı binary olarak iteratif biçimde ekrana yazdıran bir fonksiyon şöyle yazılabilir: void binprint(unsigned val) { for (int i = sizeof(val) * 8 - 1; i >= 0; --i) putchar((val >> i & 1) + '0'); putchar('\n'); } Burada sayının başındaki 0'lar da yazdırılmaktadır. Eğer sayının başındaki 0'ları yazdırmak istemeseydik kodu şöyle yazabilirdik: void binprint(unsigned val) { int i; for (i = sizeof(val) * 8 - 1; val >> i == 0 && i >= 0; --i) ; for (; i >= 0; --i) putchar((val >> i & 1) + '0'); putchar('\n'); } Aşağıda bu konuya ilişkin örnek verilmiştir: #include void binprint(unsigned val) { int i; for (i = sizeof(val) * 8 - 1; val >> i == 0 && i >= 0; --i) ; for (; i >= 0; --i) putchar((val >> i & 1) + '0'); putchar('\n'); } int main(void) { binprint(255); return 0; } * Örnek 4.1, Şimdi sayıyı ikilik sistemde bastıran fonksiyonu özyinelemeli olarak yazmak isteyelim. Burada biz sağdan sola öteleme yaparak fonksiyonu özyinelemeli biçimde çağırırız. Sonra çıkış sürecinde sayının en düşün anlamlı bitini yazdırırız. Yani aslında burada özyineleme ile düzden yapılan işlem tersine çevrilmektedir: void binprint(unsigned val) { if (val == 0) return; binprint(val >> 1); putchar((val & 1) + '0'); } Aşağıda bu konuya ilişkin örnek verilmiştir: #include void binprint(unsigned val) { if (val == 0) return; binprint(val >> 1); putchar((val & 1) + '0'); } int main(void) { binprint(255); return 0; } * Örnek 5.0, Bir bilgisayar sisteminde bir ekran varsa genel olarak o ekrana text modda yalnızca karakterler bastırılabilmektedir. Sayılar aslında karakterle dönüştürülüp yazıdırılırlar. Örneğin printf fonksiyonu "%d" format karakteri ile aslında int değeri karakterlere dönüştürür bu karakterleri ekrana basar. Başka bir deyişle aslında temel bir bilgisayar sisteminde programcının elinde yalnızca putchar benxeri bir fonksiyon vardır. Pekiyi biz int bir değeri yalnızca putchar kullnarak nasıl yazdırabiliriz? İlk akla gelen yöntem sayıyı basamaklarına ayrıştırıp onları putchar kullanarak yazdırmaktır. Ancak sürekli 10'a bölme yöntemi ile basamak ayrıştırılması yapıldığında basamaklar ters sırada elde edilmektedir. Örneğin: while (val) { digit = val % 10; val /= 10; } O halde bizim basamakları ters sırada elde edip bir diziye yerleştirmemiz sonra da o diziyi ters sırada yazdırmamız gerekir. Sayı negatifse onun negatif olduğu bilgisini saklayıp sayıyı pozitif hale dönüştürmek uygun olur. Aşağıda muhtemel bir iteratif çözüm örneği verilmiştir. Aşağıda bu konuya ilişkin örnek verilmiştir: #include void printd(int val) { int digit; char buf[64]; int nflag; int i; nflag = 0; i = 0; if (val < 0) { nflag = 1; val = -val; } for (i = 0; val != 0; ++i) { digit = val % 10; buf[i] = '0' + digit; val /= 10; } if (nflag) buf[i++] = '-'; for (--i; i >= 0; --i) putchar(buf[i]); putchar('\n'); } int main(void) { printd(-123456); return 0; } * Örnek 5.1, Aslında bir sayıyı 10'luk sistemde yalnızca putchar kullanarak yazdırma işlemi özyinelemeli biçimde çok daha kolay yapılabilmektedir. Yani bu problemin özyinelemeli çözümü iteratif çözümünden daha etkindir. Özyinelemeli çözüm şöyle olabilir: void printd(int val) { if (val < 0) { putchar('-'); val = -val; } if (val / 10) printd(val / 10); putchar(val % 10 + '0'); } Burada fonksiyonu -12345 değeri ile çağırmış olalım. Fonksiyon ilk çağrıda bir kez '-' karakterini ekrana basacak sonra bir daha bu ekrana basmayacaktır.Sonra özyinelemenin aşağıdaki gibi yapıldığına dikkat ediniz: if (val / 10) printd(val / 10); Burada 12345 değerinin 10'a bölümü 0 olmadığı için fonksiyon kendini 1234 değeri ile çağıracaktır. Benzer durumlar aşağıdaki gibi tekrarlanacaktır: val = 12345 val = 1234 val = 123 val = 12 val = 1 Burada artık fonksiyon sürecine girecektir. Çıkarken de en düşük basamağı yazdıracaktır. Aşağıda bu konuya ilişkin örnek verilmiştir: #include void printd(int val) { if (val < 0) { putchar('-'); val = -val; } if (val / 10) printd(val / 10); putchar(val % 10 + '0'); } int main(void) { printd(-123456); return 0; } * Örnek 5.2, Aslında yukarıdaki algoritma diğer tabanlar için de benzer biçimde çalışabilmektedir. Fakat burada dikkat edilmesi gereken nokta 10'luk sistemden büyük sistemlerde (örneğin 16'lık sistemde) basamakların artık A, B, C, ... biçiminde isimlendirilmesidir. ASCII tablosunda (ve diğer tablolarda da böyle) 0-9 arasındaki sayılar peşi sıra gitmektedir. Ancak '9' karakterinden sonra 'A' gelmemektedir. Arada 8 tane farklı karakter vardır. O halde eğer taban 10'dan büyükse bu durumu dikkate almak gerekir. Örneğin: putchar(n % base > 9 ? n % base - 10 + 'A' : n % base + '0'); Buraada taban 10'dan büyükse düzeltme yapılmıştır. Aşağıda bu konuya ilişkin örnek verilmiştir: #include void printd(int val, int base) { if (val < 0) { putchar('-'); val = -val; } if (val / base) printd(val / base, base); putchar(val % base > 9 ? val % base - 10 + 'A' : val % base + '0'); } int main(void) { printd(255, 16); return 0; } * Örnek 6, Kapalı bir şeklin içinin boyanması için birkaç algoritma kullanılmaktadır. Bunlardan birine "floodfill" algoritması denilmektedir. Bu algoritmada iç bir noktadan başlanır. Fonksiyon dört yönde kendini çağırır. Böylece bir su baskını gibi şekle nüfuz eder. Tabii sınıf noktası gelindiğinde ya da da önce boyabnış olan bir noktaya gelindiğinde fonksiyon hemen return etmelidir. Aşağıdaki örnekte floodfill algoritması 10x20'lik karakter tabanlı bir bitmap resim üzerine uygulanmıştır. Resim aşağıdaki olabilir: ####### ##### # ####### ###### # # # # # ###### #### # ##### # ## # ### Biz örneğimizde önce resmi bir text dosyaya kaydedip bu text dosyayı char türden bir matrise okumaktayız. Aşağıda bu konuya ilişkin örnek verilmiştir: #include #include #include #define ROWSIZE 10 #define COLSIZE 20 #define FILLCHAR '.' char g_bitmap[ROWSIZE][COLSIZE]; void read_bitmap(void) { FILE *f; char buf[1024]; if ((f = fopen("bitmap.txt", "r")) == NULL) { fprintf(stderr, "cannot open file!..\n"); exit(EXIT_FAILURE); } for (int i = 0; i < ROWSIZE; ++i) { fgets(buf, 1024, f); memcpy(g_bitmap[i], buf, COLSIZE); } fclose(f); } void disp_bitmap(void) { for (int i = 0; i < ROWSIZE; ++i) { for (int k = 0; k < COLSIZE; ++k) putchar(g_bitmap[i][k]); putchar('\n'); } } void floodfill(int row, int col) { if (g_bitmap[row][col] == '#' || g_bitmap[row][col] == FILLCHAR) return; g_bitmap[row][col] = FILLCHAR; floodfill(row + 1, col); floodfill(row, col - 1); floodfill(row - 1, col); floodfill(row, col + 1); } int main(void) { read_bitmap(); disp_bitmap(); floodfill(6, 10); printf("\n\n"); disp_bitmap(); return 0; } int main(void) { if (!read_bitmap("bitmap.txt")) { fprintf(stderr, "cannot read bitmap!..\n"); exit(EXIT_FAILURE); } floodfill(5, 5, '.'); disp_bitmap(); return 0; } /* ####### ##### # ####### ###### # # # # # ###### #### # ##### # ## # ### */ * Örnek 7.0, Bilindiği gibi "seçerek sıralama (selection sort)" algoritması bir dizinin en küçük elemanın bulunup dizinin ilk elemanı ile yer değiştirilmesi temeline daynmaktadır. Bu biçimde dizi her defasında bir eleman daraltılarak aynı işlemler yapılmaktadır. Selection sort algortmasının iteratif çözümü aşağıda verilmiştir. #include void ssort(int *pi, size_t size) { size_t i, k; size_t min_index; int min; for (i = 0; i < size - 1; ++i) { min_index = i; min = pi[i]; for (k = i + 1; k < size; ++k) if (pi[k] < min) { min = pi[k]; min_index = k; } pi[min_index] = pi[i]; pi[i] = min; } } void disp(const int *pi, size_t size) { size_t i; for (i = 0; i < size; ++i) printf("%d ", pi[i]); printf("\n"); } #define SIZE 10 int main(void) { int a[SIZE] = { 34, 12, 7, 84, 72, 39, 75, 45, 59, 21 }; ssort(a, SIZE); disp(a, SIZE); return 0; } * Örnek 7.1, Seçerek sıralama yöntemini özyinelemeli bir biçimde de uygulayabiliriz. Tabii bu yöntemin özyinelemeli uygulaması makul değildir. Ancak biz burada özyineleme çalışması yapmak istiyoruz. Algoritmanın özyinelemeli versiyonunda dizinin en büyük elemanı bulunur. Bu eleman son elemanla yer değiştirilir. Sonra fonksiyon bir eksik uzunlukla kendini çağırır. Tabii uzunluk 1'e geldiğinde algoritma artık çıkış sürecine girmelidir. #include void selection_sort(int *pi, size_t size) { int max; size_t max_index; if (size <= 1) return; max = pi[0]; max_index = 0; for (size_t i = 1; i < size; ++i) if (pi[i] > max) { max = pi[i]; max_index = i; } pi[max_index] = pi[size - 1]; pi[size - 1] = max; selection_sort(pi, size - 1); } int main(void) { int a[10] = {41, 23, 12, 7, 37, 98, 29, 16, 82, 66}; selection_sort(a, 10); for (int i = 0; i < 10; ++i) printf("%d ", a[i]); printf("\n"); return 0; } * Örnek 8, 8 vezir problemi bir satranç tahtasına birbirini yemeyen 8 vezirin yerleştirilmesi problemidir. Problem özyinelemeli bir karakterdedir. Tipik bir çözümde satranç tahtası global bir matris ile tamsil edilir. Vezirlerin yemediği yerler ve vezirlerin yediği yerler matriste işaretlenir. Özyinelemeli fonksiyon global matrise bakarak o anda vezirlerin yemediği ilk kareye verizi yerleştirir. Global matirisi günceller ve kendisini çağırır. Eğer tahtada vezirlerin yemediği hiç kare kalmazsa fonksiyon kendisini sonlandırır. Eğer 8 vezir yerleştirilebilmişse tahta print edilir. Bir çağrı sonlandığında global dizinin eski haline getirilmesi gerekmektedir. Aşağıda 8 vezir probleminin çözümüne ilişkin bir örnek verilmiştir. #include #include #define SIZE 8 int g_qcount; int g_count; char g_board[SIZE][SIZE]; void init_board(void) { int r, c; for (r = 0; r < SIZE; ++r) for (c = 0; c < SIZE; ++c) g_board[r][c] = '.'; } void print_board(void) { int r, c; printf("%d\n", g_count); for (r = 0; r < SIZE; ++r) { for (c = 0; c < SIZE; ++c) printf("%c", g_board[r][c]); printf("\n"); } printf("\n"); } void locate_queen(int row, int col) { int r, c; g_board[row][col] = 'V'; r = row; for (c = col + 1; c < SIZE; ++c) g_board[r][c] = 'o'; for (c = col - 1; c >= 0; --c) g_board[r][c] = 'o'; c = col; for (r = row - 1; r >= 0; --r) g_board[r][c] = 'o'; for (r = row + 1; r < SIZE; ++r) g_board[r][c] = 'o'; for (r = row - 1, c = col - 1; r >= 0 && c >= 0; --r, --c) g_board[r][c] = 'o'; for (r = row - 1, c = col + 1; r >= 0 && c < SIZE; --r, ++c) g_board[r][c] = 'o'; for (r = row + 1, c = col - 1; r < SIZE && c >= 0; ++r, --c) g_board[r][c] = 'o'; for (r = row + 1, c = col + 1; r < SIZE && c < SIZE; ++r, ++c) g_board[r][c] = 'o'; } void queen8(int row, int col) { char board[SIZE][SIZE]; for (; row < SIZE; ++row) { for (; col < SIZE; ++col) { if (g_board[row][col] == '.') { memcpy(board, g_board, sizeof(board)); ++g_qcount; locate_queen(row, col); if (g_qcount == SIZE) { ++g_count; print_board(); } queen8(row, col); --g_qcount; memcpy(g_board, board, sizeof(board)); } } col = 0; } } int main(void) { init_board(); queen8(0, 0); return 0; } * Örnek 9.0, Dizin ağacının dolaşılması özyineleme gerektiren tipik algoritmalardandır. Dolaşım bir fonksiyon ile şöyle yapılmaktadır: Önce prosesin çalışma dizini fonksiyonun başında dolaşılacak dizin olarak set edilir. Sonra o dizin içerisindeki bütün dizin girişleri bulunur. Bir dizin girişi eğer bir dizin belirtiyorsa fonksiyon o dizin ile kendini çağırır. Ancak bir dizinde dizin listesinin sonuna gelindiğinde yine prosesin çalışma dizini o dizinin üst dizini olarak set edilir. Ancak gerek Windows sistemlerinde gerekse UNIX/Linux sistemlerinde bir dizinin içeriği erişim hakkı nedeniyle okunamayabilir. Bu durumda tamamen özyineleme durdurulabilir ya da bu hatalar görmezden gelinebilir. Windows sistemlerinde bir dizin var olduğu halde SetCurrentDirectory fonksiyonu ile erişim hakları yüzünden o dizin prosesin çalışma dizini yapılamayabilmektedir. Halbuki UNIX/Linux sistemlerinde chdir fonksiyonu bir dizine erişim hakkı olmasa bile başarılı olmaktadır. Aşağıda Windows sistemlerinde dizin ağacının dolaşılmasına bir örnek verilmiştir. Bu örnekte SetCurrentDirectory fonksiyonu başarısız olursa prosesin çalışma dizini değiştirilemediği için üst dizine geri dönüş yapılmamamktadır. #include #include #include void PutSysErr(LPCSTR lpszMsg); void WalkDir(LPCSTR lpszDirPath) { HANDLE hFF; WIN32_FIND_DATA fd; if (!SetCurrentDirectory(lpszDirPath)) { PutSysErr("SetCurrentDirectory"); return; } if ((hFF = FindFirstFile("*.*", &fd)) == INVALID_HANDLE_VALUE) { PutSysErr("FindFirstFile"); goto EXIT; } do { if (strcmp(fd.cFileName, ".") == 0 || strcmp(fd.cFileName, "..") == 0) continue; /* printf("%s\n", fd.cFileName); */ if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { printf("%s\n", fd.cFileName); WalkDir(fd.cFileName); } } while (FindNextFile(hFF, &fd)); if (GetLastError() != ERROR_NO_MORE_FILES) PutSysErr("FindNextFile"); FindClose(hFF); EXIT: if (!SetCurrentDirectory("..")) PutSysErr("SetCurrentDirectory"); } int main(void) { WalkDir("C:\\windows"); return 0; } void PutSysErr(LPCSTR lpszMsg) { DWORD dwLastErr = GetLastError(); LPTSTR lpszErr; if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastErr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpszErr, 0, NULL)) { fprintf(stderr, "%s: %s", lpszMsg, lpszErr); LocalFree(lpszErr); } } * Örnek 9.1.0, Dizin ağacını dolaşırken onları kademeli bir biçimde yazdırdabiliriz. Bunun için WalkDir fonksiyonuna kademeyi belirten bir level parametresi eklenebilir. Sonra her özyinelemede bu level parametresi bir artırılabilir. Kademeli yazdırmak için printf fonksiyonunda "%*s" format karakterini kullanabiliriz. Örneğin: printf("%*s%s\n", level * TABSIZE, "", fd.cFileName); Burada "%*s" format karakterinde "*" format karakterine level * TABSIZE argümanı, s format karakterine ise "" argümanı karşılık gelmektedir. Dolayısıyla bu çağrıda soldan level * TABSIZE kadar boşluk bırakılmış olmaktadır. Aşağıdaki örnekte kademeli yazım uygulanmıştır. Ancak bir dizine geçme ya da dizin listesini almada bir problem ortaya çıkarsa ve stderr dosyası yönlendirilmemişse kademede bozukluk gözükebilecektir. #include #include #include #define TABSIZE 4 void PutSysErr(LPCSTR lpszMsg); void WalkDir(LPCSTR lpszDirPath, int level) { HANDLE hFF; WIN32_FIND_DATA fd; if (!SetCurrentDirectory(lpszDirPath)) { PutSysErr("SetCurrentDirectory"); return; } if ((hFF = FindFirstFile("*.*", &fd)) == INVALID_HANDLE_VALUE) { PutSysErr("FindFirstFile"); goto EXIT; } do { if (strcmp(fd.cFileName, ".") == 0 || strcmp(fd.cFileName, "..") == 0) continue; /* printf("%s\n", fd.cFileName); */ if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { printf("%*s%s\n", level * TABSIZE, "", fd.cFileName); WalkDir(fd.cFileName, level + 1); } } while (FindNextFile(hFF, &fd)); if (GetLastError() != ERROR_NO_MORE_FILES) PutSysErr("FindNextFile"); FindClose(hFF); EXIT: if (!SetCurrentDirectory("..")) PutSysErr(lpszDirPath); } int main(void) { WalkDir("C:\Dropbox\Shared\Kurslar\SysProg-1"", 0); return 0; } void PutSysErr(LPCSTR lpszMsg) { DWORD dwLastErr = GetLastError(); LPTSTR lpszErr; if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastErr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpszErr, 0, NULL)) { fprintf(stderr, "%s: %s", lpszMsg, lpszErr); LocalFree(lpszErr); } } * Örnek 9.1.1, Yukarıdaki WalkDir fonksiyonlarında önemli bir kusur fonksiyonun prosesin çalışma dizinini değiştirerek işini sonlandırmasıdır. Fonksiyonun çağrılmadan önceki çalışma dizinini yeniden set etmesi için bir sarma fonksiyon kullanılabilir. (access POSIX fonksiyonu Windows sistemlerinde _access ismiyle de bulunmaktadır.) Bu sayede level parametresi de sarma fonksiyon tarafından asıl özyinelemeli fonksiyon çağrılırken kullanılacaktır. Dolayısıyla asıl fonksiyonun parametrik yapısı daha sade olacaktır. #include #include #include #define TABSIZE 4 void PutSysErr(LPCSTR lpszMsg); void WalkDirRecur(LPCSTR lpszDirPath, int level) { HANDLE hFF; WIN32_FIND_DATA fd; if (!SetCurrentDirectory(lpszDirPath)) { PutSysErr("SetCurrentDirectory"); return; } if ((hFF = FindFirstFile("*.*", &fd)) == INVALID_HANDLE_VALUE) { PutSysErr("FindFirstFile"); goto EXIT; } do { if (strcmp(fd.cFileName, ".") == 0 || strcmp(fd.cFileName, "..") == 0) continue; /* printf("%s\n", fd.cFileName); */ if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { printf("%*s%s\n", level * TABSIZE, "", fd.cFileName); WalkDirRecur(fd.cFileName, level + 1); } } while (FindNextFile(hFF, &fd)); if (GetLastError() != ERROR_NO_MORE_FILES) PutSysErr("FindNextFile"); FindClose(hFF); EXIT: if (!SetCurrentDirectory("..")) PutSysErr(lpszDirPath); } void WalkDir(LPCTSTR lpszDirPath) { char cwd[MAX_PATH]; if (!GetCurrentDirectory(MAX_PATH, cwd)) PutSysErr("GetCurrentDirectory"); WalkDirRecur(lpszDirPath, 0); if (!SetCurrentDirectory(cwd)) PutSysErr("SetCurrentDirectory"); } int main(void) { WalkDir("C:\\Dropbox"); return 0; } void PutSysErr(LPCSTR lpszMsg) { DWORD dwLastErr = GetLastError(); LPTSTR lpszErr; if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastErr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpszErr, 0, NULL)) { fprintf(stderr, "%s: %s", lpszMsg, lpszErr); LocalFree(lpszErr); } } * Örnek 9.2, Dizin ağacını dolaşırken bulunan dizin girişlerini ekrana yazdırmak yerine başka bir şey yapmak isteyebiliriz. Böyle bir fonksiyonun genelleştirilmesi için fonksiyon göstericilerinden faydalanılabilir. Aşağıdaki örnekte dizin girişleri bulundukça bir callback fonksiyon çağrılmaktadır. Böylece WalkDir fonksiyonunu kullanan kişiler dosyaları ekrana yazdırmak yerine başka işlemler de yapabilirler. #include #include #include #define TABSIZE 4 void PutSysErr(LPCSTR lpszMsg); void WalkDirRecur(LPCSTR lpszDirPath, int level, void (*Proc)(const WIN32_FIND_DATA *, int)) { HANDLE hFF; WIN32_FIND_DATA fd; if (!SetCurrentDirectory(lpszDirPath)) { PutSysErr("SetCurrentDirectory"); return; } if ((hFF = FindFirstFile("*.*", &fd)) == INVALID_HANDLE_VALUE) { PutSysErr("FindFirstFile"); goto EXIT; } do { if (strcmp(fd.cFileName, ".") == 0 || strcmp(fd.cFileName, "..") == 0) continue; Proc(&fd, level); if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) WalkDirRecur(fd.cFileName, level + 1, Proc); } while (FindNextFile(hFF, &fd)); if (GetLastError() != ERROR_NO_MORE_FILES) PutSysErr("FindNextFile"); FindClose(hFF); EXIT: if (!SetCurrentDirectory("..")) PutSysErr(lpszDirPath); } void WalkDir(LPCTSTR lpszDirPath, void (*Proc)(const WIN32_FIND_DATA *, int)) { char cwd[MAX_PATH]; if (!GetCurrentDirectory(MAX_PATH, cwd)) PutSysErr("GetCurrentDirectory"); WalkDirRecur(lpszDirPath, 0, Proc); if (!SetCurrentDirectory(cwd)) PutSysErr("SetCurrentDirectory"); } void DispFile(const WIN32_FIND_DATA *fd, int level) { printf("%*s%s\n", level * TABSIZE, "", fd->cFileName); } int main(void) { WalkDir("C:\\Dropbox\\Shared\\Kurslar\\SysProg-1", DispFile); return 0; } void PutSysErr(LPCSTR lpszMsg) { DWORD dwLastErr = GetLastError(); LPTSTR lpszErr; if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastErr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpszErr, 0, NULL)) { fprintf(stderr, "%s: %s", lpszMsg, lpszErr); LocalFree(lpszErr); } } * Örnek 10.0, Özyinelemeli fonksiyonlarda belli bir koşul sağlandığında özyinelemenin sonlandırılması da istenebilmektedir. Aşağıdaki örnekte callback fonksiyonun prototipi şöyledir: BOOL Proc(const WIN32_FIND_DATA *fd, int level); Fonksiyon her dizin girişi bulundukça çağrılmaktadır. Callback fonksiyon eğer sıfır dırşı bir değerle geri dönerse bu durum özyinelemenin devam edeceği anlamına gelmektedir. Eğer fonksiyon 0 ile geri dönerse bu durum özyinelemenin sonlandırılacağı anlamına gelmektedir. Tabii özyineleme sonlandırılırken eğer kaynak tahsisatı yapılmışsa (örneğimiz FindFirstFile fonksiyonunun geri döndürdüğü handle gibi) bunların serbest bırakılmasına dikkat edilmelidir. Aşağıdaki örnekte "sample.c" dosyası bulunduğunda özyineleme sonlandırılmaktadır. Bu örnekte Microsoft'a özgü _stricmp fonksiyonu kullanılmıştır. Bu fonksiyon büyük hard küçük harf duyarlılığı olmadan karşılaştırma yapmaktadır. #include #include #include #define TABSIZE 4 void PutSysErr(LPCSTR lpszMsg); BOOL WalkDirRecur(LPCSTR lpszDirPath, int level, BOOL (*Proc)(const WIN32_FIND_DATA *, int)) { HANDLE hFF; WIN32_FIND_DATA fd; BOOL bResult; bResult = TRUE; if (!SetCurrentDirectory(lpszDirPath)) { PutSysErr("SetCurrentDirectory"); return TRUE; } if ((hFF = FindFirstFile("*.*", &fd)) == INVALID_HANDLE_VALUE) { PutSysErr("FindFirstFile"); goto EXIT1; } do { if (strcmp(fd.cFileName, ".") == 0 || strcmp(fd.cFileName, "..") == 0) continue; if (!Proc(&fd, level)) { bResult = FALSE; goto EXIT2; } if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) if (!WalkDirRecur(fd.cFileName, level + 1, Proc)) { bResult = FALSE; goto EXIT2; } } while (FindNextFile(hFF, &fd)); if (GetLastError() != ERROR_NO_MORE_FILES) PutSysErr("FindNextFile"); EXIT2: FindClose(hFF); EXIT1: if (!SetCurrentDirectory("..")) PutSysErr(lpszDirPath); return bResult; } BOOL WalkDir(LPCTSTR lpszDirPath, BOOL (*Proc)(const WIN32_FIND_DATA *, int)) { char cwd[MAX_PATH]; BOOL bResult; if (!GetCurrentDirectory(MAX_PATH, cwd)) PutSysErr("GetCurrentDirectory"); bResult = WalkDirRecur(lpszDirPath, 0, Proc); if (!SetCurrentDirectory(cwd)) PutSysErr("SetCurrentDirectory"); return bResult; } BOOL DispFile(const WIN32_FIND_DATA *fd, int level) { printf("%*s%s\n", level * TABSIZE, "", fd->cFileName); if (!_stricmp(fd->cFileName, "sample.c")) return FALSE; return TRUE; } int main(void) { WalkDir("C:\\Dropbox\\Shared\\Kurslar\\SysProg-1", DispFile); return 0; } void PutSysErr(LPCSTR lpszMsg) { DWORD dwLastErr = GetLastError(); LPTSTR lpszErr; if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastErr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpszErr, 0, NULL)) { fprintf(stderr, "%s: %s", lpszMsg, lpszErr); LocalFree(lpszErr); } } * Örnek 10.1, Pekiyi callback fonksiyon içerisinde dosyaya ilişkin tam yol ifadesi (full path) nasıl elde edilebilir? Yukarıdaki örneğimizde zaten callback fonksiyon çağrıldığında prosesin çalışma dizini dizin girişinin içinde bulunduğu dizin biçimindedir. Dolayısıyla callback fonksiyon içerisinde GetCurrentDirectory fonksiyonu uygulanıp buradan elde edilen yol ifadesi ile callback fonskyiona geçirilen WIN32_FIND_DATA içerisindeki dosya birleştirilirse tam yol ifadesi elde edilebilir. Aşağıdaki örnekte "sample.c" dosyalarının bulunduğu tam yol ifadeleri yazdırılmıştır. #include #include #include #define TABSIZE 4 void PutSysErr(LPCSTR lpszMsg); BOOL WalkDirRecur(LPCSTR lpszDirPath, int level, BOOL(*Proc)(const WIN32_FIND_DATA *, int)) { HANDLE hFF; WIN32_FIND_DATA fd; BOOL bResult; bResult = TRUE; if (!SetCurrentDirectory(lpszDirPath)) { PutSysErr("SetCurrentDirectory"); return TRUE; } if ((hFF = FindFirstFile("*.*", &fd)) == INVALID_HANDLE_VALUE) { PutSysErr("FindFirstFile"); goto EXIT1; } do { if (strcmp(fd.cFileName, ".") == 0 || strcmp(fd.cFileName, "..") == 0) continue; if (!Proc(&fd, level)) { bResult = FALSE; goto EXIT2; } if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) if (!WalkDirRecur(fd.cFileName, level + 1, Proc)) { bResult = FALSE; goto EXIT2; } } while (FindNextFile(hFF, &fd)); if (GetLastError() != ERROR_NO_MORE_FILES) PutSysErr("FindNextFile"); EXIT2: FindClose(hFF); EXIT1: if (!SetCurrentDirectory("..")) PutSysErr(lpszDirPath); return bResult; } BOOL WalkDir(LPCTSTR lpszDirPath, BOOL(*Proc)(const WIN32_FIND_DATA *, int)) { char cwd[MAX_PATH]; BOOL bResult; if (!GetCurrentDirectory(MAX_PATH, cwd)) PutSysErr("GetCurrentDirectory"); bResult = WalkDirRecur(lpszDirPath, 0, Proc); if (!SetCurrentDirectory(cwd)) PutSysErr("SetCurrentDirectory"); return bResult; } BOOL DispFile(const WIN32_FIND_DATA *fd, int level) { char buf[1024]; if (!_stricmp(fd->cFileName, "sample.c")) { GetCurrentDirectory(MAX_PATH, buf); strcat(buf, "\\"); strcat(buf, fd->cFileName); printf("%s\n", buf); } return TRUE; } int main(void) { WalkDir("C:\\Dropbox\\Shared\\Kurslar\\SysProg-1", DispFile); return 0; } void PutSysErr(LPCSTR lpszMsg) { DWORD dwLastErr = GetLastError(); LPTSTR lpszErr; if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastErr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpszErr, 0, NULL)) { fprintf(stderr, "%s: %s", lpszMsg, lpszErr); LocalFree(lpszErr); } } * Örnek 11.0, UNIX/Linux sistemlerinde de dizin ağacı benzer biçimde dolaşılır. Yine özyinelemeli fonksiyon girişte prosesin çalışma dizinini set eder, çıkışta da çalışma dizinini yeniden üst dizin olacak biçimde ayarlar. Aşağıda tipik bir örnek verilmiştir. UNIX/Linux sistemlerinde readdir fonksiyonu ile yalnızca dizin girişinin isminin ve i-node numarasının elde edildiğini anımsayınız. Dosya bilgilerinin elde edilmesi için stat fonksiyonları kullanılmalıdır. Ancak bu tür uygulamalarda stat yerine lstat fonksiyonu tercih edilmelidir. stat fonksiyonunun sembolik bağlantıları izlemesi sonsuz döngü oluşumuna yol açabilmektedir. #include #include #include #include #include #include #include void walkdir(const char *path) { DIR *dir; struct dirent *de; struct stat finfo; if (chdir(path) == -1) { perror("chdir"); return; } if ((dir = opendir(".")) == NULL) { perror("opendir"); goto EXIT; } while (errno = 0, (de = readdir(dir)) != NULL) { if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; if (lstat(de->d_name, &finfo) == -1) { perror("lstat"); continue; } if (S_ISDIR(finfo.st_mode)) { printf("%s\n", de->d_name); walkdir(de->d_name); } } if (errno != 0) perror("readdir"); closedir(dir); EXIT: if (chdir("..") == -1) perror("chdir"); } int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "wrong number of arguments!..\n"); exit(EXIT_FAILURE); } walkdir(argv[1]); return 0; } * Örnek 11.1, Yukarıdaki programı yine Windows sistemlerinde yaptığımız gibi kademeli görüntüleyecek aşağıdaki gibi biçimde değiştebiliriz. #include #include #include #include #include #include #include #define TABSIZE 4 void walkdir(const char *path, int level) { DIR *dir; struct dirent *de; struct stat finfo; if (chdir(path) == -1) { perror("chdir"); return; } if ((dir = opendir(".")) == NULL) { perror("opendir"); goto EXIT; } while (errno = 0, (de = readdir(dir)) != NULL) { if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; if (lstat(de->d_name, &finfo) == -1) { perror("lstat"); continue; } if (S_ISDIR(finfo.st_mode)) { printf("%*s%s\n", level * TABSIZE, "", de->d_name); walkdir(de->d_name, level + 1); } } if (errno != 0) perror("readdir"); closedir(dir); EXIT: if (chdir("..") == -1) perror("chdir"); } int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "wrong number of arguments!..\n"); exit(EXIT_FAILURE); } walkdir(argv[1], 0); return 0; } * Örnek 11.2, Fonksiyonun prosesin çalışma dizinini geri set eden sarmalı biçimi de aşağıdaki gibi olabilir. #include #include #include #include #include #include #include #define TABSIZE 4 void walkdir_recur(const char *path, int level) { DIR *dir; struct dirent *de; struct stat finfo; if (chdir(path) == -1) { perror("chdir"); return; } if ((dir = opendir(".")) == NULL) { perror("opendir"); goto EXIT; } while (errno = 0, (de = readdir(dir)) != NULL) { if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; if (lstat(de->d_name, &finfo) == -1) { perror("lstat"); continue; } if (S_ISDIR(finfo.st_mode)) { printf("%*s%s\n", level * TABSIZE, "", de->d_name); walkdir_recur(de->d_name, level + 1); } } if (errno != 0) perror("readddir"); closedir(dir); EXIT: if (chdir("..") == -1) perror("chdir"); } void walkdir(const char *path) { char cwd[4096]; if (getcwd(cwd, 4096) == NULL) { perror("getcwd"); return; } walkdir_recur(path, 0); if (chdir(cwd) == -1) { perror("chdir"); return; } } int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "wrong number of arguments!..\n"); exit(EXIT_FAILURE); } walkdir(argv[1]); return 0; } * Örnek 11.3, Aşağıda dizin ağacını dolaşırken callback fonksiyonu çağıran bir örnek verilmiştir. Burada callback fonksiyon Windows sistemlerinde yaptığımız örneğe benzerdir: bool walkproc(const char *name, const struct stat *finfo, int level); Yine callback fonksiyon çağrıldığında prosesin çalışma dizini dizin girişinin bulunduğu dizindedir. Fonksiyon sıfır dışı bir değerle geri döndürülürse dolaşma devam ettirlir, 0 değeri ile geri döndürülürse dolaşma sonlandırılır. #include #include #include #include #include #include #include #include #define TABSIZE 4 bool walkdir_recur(const char *path, bool (*proc)(const char *, const struct stat *, int), int level) { DIR *dir; struct dirent *de; struct stat finfo; bool retval; retval = true; if (chdir(path) == -1) { perror("chdir"); return true; } if ((dir = opendir(".")) == NULL) { perror("opendir"); goto EXIT1; } while (errno = 0, (de = readdir(dir)) != NULL) { if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; if (lstat(de->d_name, &finfo) == -1) { perror("lstat"); continue; } if (!proc(de->d_name, &finfo, level)) { retval = false; goto EXIT2; } if (S_ISDIR(finfo.st_mode)) if (!walkdir_recur(de->d_name, proc, level + 1)) { retval = false; goto EXIT2; } } if (errno != 0) perror("readddir"); EXIT2: closedir(dir); EXIT1: if (chdir("..") == -1) perror("chdir"); return retval; } void walkdir(const char *path, bool (*proc)(const char *, const struct stat *, int)) { char cwd[4096]; if (getcwd(cwd, 4096) == NULL) { perror("getcwd"); return; } walkdir_recur(path, proc, 0); if (chdir(cwd) == -1) { perror("chdir"); return; } } bool disp(const char *name, const struct stat *finfo, int level) { printf("%*s%s\n", level * TABSIZE, "", name); return true; } int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "wrong number of arguments!..\n"); exit(EXIT_FAILURE); } walkdir(argv[1], disp); return 0; } * Örnek 12, Aşağıdaki örnekte belli bir kök dizinden başlanarak "sample.c" dosyaları bulunmuş, onların mutlak yol ifadeleri ve uzunlukları yazdırılmıştır. #include #include #include #include #include #include #include #include #define TABSIZE 4 bool walkdir_recur(const char *path, bool (*proc)(const char *, const struct stat *, int), int level) { DIR *dir; struct dirent *de; struct stat finfo; bool retval; retval = true; if (chdir(path) == -1) { perror("chdir"); return true; } if ((dir = opendir(".")) == NULL) { perror("opendir"); goto EXIT1; } while (errno = 0, (de = readdir(dir)) != NULL) { if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; if (lstat(de->d_name, &finfo) == -1) { perror("lstat"); continue; } if (!proc(de->d_name, &finfo, level)) { retval = false; goto EXIT2; } if (S_ISDIR(finfo.st_mode)) if (!walkdir_recur(de->d_name, proc, level + 1)) { retval = false; goto EXIT2; } } if (errno != 0) perror("readddir"); EXIT2: closedir(dir); EXIT1: if (chdir("..") == -1) perror("chdir"); return retval; } void walkdir(const char *path, bool (*proc)(const char *, const struct stat *, int)) { char cwd[4096]; if (getcwd(cwd, 4096) == NULL) { perror("getcwd"); return; } walkdir_recur(path, proc, 0); if (chdir(cwd) == -1) { perror("chdir"); return; } } bool disp(const char *name, const struct stat *finfo, int level) { char buf[4096]; if (!strcmp(name, "sample.c")) { getcwd(buf, 4096); strcat(buf, "/"); strcat(buf, name); printf("%s, %lld\n", buf, (long long)finfo->st_size); } return true; } int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "wrong number of arguments!..\n"); exit(EXIT_FAILURE); } walkdir(argv[1], disp); return 0; }