[A'dan Z'ye] C Programlama Dilini Öğrenelim!

CyberXhackk

Kıdemli Üye
13 Mar 2016
3,132
10
C/C++ Dev.

C Programlama Dili 1 - Compile Time Hakkında Bilgiler
WDbwNI.gif



C programlama dili


Merhaba arkadaşlar, bu yazımda sizlere C programlama dilinin detaylarından ve inceliklerinden bahsedeceğim. Uzun soluklu bir yazı olacaktır öncelikle onu belirteyim. Girebildiğim kadar girmeye ve pointer mantığını elimden geldiğince basitleştirerek anlatmaya çalışacağım.



Neden C Öğrenmeliyiz?

Programlama dilleri problemin çözümüne doğru giden yolda kullanılan bir araçtan ibarettir. Her programlama dili ile X problemi çözülebilir. Fakat X problemini çözmek için hangi dil uygundur kısmına gelecek olursak bu konu tartışmaya açık bir hale gelir. Belli programlama dilleri belli sorunları çözmek için ortaya çıkagelmiştir. Yazının içinde de göreceksiniz zaten C dilinin ortaya çıkma sebebi UNIX'in geliştirilmek istenmesidir. Yani kısaca her dilin belirli kullanım amaçları vardır diyebiliriz. C programlama dili sembolik makine diline en yakın dil olmasından dolayı çok hızlı çalışmaktadır. Piyasada bulunan en hızlı dildir. Bu cümleden anlayacağınız üzere hızın kritik olduğu durumlarda bu dili kullanmalıyız. Gömülü sistemler, sistem programları, işletim sistemleri, otonom araçlar, akıllı ev sistemleri. Bu uygulama alanlarının dünyasında C/C++ olmazsa olmaz diyebiliriz.

C bilmem ne gibi avantaj sağlayacak ?


İlk olarak C bilmek diğer dillere göre çok daha iyi düzeyde programlama mantığının anlaşılmasını sağlar. C öğrenen kişi çok fazla fonksiyon bulunmadığından dolayı düşündüğü algoritmaları kendi geliştirmek zorundadır. Kısaca C öğrenmek algoritma geliştirme yeteceği katar. Ram ile doğrudan ilişkili olduğumuz bu C dilinde Ram, bilgisayar çalışma mimarisi gibi konuları çok daha iyi anlamamızı sağlar. C dilinde her şey geliştiriciye bırakıldığı için dinamik bellek yönetimi denilen bir kavram oluşmuştur. Bu kavram ile heap-stack alanını daha yakından tanıyıp kavrayabiliriz. Piyasadaki çoğu dil C dilinden esinlenerek ortaya çıkmıştır. C asla eskimez, her daim C/C++ dillerine ihtiyaç olacaktır.
Kısaca özetleyecek olursak;

*Problem çözme becerisi
*Bilgisayarın çalışma mimarisi
*Derleyicinin çalışma mantığı
*Diğer dillere geçişlerde o dilleri daha kolay anlamak
*Gömülü sistemler üzerine çalışabilme
*İşletim sistemlerini daha yakından tanıma(çalışma mantığı)


C Dilinin Tarihçesi

0Qy6IO.jpg



C programlama Dili 1970-1971 yıllarında AT&T Bell laboratuvarında UNIX işletim sisteminin geliştirilmesi süresinde yan ürün olarak tasarlandı. AT&T o zamanlar Multics isimli bir işletim sistemi projesinde çalışıyordu. AT&T bu projeden çekildi ve kendi işletim sistemlerini yazma yoluna saptı. Bu işletim sistemine Multics üzerinden kelime oyunu yapılarak UNIX ismi verildi. O zamanlar işletim sistemleri sembolik makine dili ile yazılıyorlardı. Ken Thompson işleri kolaylaştırmak için B isimli programlama dilini geliştirdi. Sonraki yıllarda Dennis Ritchie bunu geliştirerek C haline getirdi. UNIX işletim sistemi 1973 yılında sil baştan yeniden C ile yazıldı. O zamanlar ilk defa gelişmiş bir programlama dili kullanılarak işletim sistemi yazılmıştır, kısacası bu olay programlama tarihinde devrim niteliği taşımaktadır. 1980 yıllarında IBM ilk kişisel bilgisayarlarını piyasaya sürdü. 1978 yılında Dennis Ritchie ve Brian Kernigan tarafından ‘The C Programming Language’ kitabı yazıldı. C programlama Dili kişisel bilgisayarlarda kullanılan en yaygın dil oldu.

CBzdH1.jpg



Programlama Dillerinin Seviyelerine Göre Sınıflandırılması

zH3WIM.jpg


Programlama dillerindeki seviyenin ölçütü programlama dilinin insan algısına yakınlılığını temsil eder. Yüksek seviyeli diller insan algısına en yakın dillerdir ve kolay öğrenilirler. Alçak seviyeli diller ise insan algısına en uzak dillerdir ve öğrenilmeleri oldukça zordur. En alçak seviyeli dil ise makine dilidir (machine language). Bu sistem binary system olarak da geçmektedir. Sadece 1 ve 0’ların olduğu kodlardan bahsediyoruz ve yazmanın ne kadar zor ve zahmetli olduğunu az çok tahmin edebilirsiniz. Bu dilin bir üstünde Assembly dili mevcuttur. Assembly makine dilini göre her ne kadar daha kolay olsa dahi yine de insanlar için algılanması ve kodlanması zor bir dildir. Bunun bir üstünde ise C dili bulunmaktadır. C dili Ingilizce diline daha yakın olmakla beraber, basit matematik işlemi oparetörleri ve basit anahtar kelimeleri ile birlikte çok daha anlaşılır bir dildir (assembly diline göre).



COMPILE (Derlemek)


1COQST.jpg


Compile Ingilizce bir kelimedir. Türkçe anlamı derlemek demektir. C compile edilmesi gereken bir dildir. İşletim sistemi bizim yazdığımız Ingilizce kelimelerden oluşan kodu anlayamaz, bu kodu compile ederek kendinin anlayacağı kod parçacıklarına dönüştürmek zorundadır. Bir C kodunu derlediğimiz taktirde ilk olarak assembler yardımı ile assembly koduna dönüştürülür, ondan sonra ikilik kod dediğimiz makine koduna dönüştürülür, artık işletim sistemi yazdığımız kodun ne olduğunu anlamaktadır.

Günümüzde en çok kullanılan derleyici GCC isimli derleyicidir. GNU C compiler kelimelerinin baş harfinden oluşmaktadır. Microsoft tarafında da çeşitli derleyiciler bulunmaktadır.




IDE KAVRAMI

Normal olarak derleyiciler komut satırından çalıştırılan programlardır. Editör ortamında kod yazılır, terminal üzerinde bu kod derlenir. İşin aslı bu şekildedir. Fakat günümüzde IDE denilen bir kavram ortaya çıkmıştır. Integrated Development Environment kelimelerinin baş harflerinden oluşmaktadır. IDE’lerin çıkma sebebi ise programı editör üzerinde yazıp daha sonra terminal ekranında derlemek çok zahmetli olmaktadır. Biz bilgisayarımıza herhangi bir IDE kurduğumuz taktirde onunla birlikte bir derleyici kurulur. IDE aslında derleyici değildir. Bizim kod yazmamızı ve derlememizi kolaylaştıran bir programdır. Compile & Run gibi butonlar mevcuttur. Derleyici değilse compile butonunun işi ne diyebilirsiniz. Biz o butona bastığımız taktirde terminalde el ile derleme derdinden kurtuluruz, arka planda program bunu bizim için yapmaktadır.



Ön işlem ( preprocess)


C programımız derlenmeden önce ön işlem denilen bir işlemden geçirilmektedir. Bu işlem programın en üstünde # (diyez) işareti ile başlayan satırları yorumlar. Bu satırlar yorumlandıktan sonra ve gerekli işlemler yapıldıktan sonra compile işlemine başlanır.



Compile processing

Compile işlemi tokenizing işlemi ile başlamaktadır. Nedir bu işlem biraz bundan bahsedelim. Derleyici kodu anlamlandırmadan önce kodun içeriğini Token parçalarına ayrılır. Bu işlem yapılırken derleyici isimlere ve cümlelere bakmaz sadece tokenlerine ayırır. Kodun anlamını, neler yaptığını, koddaki mantık hatalarını o evrede anlayamaz. Token en küçük birim anlamına geliyor şeklinde düşünebiliriz. Tokenler nelerdir onları inceleyelim.

Keywords/Reserved Words (Anahtar Sözcük)

Anahtar sözcükler dilin tasarımınca önceden belirlenmiş, baştan özel bir anlam yüklenmiş ve başka bir anlamca kullanılması yasaklanmış olan sözcüklerdir. C dilinde 32 adet anahtar sözcük bulunmaktadır.

4eH93O.jpg


Identifiers (İsimlendirme)

2LbxTT.png


C dilindeki varlıklara verilen özgün isimlerdir. Örnek olarak şunları verebiliriz:
*Değişkenlere verilen isimler
*Fonksiyonlara verilen isimler
*Sabitlere verilen isimler
*Etiketlere verilen isimler


Operators (Operatörler)

Operatörler belli bir işlem yaptıran tokenlerdir. Programlama dilinden programlama diline değişiklik gösterse de genel yapısı aynıdır. Matematik işlemleri, mantık işlemleri gibi işlemleri operatörler sayesinde yapabiliyoruz.

BeQIWK.jpg




Constant/Literal (Sabitler)

Kod içerisinde doğrudan sayısal büyüklük veya karakter dizisi belirten ifadelerdir. Sayısal sabitleri yazmak için 3 ayrı sistem bulunmaktadır.

*Decimal (Onluk)
*Hexadecimal (Onaltılık)
*Octal (Sekizlik)


Decimal sayılar yazılırken olduğu gibi yazılır.
Hexadecimal sayılar yazılırken başlarına ‘0x’ veya ‘0X’ değerlerini alarak yazılırlar. Octal sayılar yazılırken başlarına ‘0’ değerini alarak yazılırlar.
0x35 (10), 035 (16), 35 (8)
Yukarıda gördüğümüz 3 değer de birbirinden farklı sayıları temsil etmektedir.


String Literal (Karakter Sabitleri)

Çift tırnak arasına yazılmış sabitlerdir. ASCII kodlarına göre nitelendirilmektedirler.

eHSN5O.jpg



Yukarıdaki kodda a,b ve c değişkenlerinin değeri sabit değerdir. Eğer c = a şeklinde bir şey deseydik c değerinin içi sabit değil değişken olacaktı.

Delimeters/Punctuators (Ayıraçlar)

Yukarıda belirttiğimiz grupların dışında kalan, ifadeleri ayırmak için kullanılan tüm atomlara ayıraçlar denir. Örnek verecek olursak ';' ve '{ }' ayıraç atomudur.




Derleyici Kuralları

KJKOyP.png


Tokenizing (atomlarına ayırma) aşamasında yazım kuralı hatası (syntax) bulunmaması gerekmektedir. 2 token arasında herhangi bir satır atlama, boşluk bırakma, boşluk bırakmama gibi bir kural yoktur. Biz çok okunaklı bir kod bile yazsak tokenizing açamasında bu kodlar birleştirilmektedir. Programcı açısından her ne kadar bu bir kural olmasa dahi okunabilirlik açısından okunaklı kod yazmak önemlidir.






Global nameSpace/Local namespace

Yazdığımız C kodu 2 farklı bölümden oluşmaktadır. Global isim alanı ve yerel isim alanı. Fonksiyonlarımızın içinde kalan kısım bizim yerel isim alanımızdır. Fonksiyonların dışında kalan kısım ise global isim alanımızdır. Bu alanların özelliklerine birazdan geleceğiz.


8PB8NI.jpg




Declaration /Statement

C dilinde kullandığımız her cümle başlıktaki 2 yapıdan birini içermelidir. Declaration bildirim anlamına gelmektedir. Herhangi bir işlem yapılmaz. Sadece bildirim yapılır. Hem global namespace alanında hem de local namespace alanında kullanılabilir. Bir değişkeni veya fonksiyonu tanımlarken kullandığımız cümle demektir. Bildirimde bulunmazsak compiler o değişkenin veya fonksiyonun ne olduğunu tanımayacaktır.

Statement ise deyim anlamına gelmektedir. Compiler deyimleri gördüğü zaman bir işlem yapmasını gerektiğini bilmektedir. Döngü yazarken, karar yapısı koyarken, matematik işlemi yaparken vb. deyimler kullanılmaktadır. Deyimler global namespace üzerinde yazılamaz. Local namespace yani fonksiyon içerisinde yazılmak zorundadır.

02ffLG.jpg



Yukarıdaki örnekteki kodu derlediğiniz taktirde hata verecektir. İki değişkeni de global alanda tanımladık, kuralımıza göre global alanda sadece declaration yapabileceğimiz yönündeydi. İlk değişkeni tanımlarken sıkıntı yok, fakat ikinci değişken bir deyim içeriyor. Bir sabit ile bir değişkenin toplanması deyim anlamına geliyor. 5 + 3 diyebilirdik, çünkü 2 değer de Constant değer içeriyor, fakat böyle bir kullanım yapamayız.

#include Direktifi

Bu komut önişlemci komutudur. Bu komut derleme sırasında komutun yerleştirildiği yere kopyalanacağını belirtir, bu satırı silip yerine stdio.h isimli dosyanın içeriğini oraya yerleştirmek ile aynı sonucu doğurur. #include sözcüğü ise dahil etmek anlamına gelir. C dilinde en temel fonksiyonları kullanabilmemiz için bile temel kütüphaneleri dahil etmemiz gerekir, diğer dillerde alışılagelenin aksine.

C Dilinde Karşınıza Çıkabilecek Temel Kütüphaneler

<stdio.h> En temel c kütüphanesidir, standart girdi çıktı fonksiyonlarına ulaşmamız için gereklidir. Stdin, stdout, stderr dosyalarını kullanır.

<stdlib.h> Temel c kütüphanelerinden biridir, temel fonksiyonlara ulaşmamız için bu kütüphaneleri dahil etmemiz gerekmektedir. Bu kütüphane ile gelen fonksiyonları sıralayalım;

Kod:
 abort();
abs();
atexit();
atof();
atoi();
atol();
bsearch();
calloc();
div();
exit();
free();
getenv();
labs();
ldiv();
malloc();
mblen();
mbstowcs();
mbtowc();
qsort();
rand();
realloc();
srand();
strtod();
strtol();
strtoul();
system();
wcstombs();
wctomb();


<math.h> Adından da anlaşılacağı üzere matematik işlemleri yapmamıza yarayan fonksiyonları içeren kütüphanedir. Bu kütüphane ile gelen fonksiyonları sıralayalım;

Kod:
 acts();
asin();
atan();
atan2();
ceil();
cos();
cosh();
exp();
fabs();
floor();
fmod();
frexp();
ldexp();
log();
log10();
modef();
pow();
sin();
sinh();
sqrt();
tan();
tanh();

<string.h> Karakter dizilerinde belli işlemler yapmamızı sağlayan fonksiyonlardır. Bu kütüphane içerisinde çok fazla üst düzey fonksiyon bulunmaktadır. Gelin bu fonksiyonları da inceleyelim.

Kod:
memccpy();
memchr();
memcmp();
memcpy();
memcpy_s();
memmove();
memmove_S();
memset();
memset_s();
strcat();
strcat_s();
strchr();
strcmp();
strcoll();
strcpy();
strcpy_s();
strcspn();
strdup();
strerror();
strerror_s();
strerrorlen_s();
strlen();
strlen_s();
strncat();
strncat_s();
strncmp();
strncpy_s();
strndup();
strpbrk();
strrchr();
strspn();
strstr();
strtok();
strtok_s();
strxfrm();



#define Direktifi

Define bir ön işlemci emridir, programın içinde kesinlikle değiştirilmesini istemediğimiz verilerimiz için kullanılır. Ön işlemci tarafından işlendiği için program içerisinde değişken gibi değil Constant/literal (sabit) olarak değerlendirilir. Genel kullanım olarak define değerlere isim atama büyük harf ile yapılır, değer ile isim arasına eşittir konulmaz, sadece boşluk bırakılır.

Örnek kullanım ;
Kod:
#define A 15
#define PI 3.14

Not: Define ile sabit tanımlarken büyük harf alışılagelen bir kullanımdır, programcıların dikkat etmesi gerekir.



C Dilinde Değişkenler

Değişkenler adı üzerinde istediğimiz zaman değiştirebildiğimiz bilgisayarın belleği (ram) üzerinde depolanan verilerimizdir.

Neden veri depolamaya ihtiyaç duyarız?

-Programın belli noktalarında kullanıcıdan veri almak isteyebiliriz, programın akışını kullanıcıdan kullanıcıya değiştirebilmek için.

-Belli durumlarda belli işlemlerin yapılmasını ve bu yaptığımız işlemleri programın başka kısmında kullanmak isteyebiliriz. Bunun için geçici bir depolama ünitesine ihtiyaç duyarız.

Ram üzerinde depoladığımız verilerin (değişken) belli bir veri tipi olmak zorundadır. Bu veri tipinin ne olduğunu değişkeni tanımladığımız satırda belirtmek zorundayız. Kısaca bir değişken tanımlamak için şöyle bir kullanım yaparız.

Kod:
veri_tipi degisken_ismi(Identifier) = degisken_degeri ;

Veri tipini belirleriz. Değişkene bir isim veririz ve bu verdiğimiz isim değişkenin ne ile ilgili olduğunu çağrıştırmalıdır, daha sonra programın başka bölümünde çağırırken unutmamak adına. Değişkene isim verirken belli kurallar çerçevesinde veririz bu ismi. O kurallara daha sonra geleceğiz. Ve sonra =(atama operatörü) kullanırız. Matematikteki eşittir işareti ile karıştırmayın. İşaretin buradaki amacı sağdaki değeri al, soldaki değişkene eşitle demektir. Atama operatöründen sonra ise belirlediğimiz değişken tipine göre bir veri veririz.

Ya veriyi kullanıcıdan almak istiyorsak?

Kod:
int x;
scanf("%d",&x);

Veriyi kullanıcıdan almak istediğimiz taktirde sadece veri tipi ve isim belirtmemiz yeterlidir. Bellek üzerinde değişken için bir yer açarız. Henüz bellekte bir değeri yoktur, ama adresi vardır. Kısaca programa x değişkeni için bir yer ayır, ben daha sonra o yeri dolduracağım deriz. scanf fonksiyonu yani kullanıcıdan girdi almamızı sağlayan fonksiyon ile kullanıcıdan veri alırız ve o veriyi x’in adresine kaydet deriz.

Değişken İsim Kuralları

-Değişkenlere verdiğimiz isim sayı ile başlayamaz. Fakat içinde veya sonunda sayı geçebilir.

-Değişkenlere verdiğimiz isim içinde özel karakter barındıramaz. Bu kural için bir adet istisnamız mevcut _ işareti kullanılabilir.

-Değişkenlere verdiğimiz isim 32 karakterden daha uzun olamaz.

-Değişkenlere verdiğimiz isim programın kendi kullandığı özel sözcükleri kullanamaz. (int, short, for, if, **** gibi…)

-C dilinde değişkenler case-sensitive yani büyük harf- küçük harf kurallı dillerdir.

Kod:
int merhaba;
int mErHaBa;

Yukarıda tanımladığımız 2 değişken birbirinden bağımsızdır, ayrıca olan bir değişken ile tıpatıp aynı isimde bir değişken tanımlarsak hata alırız.


C Dilinde Temel Veri Tipleri

Veri Tipi nedir öncelikle ondan bahsedeyim. Veri tipi dediğimiz kavram oluşturduğumuz değişkenin türünü niteler. Aslında temel olarak veri tipi ram üzerinde kaç byte tutulacağını ve o değişkenin nasıl yorumlayacağını bilgisayarın anlaması için oluşturulmuş kavramdır. Programlama dillerinde depolamak istediğimiz her değişken ram üzerinde belli bir adreste depolanır. Verileri tiplerine ayırmamızdaki temel sebep ram üzerinde gereksiz yer harcamamaktır. Küçük sayı kullanacaksak ona göre veri tipini belirleriz ve gereksiz byte harcamış olmayız, işaretsiz tamsayı kullanacaksak ona göre belirleriz ve karmaşıklığa yol açmayız. Kısaca veri tipi bize verinin ne olduğunu söyler. Bellekteki tüm değişkenler (karakter dahi olsa) kodumuzu compile ettikten sonra 0’lar ve 1’lere dönüştürürler.

Char Veri Tipi

Char veri tipi sadece tek 1 karakter içindir. Bellekte bir byte yer tutar ve tanımlanırken ‘ ‘ işaretleri arasına yazılır. Bellekte char için ayrılan kısım bir byte olduğu için char ile 255’den fazla veri tutamayız. Karakter tutuyorduk hani biz bu veri tipiyle dediğinizi duyar gibiyim. Karakter ile 255 sayısını nasıl kıyaslayacağız? Yukarıda yazdığım cümleyi tekrar okuyalım:

Bellekteki tüm değişkenler (karakter dahi olsa) kodumuzu compile ettikten sonra 0’lar ve 1’lere dönüştürürler.

Karakter değişkeninde a harfini tutuyoruz diyelim, a harfini nasıl sayıya dönüştüreceğiz peki? Zamanında programcılar bu soruyu kendilerine sormuşlar ve ortak bir çözüm bulmaları gerektirdiğinden bir standart oluşturmuşlar. Tüm dünyanın kabul göreceği standartlar. Ve buna American Standard Code for Information Interchange(ASCII) adını koymuşlar.

00JRVx.jpg



Yukarıda gördüğümüz üzere klavye üzerinde bulunan her input değeri için bir sayısal değer bulunmaktadır.

Karakter değişkeni tanımlayıp ASCII koduna göre eşdeğerini görelim.

Kod:
char a = 'a';
printf("%d",a); // d placeholder'ı kullanarak integer değerini
printf("%c",a); // c placeholder'ı kullanarak string değerini

Yukarıda gördüğünüz gibi a değişkeninin hem char değerini hem integer değerini yazdırdık.

Integer Veri Tipi

Integer veri tipi tam sayıları depolayan veri tipimizdir. Bellek üzerinde kapladığı alan her ne kadar bilgisayardan bilgisayara farklılık gösterse de genel olarak 4 bytedır. 4 byte değer ise 32 bit yani maksimum olarak 2^32-1 değerini alabilir. Sizin bilgisayarınızda integer veri tipinin bellekte ne kadar yer kapladığını öğrenmek için sizeof() operatörünü kullanabilirsiniz.

Gelin integer veri tipini kullanarak değişken tanımlayalım.

Kod:
int x = 5;
printf("%i",x);
printf("%d",x);

Integer değişkenleri için 2 farklı placeholder (yer tutucu) kullanabiliriz. %d ve %i. Genel olarak %d kullanımı daha yaygındır.

Float ve Double Veri Tipi

Float ve double kayan ondalıklı sayılar, biraz daha türkçeleştirecek olursak virgüllü sayılar için kullanılır. Fakat programlama dilinde ondalık ve tam sayı değerini virgül ile değil nokta ile ayırırız, virgül ile ayırmayı denersek derleyici bunu anlamaz ve syntax hatası olarak karşılar. Madem ikisi de ondalık değerleri temsil ediyor. Neden 2 farklı veri tipi kullanıyoruz sorusunun yanıtına gelirsek cevabı basittir. Depoladıkları alan. Float veri tipi 4 byte kadar alan ayırır, double veri tipi ise 8 byte kadar alan ayırır. 4 byte 32 bit alana denk gelir ve integer kadar veri depolayabilir aslında. Double ise tam olarak iki katı. Bilgisayar üzerinde kayan noktalı sayıların hesaplanması net sonuç vermez, işlemi ne kadar yüksek byte üzerinde yaparsak sonuç doğruluğa o kadar yaklaşır. Ama kesin doğrudur diyemeyiz, bu sebepten ötürü programcılara double veri tipini kullanmaları tavsiye edilir.

Kod:
float x = 5.4;
float y = 5.0;
double z = 4.9;
double t = 4;

Yukarıdaki kullanımların hepsi doğrudur. Son kullanımda t değişkenine direk 4 değeri atanmıştır. Tam sayı değeri, program bunu float olarak kaydeder bellek üzerine. 4.0 yani. Float ve Double veri tipleri için yazdırırken %f placeholder’ı kullanırız. Değer okurken (scanf) float için %f double için %lf kullanırız.


C Dilinde Girdi/Çıktı Fonksiyonları

Biz kodumuzda bir girdi çıktı fonksiyonu kullandığımız veya yazdığımız zaman fonksiyon direk klavyemizden veri okumaz veya direk ekranımıza yazdırmaz. C dilinin ilk olarak UNIX ile ortaya çıktığını söylemiştik, UNIX sistemleri daha önce araştırdıysanız şu cümleyi mutlaka duymuşsunuz demektir;

UNIX Sistemlerde her şey bir dosyadır (donanım dahil).

Yani kısaca C dilinde biz dosyaya yazar, dosyadan okuruz. Kullanıcıdan veri alırken stdin dediğimiz bir dosyadan okuma yapar, ekrana bir şeyler bastırmak istediğimizde ise stdout dosyasına yazarız. Error mesajı da stderr dosyasından basılır.

Çıktı Fonksiyonları

En temel 2 çıktı fonksiyonundan bahsedeceğim sizlere, printf() ve putchar() fonksiyonları.

Printf fonksiyonu içine 2 argüman alır. Birinci argüman içerisine 2 tırnak içerisinde gireceğimiz veriyi veya kullanmak istediğimiz yer tutucuyu yazarız. 2 argümanı virgül ile ayırmak koşuluyla ikinci argümanın yerine de bir ifade (expression) yazarız. a + b, c + 3, 3+2 gibi...

Putchar fonksiyonu ise adından anlaşılacağı gibi tek bir karakteri basmaya yarar. İçine argüman olarak direk değişken verebiliriz veyahut tek tırnak ( ' ' ) şeklinde karakter verebiliriz.


Girdi Fonksiyonları

scanf, getch, getche, getchar, gets.....

Bir sürü girdi fonksiyonu kullanılmaktadır. Bunların hepsinin ufak ufak farkları mevcuttur. Bunlardan bahsedelim.

Scanf fonksiyonu en çok kullandığımız girdi fonksiyonudur. 2 argüman alır, soldaki argümana çift tırnak içerisinde yer tutucumuzu spesifik bir şekilde tanımlayabiliriz. Sağdaki argümana ise atamak istediğimiz değişkenin adresini veririz (&degisken_adi). Bu fonksiyon biz boşluk değerini verene kadarki kısmı okur. Sonrasını almaz (2 değişken okuyorsa boşluktan sonrası diğer değişkene atanır).

Gets fonksiyonu sonradan C standartlarından kaldırılmış ama hala kullanabileceğimiz bir fonksiyondur. Bu fonksiyon ise enter ('\n') girilene kadarki kısmı okur. Yani istediğimiz yerde bitirebiliriz cümlemizi.

Getchar fonksiyonu ise çok dikkatli kullanılması gereken bir fonksiyondur. Neden diye soracak olursak bu fonksiyon karakter karakter okuma yapmaktadır ve enter tuşuna basılmasını bekler. K harfine bastık ve enter tuşuna bastık, hem k karakterini hem '\n' karakterini stdin dosyasına yazmaktadır. Yani putchar ile 2 defa değişkenimizi okursak birincide K harfini bastıracak ikincide ise '\n' karakterini bastıracaktır. Buna çözüm olarak kullanmamız gereken fonksiyon ise fflush() fonksiyonudur. Dosya akışını temizlemeye yarar. Dosya akışını temizleyeceğiz, peki ya hangi dosya akışını?

Cevap belli değil mi? stdin bir dosyadır getchardan aldığı karakteri bu dosyaya yazar. Bizim buranın akışını temizlememiz gerekir. fflush(stdin); diyerek stdin dosyasının akışını temizlemek zorundayız.

Getch fonksiyonu standart kütüphane ile gelmez <conio.h> kütüphanesi ile dahil ederiz. Bu fonksiyon karakteri girdiğimiz gibi işlem yapar enter tuşuna basmamızı beklemez bizden. Karakter alırken bu fonksiyonu kullanmamız daha mantıklı olacaktır bu sebepten dolayı.




Döngüler ve Karar Yapıları

Programa dillerinin temel yapıtaşı olan döngüler ve karar yapılarından bahsedelim. C dilinde 3 farklı döngü yapısı vardır. Hepsi birbirine çevrilebilir, aralarında hiç bir fark bulunmamaktadır. Bu döngülerden bahsedelim;
for döngüsü, while döngüsü ve do while döngüsü

For Döngüsü

For döngülerinin içi 3 kısımdan oluşur. Birinci kısım ilk atama kısmıdır. Eski C standartlarında ilk değişken oluşturma kısmını for döngüsünün üstünde yapmak zorundaydık, yeni C standartlarında bu kalkmış bulunmaktadır. İkinci kısım koşul kısmıdır. Buraya girdiğimiz koşul true döndüğü müddetçe döngümüz çalışacaktır. Döngünün içerisinde 2 noktalı virgül bulunur ve bu 2 noktalı virgül döngüyü 3 parçaya ayırır. Döngünün üçüncü kısmı ise güncelleme kısmıdır. Döngüden çıkmak için bir güncelleme yapmak zorundayız, yoksa sonsuz döngüye gireriz.

Kod:
for (tanım kısmı ; kontrol kısmı ; güncelleme kısmı );

While Döngüsü

While döngüsünün içi bir kısımdan oluşur. Yapı olarak for döngüsünden farklı görünse de tamamen aynıdır. Her for döngüsü while döngüsü ile yapılabilir, her while döngüsü for döngüsü ile yapılabilir. While döngüsü tek kısımdan oluşuyor demiştik, bu kısım ise döngünün karar yapısı, true ise döngü çalışır. False ile döngü çalışmaz. C de true false olayından bahsetmiştik. 0 False değere sahiptir, onun dışındaki bütün değerler true değere sahiptir. Negatif sayılar da dahil olmak üzere.

Kod:
int j = 0; //tanım kısmı
while(j< 10) // kontrol kısmı
{
j++; // güncelleme kısmı
}

Do-While Döngüsü

Do while döngüsü while döngüsüyle hemen hemen aynı bir yapıdadır. Tek farkı önce scope alanındaki işlemler yapılır, sonra kontrolden geçer. While 0 yazsak bile o işlemler en az 1 defa yapılacaktır yani.

Kod:
do{

j++;

}while(j<10);

If-Else Karar Yapısı

If-Else karar yapıları belli koşullarda belli işlemleri yapabildiğimiz yapılardır. Programın genel akışı if-else yapısı ile şekillenir. If içine bir veya birden daha fazla koşul yapılabilir. Döngüden farklı yapan şey bir defa yapılmasıdır işlemlerin.

Kod:
int x =5;
if(x == 5)
{
printf("if koşulu çalıştı");
}

If yapısının ardından derleyici else-if veya else yapısını bekler. Yoksa da ilgilenmez. If doğru ise else-if veya else yapılarının doğruluğu kontrol edilmez.


Yanlış kullanım
Kod:
int x = 5;
if(x <10)
{
printf("if çalıştı);
}
if(x >10)
{
printf("ikinci if çalıştı");
}

Yukarıda yanlış bir kullanım gösterdim, neden yanlış diyeceksiniz. x'in 10'da küçük olması ile x'in 10'dan büyük olması aynı anda mümkün olamaz. Ya küçüktür ya büyüktür. Program 2 defa if yapısına girerek yavaşlayacaktır. Doğru kullanımı ise if-else veya if-else if şeklinde olmalıdır. Çünkü bu yapılarda if doğru ise diğer bağımlı bloklar atlanmaktadır.

Doğru kullanım
Kod:
int x = 5;
if(x <10)
{
printf("if çalıştı);
}
else if(x >10)
{
printf("else if çalıştı");
}


Switch-Case Karar yapısı

Bu karar yapısı çok fazla olasılık varsa kullanılması gereken bir yapıdır. Her switch case yapısı if else if yapısı ile yazılabilir, her if else if yapısı da switch case ile yazılabilir. Fakat fazla olasılık olan durumlarda if else if kullanmak çok zahmetli olmaktadır.

Kod:
switch(değişken)
{
case 1:
xxxxx;
break;
case 2:
yyyyy;
break;
case 3:
zzzzzz;
break;
default:
deger bulunamadı;
}

Switch içine bir değişken alır, diyelim x değişkenini verdik. X değişkenindeki değere bakıyoruz, bu değer de 5 olsun. case 1'e bakar 2'ye bakar, en son da 3'e bakar. Hiçbiri bulunamadıysa default öntanımlı değere girer.



Pointer(işaretçi) Nedir

Pointer ingilizce bir kelimedir. Türkçe anlamı İşaretçi , göstergedir. Kelimenin anlamından anlayacağımız üzere Pointer dediğimiz kavram bir değişkenin adres bilgisini depolar, o değişkeni gösterir. Yani kısaca pointer herhangi bir değişkenin ram üzerinde tutulduğu adres bilgisidir.

Biz herhangi bir programlama dilinde bir değişken oluşturduğumuz zaman oluşturduğumuz değişken ram üzerinde rastgele (random) bir bellek ünitesine yerleşir. Ram üzerindeki adresleme işlemi 16’lık sayı tabanına (hexadecimal) göre yapılır.


Kod:
int x =5; // x adında değişken oluşturup içerisine 5 değerini atıyoruz

Bu x değişkeninin bellek üzerinde rastgele bir yer üzerinde depolandığını biliyoruz. Bu adresin de hexadecimal biçimde gösterildiğini söyledik. Bu adresi öğrenmemiz için bir pointer tanımlamamız gerekiyor. Pointer tanımlamak için * deyimini kullanırız.

Kod:
int *p = &x; // x değişkeninin adresini tutan pointer değişkeni

Yukarıda gördüğümüz kodda & işaretini kullandık. Bu işaretin adı ampersand işaretidir. Bu işaretin iki defa yan yana kullanılması demek ve demektir. Tek başına kullanıldığı taktirde ise …’nın yeri şeklinde kısaca özetleyebiliriz. Ampersand işareti değişkenin yerini, yani değişkenin adresini belirtmektedir.

Pointer tipinde bir değişkeni ekrana yazdırmak istediğimiz taktirde placeholder (yer tutucu) kullanmamız gerekiyor. Pointerlar için kullanacağımız placeholder ise %p işaretidir.

Kod:
int x = 5;

int *p = &x;

printf("%p",p);

Bu yazdığımız kod bize x değişkeninin adresini çıktı (output) verecektir. Kodu tekrar tekrar çalıştırırsak ise farklı sonuçların geldiğini göreceğiz. Yazının giriş kısmında da belirttiğimiz üzere değişkenler ram üzerine rastgele konumlandırılırlar.

Pointer kullanarak tuttuğu adres değişkeninin içindeki değeri görmemiz mümkün müdür? Cevabımız evet. Kullandığımız p pointerı x değişkeninin yerini gösteriyor. Kullandığımız x değişkeni ise bir integer. Yani printf komutunun içine integer değerlerin yer tutucusu olan %d değerini yazarsam ne sonuç alırım? 5 değerini yazdırmasını bekleriz, değil mi?

Kod:
int x = 5;

int *p = &x;

printf("%d",p);

Bu kod parçası bize hata döndürecektir. Hatanın sebebine gelecek olursak printf fonksiyonu ile integer değer bastırmak istiyoruz. Bu yüzden %d ifadesini kullandık. Virgülden sonraki kısmın da integer bir değer olması gerekiyor. Fakat virgülden sonra p yazıyoruz. p integer bir değer değil. p aslında yeni bir değişken tipi. Integer, float, double, char, long, pointer gibi düşünün. Integer değer taşımıyor içerisinde, adres değeri taşıyor. Burada yardımımıza * ifadesi koşuyor. Bu ifade pointerın gösterdiği adresin içindeki değer anlamına gelmektedir. Pointerın gösterdiği adres dediğimiz şey aslında x değişkeninin adresidir. Yani x değişkenin adresinin içi demek istiyoruz. Yani x değeri, yani 5….

Doğru kod :
Kod:
int x = 5;

int *p = &x;

printf("%d", *p);

Mantığını daha yakından kavramak için birkaç örnek yapalım.

Kod:
int a = 10;

int *ptr = &a;

*ptr = 15;

Kod:
int b = 20;

int c = 30;

int *ptr2 = &b;

ptr2 = &c;

Yukarıda yazdığım iki kod arasındaki farkı anlayabiliniz mi ?

Birinci kod parçasında a adında değişken oluşturduk ptr pointerı a değişkeninin adresini tutuyor ve ptr pointerının tuttuğu adresin içini (*) 15 değeriyle değiştiriyoruz. Bu kavram çok önemli pointerın tuttuğu adresin içi (yani doğrudan a değişkeni değişiyor)

İkinci kod parçasına geldiğimizde ise iki farklı değişken oluşturuyoruz b ve c adında. ptr2 isimli pointerımız b değişkeninin adresini tutuyor. Sonradan ptr2 pointerını (içi değil, işaret kullanmadık) yani pointerın gösterdiği değişkeni değiştiriyoruz. Yani ptr2 pointerımız artık b değişkeninin adresini değil de c değişkeninin adresini tutuyor.

Pointer konusunu anlamak 2 işaretin anlamından ibaret aslında. Ampersand ve yıldız işareti bu konu için çok önemlidir.

Programlama dillerinde her değişkenin bir yer tuttuğunu belirtmiştik yazının başında. Tanımladığmız pointerlar da birer değişken değil midir? Yani kısacası pointerlar da hafızada bir yer tutar. Pointerın hafızada tuttuğu yeri görelim.

Kod:
int x = 5;
int *p = &x;
int *p2 = &p;

Bu kullanımı olan bir gösterim değildir. Kimse pointerın tutulduğu adresi bilmek istemez (extrem durumlar haricinde) Burada sadece mantığını anlatmak için böyle bir gösterim yaptım. Bu kod parçasında p2 pointerı da ram üzerinde random bir alana konumlanmıştır. Bu böyle sonsuz döngüye kadar gidebilir yani.

Pointer tanımlamadan da bir değişkenin adresini görmemiz mümkündür, bu kullanım integer tanımlamadan integer yazdırmaya benzer aslında.

Kod:
printf("%d",15); // integer değişken tanımlamadan 15 yazdırdık.

int x = 5;
printf("%p", &x); // Pointer tanımlamadan x in adresini gösterdik.

Gördüğümüz gibi pointer değişken oluşturmamıza gerek kalmadan x değişkeninin adresini görebiliyoruz.

Kod:
int a = 50;
printf("%d", *&a);

Şeklinde bir kullanım yaparsak ne olur? Ampersand işareti değişkenin adresi demekti ve yıldız işareti de değişkenin içi demekti.

Yani burada biz a değişkeninin adresinin içini yazdır demek istiyoruz. Burada sadece mantığını anlatmaya çalışıyorum bu kullanım ilerde görebileceğiniz bir kullanım değil. *&a yazmak ile direkt a yazmak aynı kapıya çıkıyor aslında. a yazdığımda a değişkenini bastırır. *&a yazdırdığımızda ise dolaylı yoldan (gereksiz ) a değişkenini yazdırmış oluruz.

Aşağıdaki çıktıları kendiniz bulup compile edip karşılaştırınız :
Kod:
int a = 10;
int b = 20;
int c = 30;

int *p = &a;
*p = 15;
p = &c;
*p = b;

printf("%d", a);
printf("%d", b);
printf("%d", c);


C Dilinde Stringler(Char List)

C dilinde stringler kendi uzunluklarının bir fazlası kadar yer tutarlar bellek üzerinde. Sebebi ise her stringin bir bitiş karakteri bulunmasıdır. String bittikten sonra son karakter olarak \0 karakteri bulunur. Bu sayede bilgisayar stringlerin ne zaman bittiğini anlamış oluyor. C dilinde string tanımlamak için önceden kaç haneli olacağını belirlemek zorundayız. Ona göre bellekte bize yer ayrılır ve bellekte ayırdığımız yeri programın başka yerinde değiştiremeyiz.

Kod:
char isim[10] = "Emir";

Şeklinde bir tanımlama yaptırdığımız taktirde bunun uzunluğunu değiştiremeyiz ve uzunluğunu aştığımız taktirde programı compile ederken hata alırız. Bunun sebebi bize ayrılan alanın dışına çıkmamızdır.

C dilinde string temel veri tipleri arasında değildir. Aslında stringler char veri tipinden oluşan listelerdir. String kütüphanesini dahil ederek stringleri kullanabiliriz istersek fakat orada da her ne kadar string yazsak da aslında char dizilerinin maskelenmiş halini kullanıyoruz.

Pointer Kullanarak String Tanımlama

Pointerların belli değişkenlerin adresini tuttuğunu biliyoruz. Stringlerin de karakter dizileri olduğunu söylemiştik. Yani her dizinin başlangıç adresi bulunmaktadır ram üzerinde ve char dizileri olduğu için 1 byte 1 byte şeklinde artmaktadır. Pointer kullanarak kaç haneli string gireceğimiz belirtmeden string tanımlayabiliyoruz. Pointer kullanarak bir dizi tanımlamak istediğimiz taktirde aşağıdaki kullanımı yaparız.

Pointer kullanarak string tanımlayalım.

Kod:
char *isim = "Emir";

Yukarıda isim adında bir gösterici tanımladık. Bellek üzerinde rastgele bir yer tuttu bize(biz bu yere 0x123 diyelim) Belleğimizin 0x123. adresinde ‘E’ karakteri mevcut. 0x124. adres üzerinde ‘m’ karakteri , 0x125 üzerinde ‘i karakteri, 0x125 üzerinde ise ‘r’ karakteri mevcuttur. Stringler kendi uzunluğunun 1 fazlası kadar byte kullanır demiştik. Yani 0x126 üzerinde de ‘\0’ karakteri mevcut. Şimdi bunun nasıl olduğunu görelim.

Kod:
char *isim = "Emir";
if (*(isim+4)=='\0')
{
printf("!!!!");
}

Yukarıdaki kodda pointer kullanarak bellekte yer ayırdık. char veri tipi 1 byte bildiğimiz üzere, isim+4 dediğimizde isimden 4 bytelık alan ileri gitmiş oluruz yani Emir stringindeki r den sonraki alana gitmiş oluruz. Yani stringin bitiş belirteci. Gerçekte var mı yok mu bundan emin değiliz ve if conditionun içinde printf ile bir kanıt göstermesini istiyoruz. Bu kodu derleyicinize yazıp derlediğiniz zaman !!!! çıktısını alacaksınız.

Pointer ile Integer Listesi tanımlayalım.

Stringler bir liste demiştik, o halde listeleri de pointer yardımı ile tanımlayabiliriz. Pointer ile liste tanımlamak için int *dizi şeklinde bir kullanım yapıyoruz.

Kod:
int *dizi = {1,2,3};
int dizi2[3];
dizi2[0] = 1;

Yukarıdaki kodda iki kullanımı birden gösterdim. Şimdi integer listesi(array) oluşturarak char pointer ile integer listesinin elemanlarına müdahale etmeyi deneyelim.

Kod:
int dizi[4];
dizi[4] = 10;
char *p = dizi;
*(p+12) = 150;

Yukarıdaki kodda dizi tanımlıyoruz. Dizinin 5. elemanını pointer yardımı ile değiştiriyoruz. Evet güzel ama bu nasıl oluyor diyebilirsiniz. Integer veri tipi 4 byte bildiğimiz üzere, char değişken tipi de 1 byte. Integer dizi oluşturduğumuzda ram üzerinde 4 byte 4 byte şeklinde 6 tane kutu oluşturur. Yani toplamda 24 bytelık bir alan oluşur belleğimizde. Sonuncu elemana char pointerı ile erişmek için 4’er 4’er atlamamız gerekiyor çünkü char 1 byte. Integer bir değeri Char bir pointerla göstermeye çalışıyoruz. Yani 6 kutu değil de 24 kutu gibi düşünelim. 20. kutuya geldiğimizde Integer listeye göre son kutunun başına gelmiş oluyoruz yani dizi[5] elemanına denk geliyor ve pointer yardımıyla değiştiriyoruz. İnanmazsanız kodu çalıştırın :)

9BW1U2.jpg



Fotoğrafta görüldüğü üzere adres 4-4 şeklinde artıyor.

Char pointer tanımlarken dikkat etmemiz gereken nokta ise char pointerları 1 byte kadar veri depolayabilirler. 1 byte dediğimiz şey 8 bitin(binary digit) bir araya gelmesiyle oluşur. Yani toplamda 2^8 ihtimal mevcuttur. Bir byte’ın maksimum alacağı değer 255 olmalıdır(0 ı da dahil ediyoruz toplamda 256 değer) Biz dizi[5] elemanı pointer yardımıyla 255 den büyük bir değere eşitlemek istediğimiz taktirde mod 256’ya göre sonuç verecektir. Hep beraber görelim.

Kod:
int dizi[4];
dizi[3] = 10;
printf("%d\n", dizi[3]);
char *p = dizi;
*(p + 12) = 256;
printf("%i\n", dizi[3]);
short *p2 = dizi;
*(p2+6) = 25;
printf("%i", dizi[3]);

Dizinin 3. elemanını bastırmak istediğimiz taktirde 0 çıktısını veriyor. mod 256’ya göre 256 0’a eşittir çünkü :)

Ayrıca yukarıdaki kodda short kullanılmış, short 2 byte olduğu için + 12 değil de + 6 değerini kullandık. Short atlaya atlaya 6 adım git gibi düşünebiliriz. short * 6 yani 2*6 = 12 Aynı kapıya çıkıyor gördüğümüz gibi sonuç.



Struct (Yapı) Kavramı

Struct dediğimiz kavram veri yapılarıdır. C dilinde temel olarak int, float, double, char veri tipleri olduğunu söylemiştik. Bunlar tabiki bizlere yetmeyecektir. Kullanıcıdan tarih almak istediğimizde daha gelişmiş veri yapısı oluşturmamız gerekecektir. Diğer programlama dillerindeki Sınıf yapısına benzetebilirsiniz bu yapıları.

Kod:
struct cyber{
int a;
char b;
float c;
}

Yukarıda cyber adında bir yapı tanımladım. Bu yapının 3 tane elemanı var farklı veri tiplerinden. Bu yapıdan bir değişken oluşturalım.

Kod:
struct cyber a;
a.a = 5;
a.b = 'a';
a.c = 5.12;

Bu oluşturduğum yapıdan bir değişken oluşturup tüm elemanlarına eleman atadım.

Daha açıklayıcı olması açısından bir de tarih yapısı oluşturalım

Kod:
struct tarih{
int ay;
int gun;
int yil;
}

Tarih yapısını oluşturduk, atamalarımızı yapalım.

Kod:
struct tarih doğumGunum;
doğumGunum.ay = 10;
doğumGunum.gun = 10;
doğumGunum.yil = 1999;

Gördüğünüz gibi tarih biçiminde bir veri istersek bu veriyi böyle karışıklık olmadan tutabiliyoruz bellek üzerinde.

Typedef ile de tanımlama yapabiliriz, ki bu daha çok tercih edilir. Çünkü syntax olarak daha basittir.

Kod:
typedef unsigned long long ULL;

ULL a;

Gördüğünüz gibi Unsigned long long tipini typedef ile kendim tekrardan tanımladım. Kullanım kolaylığı sağladı, bunu structlar için de yapabiliyoruz.

Kod:
typedef struct a{
char a;
}

Kod:
a degisken_adi;




C Dilinde Fonksiyonlar

Fonksiyonlar o program üzerinde bir işlemi birden fazla yapacağımız taktirde, aynı algoritmayı tekrar tekrar yazmamız zor olacağı için ortaya çıkmış kavramlardır. Fonksiyon yapısı içine değer veya değerler alabilir, bu değerlere göre belirli algoritmaya göre işlem yaparak geriye bir değer döndürebilirler. Fonksiyon bildirimi yaparken hangi türden değer döndüreceğini en başta yazmamız gerekmektedir. Değer döndürmeyecekse(içerde çıktı yazacaksa veya swap yapacaksa) **** tipinde bir fonksiyon tanımlarız. **** kelimesinin türkçesi boş demektir zaten. Gelin bir fonksiyon tanımlayalım.

Kod:
**** myFunc()
{
printf("Ben bir fonksiyonum!!");
}

Burada bir fonksiyon tanımlaması yaptık. Fonksiyon tanımlaması fonksiyon çağrısının üstünde yapılırsa fonksiyon prototipini tanımlamamız gerekir.

Neden böyle bir şey yapmalıyız?
Çünkü derleyici kodları yukarıdan aşağıya doğru okuyacaktır kodları. Sırayla gidecektir. myFunc() satırına geldiğinde daha önce o tanımlamayı görmediği için "ben seni tanımıyorum" diyecektir. Bu yüzden prototip tanımlarız. Genelde önişlemci direktiflerinin hemen altında tanımlanır prototip. Örneğine bakalım.

Kod:
#include <stdio.h>
**** myFunc();

Derleyici bu prototipi görünce satır atlayacak ve fonksiyonu tanımladığımız satıra dönecektir. Tanımı yapıp tekrar olduğu yere dönecektir.

Fonksiyona parametre vermek ?

Fonksiyon belirli algoritmalarda işlem yapabilmek için bizden belli sayıları isteyebilir. Mesela toplama fonksiyonu yazıyoruz diyelim. a+b yi toplayacak fakat a ve b değerlerimiz neler, bu değerler spesifik olmak zorundadır dinamik bir yapı oluşturabilmek için. 5 ile 3 ü toplamak istiyoruz. 5 değerimiz a yerine 3 değerimiz b yerine geçecektir.

Kod:
int topla(int a, int b)
{
return a+b;
}

Şimdi yukarıda yazdığım kodda 2 farklı sayıyı topluyoruz. Gelelim bu kavramı açıklamaya, burada bir return sözcüğü görüyoruz.

Ne işe yarıyor ve neden kullanıyoruz ?

Biz fonksiyonu çağırdığımız zaman ram üzerinde geçici değişkenler oluşturuyoruz. (int a, int b) dediğimiz kısımda a ve b değişkenleri fonksiyonun çalışma zamanında oluşuyor. Fonksiyon bitince değişkenler siliniyor. Program o değişkeni tanımıyor (silinmemesi için değişkenin başına static diyerek static değişken oluşturabiliriz. Bu ayrı bir konu ) Fonksiyondan sonraki satırda printf ile a yı yazdırmaya kalkarsak derleyici ben a yı tanımıyorum diyerek compile time error verecektir. Bunun için return kavramı kullanılır, geriye bir şey döndür demektir.

Yani kısaca sen benim oluşturduğum değişkenleri siliyorsun kardeşim, benim elimde ne kaldı 5 ile 3 ü topladım bu değeri nasıl kullanacağım sorununa çözüm olarak return değerimiz fonksiyonun çağrıldığı adrese o değerini döner. Örnek üzerinde anlatalım. 5 ile 3 ü topladık a ve b değişkenleri silindi artık yoklar. topla(3, 5) şeklinde çağırdığımız fonksiyonu bir değişkene atayabiliriz veya direk printf yardımı ile yazdırabiliriz.

Kod:
int main()
{
int c;
c = topla(a, b);

}

int topla(int a, int b)
{
return a+b;
}

Yukarıdaki kodda c değişkenimin içindeki değer fonksiyondan geri dönen değer olacaktır. İşte bu sebepten dolayı return kullanırız.


Call By Value (Değer ile Çağırma)

Call by value yani parametre olarak değer aldığımız fonksiyonlarda değişken oluşturulur, fonksiyon biter ve değişken silinir. Bu durumda x değerim 4 olsun y değerim 7 olsun. Ben bu değişkenleri birbiri ile değiştirmek istersem ( x=7 y=4 ) call by value yöntemi ile çağırırsam hatalı olur.

Neden hatalı olur ?

Çünkü fonksiyona gönderdiğim parametreler içerde kendi ayrı değişkenleri oluşturur, kendi içerinde değiştirir ve bu değişkenleri bellekten siler. Geriye ne kaldı elde. Hiçbir şey....

Örnek Fonksiyon

Kod:
int main()
{
int x = 4, y=7;
swap(x,y);

}
int swap(int x, int y)
{
int tmp;
tmp = x;
x = y;
y = tmp;
}

Yukarıda yazdığım kod değerleri değiştirmeyecektir. Çünkü fonksiyon parametresinde yazdığım (int x, int y) kısmı sadece bir gösterimden ibarettir. Aynı değişken isimlerini yazsak bile ayrı bir değişken oluşturup sonra hafızadan salacaktır bu değişkenleri. Bu yüzden call by reference dediğimiz yönteme başvurmalıyız bu tip problemlerimiz için.

Call By Reference (Referans ile Çağırma)

Referans ile çağırma diyince kelime olarak pek bir anlam ifade etmiyor olabilir. Pointer kullanarak çağırma dersek aklınızda daha net canlacaktır. Biz fonksiyona parametre olarak adres verirsek fonksiyon kendi içerisinde adres oluşturamayacağı için o adrese gider. O adres üzerinde işlem yapar. O adres ise bizim x ve y değerlerimiz, kısaca swap işlemini başarı ile yapmış olur.

Kod:
int main()
{
int x = 4, y=7;
swap(&x, &y);

}
int swap(int *x, int *y)
{
int tmp;
tmp = *x;
*x = *y;
*y = tmp;
}

Adresle işlem yaptığımız için ampersand işaretini (&) koymayı unutmamamız gerekmektedir. Dizilerde işlem yaparken call by reference kullanmaktayız. Çünkü diziler bir adres belirtir pointer oldukları için int dizi[10] diye tanımlayalım. printf ile dizi yazdırmayı denersek bu bize dizinin ilk indeksinin adresini dönmektedir. Pointerlarda yapılabilen bütün işlemler dizilerde de yapılabilir.



Recursive (Özyinelemeli) Fonksiyon

Bu tip fonksiyonlar kendi kendini çağıran fonksiyonlardır. Bu tip fonksiyonları kullanmak çok risklidir, eğer fonksiyon döngüsünden çıkmayı başaramazsak stack alanı dolar ve stackoverflow hatası(segmentation fault:11) alabiliriz. Dikkatli kullanılması gerekmektedir.

Kod:
#include <stdio.h>
int asalMi(int sayi, int i);
int main(int argc, char const *argv[])
{
	int n, kontrol;
	printf("sayi giriniz\n");
	scanf("%d", &n);
	for (int i = 2; i < n; ++i)
	{
		kontrol = asalMi(i, i/2);
		if (kontrol == 1)
		{
			printf("%d\n", i);
		}
	}
	return 0;
}

int asalMi(int sayi, int i)
{
	if (i == 1)
	{
		return 1;
	}
	else if (sayi % i == 0)
	{
		return 0;
	}
	else
	{
		return asalMi(sayi, i-1);
	}
}

Yukarıda yazdığım kodda asal sayı bulmamızı sağlayan rekürsif fonksiyon örneğini yaptım. Dikkat ederseniz fonksiyonu yeniden çağırma işlemim bir koşul bloğunun altında. Böyle bir kullanım yapmak fonksiyonun sonsuza dek(stack alanı dolana dek) kendini çağırmasını engelleyecektir.

Bu konu çok karıştırılan örnek olduğu için bir örnek daha paylaşmak istiyorum.

Kod:
#include <stdio.h>
#include <stdlib.h>
int fibonacci(int n);
int main(int argc, char const *argv[])
{
  system("clear");
  int x;
  printf("kaç basamaklı fibonacci olsun : ");
  scanf("%d", &x);
  printf("\nFibonacci dizisi :\n\n");
  for (int i = 0; i < x; ++i)
  {
    printf("%4d", fibonacci(i));
  }
  printf("\n\n\n");


  return 0;
}

int fibonacci(int n)
{
  if (n==0)
  {
    return 0;
  }
  else if(n==1)
  {
    return 1;
  }
  else
  {
    return (fibonacci(n-2) + fibonacci(n-1));
  }
}

Bu kod da kullanıcının girdiği basamakta Fibonacci dizisi basmaya yarar, fark ettiyseniz yine fonksiyonu yeniden çağırma noktamız bir koşulun altında.





 
Moderatör tarafında düzenlendi:

CyberXhackk

Kıdemli Üye
13 Mar 2016
3,132
10
C/C++ Dev.
cyber3



Dinamik Bellek Yönetimi

Dinamik bellek yönetimi dediğimiz kavram nedir önce ondan bahsedelim. C dilinde sınıflar yoktur, her işlemi programcının kendisinin yapmasını bekleriz. En basitinden örnek verecek olursak 5 elemanlı bir dizi tanımlarsak ve programın ortasında bunun yetmeyeceğini anlayıp 10 a çıkarmak istersek ne yapacağız? Şu anki bilgimize dayanarak şöyle bir çözüm üretebiliriz. 5 elemanlı diziyi çöpe atarız, 10 elemanlı dizi oluşturur for döngüsü ile dizinin her elemanını yeni dizimize atarız. Bellek işgal etmiş oluruz. Bu mantıklı bir çözüm değildir, daha dinamik çözümler bulmamız gerekmektedir. Konuya girmeden önce stack ve heap kavramından bahsedeyim.

Stack Nedir?

Stack programın bellekteki çalışma alanıdır. Biz programı çalıştırdığımızda ram üzerinde işletim sistemi bize programın çalışma alanı için bir bölge tahsis eder. Bu bölgeyi statik olarak kullanabiliriz. Değişme ihtimali olmayan değişkenleri kullanırız burada. Stacki bir liste şeklinde düşünelim. İlk giren son çıkar mantığı ile çalışır. Sıralı bir şekilde oluşturulur değişkenlerimiz. Örnek üzerinde gösterelim.

Kod:
#include <stdio.h>

int main()
{
	int x = 4;
	int dizi[5] = {1,2,3,4,5};
}

Yukarıdaki koddaki iki değişkenim de stack alanında depolanır. Derleme esnasında stack alanında ne kadar yer tutacakları hesaplanır. Dizi üzerinden ele alalım. Derleme esnasında bakar derleyici dizi 5 elemanlı olacak der. Olay orada bitti biz bu diziyi 5 elemanlı tanımladık, daha fazlasını kullanamayız.



Heap Nedir?

Heap alanı programın çalışma alanının dışındadır. Buradan yer tahsil etmeye çalışırken işletim sistemini veya başka bir programın kullandığı bellek alanına denk gelme ihtimalimiz mevcuttur. Eğer bu adreslerden birine denk gelirsek NULL adres döner.

Nedir bu NULL adres ?

Heap üzerinden 5 bytelık bellek istedim işletim sisteminden. Eğer işletim sisteminin kullandığı bir alansa o alanı veremez bana, ben kullanıyorum kardeşim der. Eğer uygunsa verebilir ancak. Programcılar bu soruna bir çözüm getirmek amaçlı bilgisayar belleklerinin 0. baytını NULL değer, yani yasak bölge olarak atamışlar. Yani işletim sisteminin kullandığı alana denk geldiysek bizi 0. byte'a yönlendiriyor. Yasak bölge olduğu için giremiyoruz, ve bellek alanı tahsil edilememiş oluyor.

Heap alanını kullandıktan sonra bu alanı işletim sistemine geri iade etmemiz gerekmektedir. Geri iade etmezsek eğer bu alan kullanılmaya devam ederek bellek işgaline yol açacaktır. Bu da memory leak dediğimiz bellek sızıntısı kavramını beraberinde getirmektedir. Bir oyunu veya programı uzun süre kullandığınızda programda yavaşlama oluyorsa o programda Dinamik Bellek Yönetimi kısmıyla ilgilenen programcı eksik kodlama yapmıştır. Kullanıp işi bittiği alanları işletim sistemine geri iade etmemiştir, zamanla bellek şişmiştir ve oyun/program yavaşlamaya başlamıştır. Diğer dillerin aksine C dilinde Garbage Collector yoktur.

Nedir bu Garbage Collector ?

Türkçe ismi ile çağıracak olursak çöp toplama mekanizmasıdır. Heap alanında bir bellek alanını tahsil ettikten sonra o alan ile işimiz bittiğinde otomatik olarak işletim sistemine iade edilmesini sağlayan mekanizmadır. C dilinde bu özellik mevcut değildir. Programcı tarafından yapılması gerekmektedir.


Stack Özellikleri

* Heap alanına göre daha hızlıdır.
* Oluşturulan değişkenler otomatik olarak işletim sistemine iade edilir.
* Dinamik değildir.
* Compile time(derleme zamanı) boyutlar hesaplanır.

Heap Özellikleri

* Stack alanına göre daha yavaştır.
* C dilinde garbage collector olmadığı için programcının iade işlemini yapması beklenir.
* Dinamiktir.
* Run time(çalışma zamanı) belirtilen bellek alanı hesaplanıp ayrılır.


Stack üzerinde primitive veri tipleri tutulurken, heap üzerinde referans veri tipleri bulunur. Referans veri tipini işaret eden referans ise yine stack üzerinde tutulmaktadır.

RxfBOL.png


Fotoğrafta görüldüğü üzere sayı değerimizin üzerinde referans değeri (adres değeri) tutulmakta. Belirtilen adres ise heap alanı üzerinde tutulmaktadır.



malloc() Fonksiyonu

Malloc fonksiyonu heap alanından bellek alanı tahsil etmek için kullanılmaktadır. Tek parametre almaktadır. Kullanımı örnekteki gibidir.

Kod:
int *p = malloc(5*4);

Yukarıdaki kullanım her ne kadar doğru olsa da kullanım açısından çok tavsiye edilmez. Burada 5 integerlık bir dizi için alan ayırmış olduk, fakat integerın boyutu sistemden sisteme değişebileceği için böyle bir kullanım yapmalıydık.

Kod:
int *p = malloc(5*sizeof(int));

Bazı kaynaklarda malloc fonksiyonu kullanılmadan önce integer pointer'a convert ediliyor, biz bunu yapmasak bile int *p dediğimiz için otomatik olarak integer pointera convert edilecektir zaten. Yani demek istediğim;

Kod:
int *p = (int*)malloc(5*(sizeof(int));
Kod:
int *p = malloc(5*sizeof(int));

Yukarıdaki iki kod aynıdır. Malloc fonksiyonu bize 20 byte kadar heap alanı üzerinde bellek tahsil eder. Kelime anlamı ise memory allocate'den gelmektedir. Türkçe anlamı ise hafıza ayırma demektir.



calloc() Fonksiyonu

Calloc fonksiyonu malloc fonksiyonuna çok benzemektedir. Tek farkı ise o belleği temizleyerek bize verir. Yani malloc fonksiyonuna göre daha yavaş çalışır. Temizlemekten kastımız nedir ? Tüm değerlerin içine 0 değerini koyar, malloc ile alan ayırdığımızda ise çöp değerler mevcuttur.

Kod:
int *p = calloc(5,sizeof(int));

İkinci bir fark olarak bu fonksiyon 2 parametre alır. Malloc fonksiyonunda 5*sizeof(int) derken burada 5, sizeof(int) dememiz gerekmektedir.


realloc() Fonksiyonu

realloc() fonksiyonu ise belleğin yetmeme durumunda yeniden alan ayırmak için yapılmaktadır. 2 farklı parametre alır. 1. parametre adres ister, ikinci parametre ise yeni boyutu ister.

Kod:
int *p = malloc(5*sizeof(int));

Malloc ile alan ayırdık ve bu alan bize yetmediyse;

Kod:
int *p2 = realloc(p, 10*sizeof(int));
Şeklinde bu alanı 40 byte'a çıkarıyoruz.


free() Fonksiyonu

Bölümün başında memory leak kavramından bahsetmiştim. Bu kavramın oluşma sebebi bu fonksiyonun eksik veya yanlış kullanılmasından kaynaklanmaktadır. Bizim bir bellek alanı ile işimiz bittiği taktirde o bellek alanını işletim sistemine geri iade etmeliyiz. Bunun sebebi ise C dilinde garbage collector mekanizması bulunmamaktadır. Bellek alanını geri işletim sistemine iade etmemizi sağlayan fonksiyon ise free() fonksiyonudur.

Kod:
free(p);
Yukarıda p göstergesiyle işimiz bittiğinden dolayı işletim sistemine geri iade ettik.


Expression(ifade) Kavramı

Expression kavramı programlama dilinin temelinde yatan bir kavramdır. Türkçe tanımıyla ifade demektir. Lvalue expression (sol taraf ifadesi) ve Rvalue expression(sağ taraf ifadesi) olmak üzere 2 farklı dala ayrılır.

L-Value Expression

Programlamada eşittir ifadesinin atama operatörü olduğunu biliyoruz. Anlamı ise sağ taraftaki değeri sol tarafa ata demektir. Sol taraftaki değer her zaman L-value expression olmak zorundadır. L-value expression kısaca bir değişken adıdır. Sabit ifade veya operatör yer alamaz (a+5 gibi).

Örnek olarak;

a+c bir lvalue expression değildir. Yalnız başına a veya yalnız başına c lvalue expression olarak sayılabilir.



R-Value Expression

Sağ taraf ifadesi ise her şey olabilir. Çünkü sağ taraf sol tarafa atanır kuralı olduğunu söylemiştik. Sadece sabit değerler de birer R-value expressiondur.

Örnek olarak;

15, a+b, a, 15/5 gibi çokça örnek sayılabilir.



Union Yapısı

Union yapısı struct yapısına çok benzeyen bir yapıdır. Soket haberleşmesinde kullanılır. Gelen paketleri kolay biçimde parçalamak için kullanılır. Tanımlaması aynı struct gibidir. Fakat boyutu içerisine aldığı en büyük veri tipinin boyutu olarak baz alınır. İçerisindeki tüm değişkenler bu alanı ortak kullanır.

Kod:
union{
int a;
char a,b,c,d;
}myUnion;

Yukarıdaki kodda en büyük alan kaplayan veri tipi integer olduğundan dolayı myUnion isimli veri yapımızın boyutu 4 byte olmaktadır.

S21GKL.png


Yukarıdaki görselde çok rahat anlayabilirsiniz union'un çalışma mantığını.



Pointer Struct Tanımlama


Struct oluşturup bu oluşturduğumuz veri tipinden de pointer tanımlayabiliyoruz. Tek farkı elemanlarına erişirken nokta(.) değil de ok(->) işareti kullanıyoruz. Örnek üzerinde gösterirsem eminim çok iyi anlayacaksınızdır.

LKIIW8.png


Programın Çıktısı :

WS523R.png


Programın Çalışma Mantığı :

Oluşturduğumu struct x yapısı 4 bytelık alan kaplıyor. Toplamda dört tane char değişken olduğu için. Pointer olarak kullanmaya çalışıyoruz bu yapıyı. Yine dört byte kaplayan integer değeri gösteriyoruz bu pointer ile. İnteger değerimiz kaç 1028.
Bu değerimizi onaltılık tabanda yazacak olursak;

0x00000404 (Adres değerleri 64 bit işlemcide 8 byte yani 64 bit adreslenir. O sıfırları ona göre koyduk)
Bu sayıya eşit olacaktır.

Char 1 byte gösterdiği için a değeri için 1 byte alan yazacağız bu da 0 ve 4 olacak. Çünkü bu sayımız hexadecimal tabanlı. Burayı anlamadıysanız matematikteki sayı tabanları konusuna bakmanızı tavsiye ederim.

Kısaca:
a = 04
b = 04
c = 00
d = 00

Random Değer Üretme

Aslında bilgisayarlar gerçek random değerler üretemez. Belli bir algoritması vardır. Bu da beraberinde sorunlar getirmektedir. Bu algoritma belli bir sayıdan sonra sürekli aynı değerleri üretmeye başlar. Bu algoritmanın bir seek(kök) değeri bulunmaktadır. Biz bunu değiştirmezsek eğer programı derledikten sonra 2. çağırışımızda da aynı değerleri ürettiğini göreceğiz. Buna çözüm olarak srand() seekrandom fonksiyonu ile her açılışta seek değerini değiştirmemiz gerekiyor. İyi güzel ben bu srand() ın içerisine de zaman değerini verirsem eğer program her açıldığında random değer üretmiş olabileceğim anlamına geliyor. Zaman sürekli değişiyor çünkü. Srand() fonksiyonunun mantığını anladığınızı düşünüyorum. Bu fonksiyona parametre olarak srand(time(0)) veya srand(Clock()) parametrelerini verebilirsiniz. Time 0 saniyeye bakar. Clock ise saniseyi baz alır.

Seek değerini her açılışta değiştirdiysek artık rand() fonksiyonumuza geçebiliriz. Rand fonksiyonu çok büyük değerler üretir. Bizim kendi elimizle mod almamız gerekmektedir. Matematiksel olarak kuralı şu şekildedir;

min-max kapalı aralığında sayı üretmek için

rand() % (max-min+1) + min

işlemini uygulamamız gerekmektedir. Clock veya Time fonksiyonunu kullanabilmek için time.h isimli başlık dosyasını kodumuza dahil etmemiz gerekmektedir.

Kod:
int main()
{
srand(clock());
int dizi[10];
for(int i = 0;i<10;++i)
    {
        dizi[i] = rand() % (10-0+1) +0;
    }
}

Yukarıdaki kod 0-10 aralığında, 10 dahil olmak üzere 10 adet sayı üretip dizi adındaki diziye göndermektedir.


C Programlama Trickleri

Konumun son bölümünde detay bilgi diyebileceğim, mülakatlarda karşınıza gelme ihtimali olan, çoğu yerlerde göremeyeceğiniz ufak tricklerden bahsedeceğim.

1) Döngü veya karar yapısının içerisinde mantık operatörü değil de atama operatörü kullanırsak atama operatörünün R-Value değeri geri dönecektir.

Örnek:

6BSAfa.png



2) Bir işlemde en son yapılacak operatör atama operatörüdür. Bu çok önemlidir, yanıltıcı sorular gelebilir.

işte size yanıltıcı soru:

e7yJW9.png


3) Bitsel ve ile normal ve operatörü karıştırılmamalı ! Bitsel ve birinci yanlış olsa bile 2. kısma bakar. Normal ve bakmaz esgeçer.

P0J0RI.png


4)++ ve -- kullanımı x += 1'e eşit olduğundan dolayı sayı üzerinde yapılamaz.

MLNdef.png


5) ^işareti EXOR bağlacıdır. Bitsel işlem yapar. Aynıysa 0 farklıysa 1 döner.

277LPd.png


6)Printf fonksiyonu aynı zamanda kendi içine aldığı karakter sayısından 1 fazlasını return eder.
277LPd.png


7)Dizilere pointer operatörü ile pointerlara dizi indeki ile( [] )erişebiliriz.

xMBKNK.png


8)Tek satırda if kullanımı (?)Operatörü

KbxMML.png


9)Önişlemciye verdiğimiz define değişkenler üzerinde işlem yapılmaz. Copy Paste görevi görür.

V6MeA8.png



 
Moderatör tarafında düzenlendi:

DarkHawk06

Uzman üye
12 Şub 2016
1,046
8
Pandora
Elinize emeğinize sağlık gayet uzun, anlaşılır ve emek verilen bir konu olmuş. Saygı çerçevesinde yazılan yorumları okumaya geldim. :bilgili
Not: Yazılmıcı değilim.

 

Phoique 7

Katılımcı Üye
14 Mar 2017
505
1
Manisa
Ben 2 yıldır nodejs ve react ile uğraşıyorum hala daha vay be bunu nasıl öğrenmemişim diyorken kalkıpta bir konuda C öğrenmek imkansızdır. Keşke bunun yerine C de birkaç örnek soru çözseydin emin ol daha yararlıdır.
 
Moderatör tarafında düzenlendi:

CyberXhackk

Kıdemli Üye
13 Mar 2016
3,132
10
C/C++ Dev.
Ne içeriğine baktım ne de okudum. Aşırı gereksiz bir konu olduğunu düşünüyorum. Ben 2 yıldır nodejs ve react ile uğraşıyorum hala daha vay be bunu nasıl öğrenmemişim diyorken kalkıpta bir konuda C öğrenmek imkansızdır. Keşke bunun yerine C de birkaç örnek soru çözseydin emin ol daha yararlıdır.

Burada temel kavramlardan bahsettim C üzerindeki, içinde türkçe kaynak bulunmayan konulara değindim. Kimse tek bir konuda C öğretemez. Konumun içeriği CSD C ve sistem programcıları derneğinin sunduğu 3000 tl piyasa değeri olan kursa göre hazırlandı. Bu içeriklerin çoğunu bulabilirsin, ben temelin detay kısmına değindim. Konuda C expert olacaksın demiyorum zaten :D

Ayrıca soru çözmek istiyorsan;

https://projecteuler.net/archives
https://www.hackerrank.com

gibi sitelerde örnekler tonlarca var, onlardan daha iyi örnek çözemem de anlatamam da. Bu konunun amacı o örnekleri çözebilecek seviyeye gelmek. Yeni gelen birine generic dinamik dizi implementasyonunu anlatsam ne anlayacaktır bu konudan?
 

Anonim6

Yeni üye
29 Şub 2012
0
5
emeğine sağlık da başlığı "[A'dan A'ya] C Programlama Dilini Öğrenelim!" olarak düzeltirsen sevinirim.
 

CyberXhackk

Kıdemli Üye
13 Mar 2016
3,132
10
C/C++ Dev.
emeğine sağlık da başlığı "[A'dan A'ya] C Programlama Dilini Öğrenelim!" olarak düzeltirsen sevinirim.

Teşekkür ederim. Başlıktan kasıt A'dan Z'ye C programlama Dilinin Temellerini öğrenmek aslında. A'dan Z'ye C programlama dilini öğretmeye değil öğrenmeye kalksak bile ömürden ömür gider, Çok dallanan bir yapı C programlama dili. Bunu konu olarak veya video olarak anlatmak mümkün değil.

Programla dilleri eğitiminde amaç öğrenciye A'yı verip B ve C'yi öğrencinin kendi kendine araştırarak anlayacak şekle getirmektir. Ayrıca konunun sonuna, C programlama trickleri bölümüne bir kaç tane daha mülakat sorusu, detay konu tarzında ekleme yapacağım. Totalde 20-25 adet olacak onlar. Karşıma çıktıkça ekliyorum.
 
Üst

Turkhackteam.org internet sitesi 5651 sayılı kanun’un 2. maddesinin 1. fıkrasının m) bendi ile aynı kanunun 5. maddesi kapsamında "Yer Sağlayıcı" konumundadır. İçerikler ön onay olmaksızın tamamen kullanıcılar tarafından oluşturulmaktadır. Turkhackteam.org; Yer sağlayıcı olarak, kullanıcılar tarafından oluşturulan içeriği ya da hukuka aykırı paylaşımı kontrol etmekle ya da araştırmakla yükümlü değildir. Türkhackteam saldırı timleri Türk sitelerine hiçbir zararlı faaliyette bulunmaz. Türkhackteam üyelerinin yaptığı bireysel hack faaliyetlerinden Türkhackteam sorumlu değildir. Sitelerinize Türkhackteam ismi kullanılarak hack faaliyetinde bulunulursa, site-sunucu erişim loglarından bu faaliyeti gerçekleştiren ip adresini tespit edip diğer kanıtlarla birlikte savcılığa suç duyurusunda bulununuz.