Bilgisayar Hafızasını Fethet 2: ASCII Karakterler ve Diziler

Mehmed Fatih Bayraktar
5 min readAug 2, 2023

--

Bilgisayar hafızasını karakter karakter fethetmeye devam ediyoruz. Karakterler, makine ile insan arasında iletişimi sağlayan en genel dijital arayüz elemanlarıdır. Şu an farklı diller ve coğrafyalar için pek çok karakter seti olmakla beraber, ilk ortaya konulan ve en yaygın kullanılan standart karakter seti ASCII karakter setidir.

Green binary code over black screen

İlk yazımda isalpha fonksiyonunu incelerken ASCII karakterlerden bahsetmiştik. ASCII karakterler üzerinde çalışan bir kaç fonksiyon örneği daha vereceğim. Fonksiyonların detaylı incelemelerini size bırakarak sadece ne işe yaradıklarını yazacağım. Sıradaki fonksiyonumuz isdigit.

int isdigit(int c);

Hatırlarsak isalpha fonksiyonu c ondalık tam sayı değerinin ASCII tablosundaki büyük veya küçük harflerin ondalık karşılıklarına denk gelip gelmediğini söylüyordu. Aynı şekilde isdigit fonksiyonu da 0–9 arası bir rakam olup olmadığını söyler. Ondalık karşılıklarına göre şu kontrolü yapar:

...(c >= 48 && c <= 57)
...

Bir diğer fonksiyon isalnum ise isminden anlaşılacağı gibi isalpha(c) veya isdigit(c) fonksiyonlarının ikisinden de sıfır değeri dönerse sıfır, aksi takdirde sıfırdan farklı bir değer döndürür. ASCII tablosundaki 128 karakterin tamamını kontrol etmek için ise isascii fonksiyonu kullanılabilir. Son olarak bir de isprint fonksiyonundan bahsetmek istiyorum. Bu fonksiyon c değerinin printable (yazdırılabilir) bir karakter olup olmadığını kontrol eder. Yazdırılabilir karakterler ASCII tablosundaki 32–126 ondalık değer aralığına denk gelen karakterlerdir. Bunlar dışındaki karakterlere de kontrol karakterleri denilir.

int isalnum(int c);
int isascii(int c);
int isprint(int c);
int toupper(int c);
int tolower(int c);

ASCII ile ilgili iki fonksiyondan daha bahsederek bu konuyu bitirebiliriz. Şimdiye kadarki fonksiyonlarla bir karakter değeri hakkında bilgi alıyorduk. Son iki fonksiyon ise bir karakter büyük veya küçük bir harf ise onu değiştirir. Küçük harfi büyük harfe dönüştürmek için toupper, büyük harfi küçük harfe dönüştürmek için ise tolower fonksiyonunu kullanırız. Bu durumlar dışında bu fonksiyonlar verilen karakteri değiştirmeden geri döndürürler. Aşağıda bir tanesini kendimiz nasıl yazabiliriz örnek olarak bırakıyorum. Diğeri de benzer şekilde yazılabilir.

int ft_toupper(int c) {
if (c >= 97 && c <= 122)
return (c - 32);
else
return (c);
}

Karakter Dizileri ve Hafıza: memset

Dizi (array), birden fazla değeri bir liste gibi kaydetmemizi ve daha sonra erişmemizi sağlayan temel bir veri yapısıdır. Matematikteki vektör, matris veya daha yüksek boyutlu yapılara benzerler. C programlama dilinde metin (string) şeklindeki veriler birer karakter dizisi şeklinde kullanılırlar. Yani aşağıda tanımlanan str1 ve str2 dizileri eşdeğerdir.

char *str1 = "Hello";
char str2[6] = {'H', 'e', 'l', 'l', 'o', 0};

Dizilerin veri tipi olan char ifadesi dizinin her elemanın 1 baytlık bir karakter olacağını söylüyor. İkinci satırdaki str2 dizisinin 6 karakterlik bir dizi olacağını söylüyoruz. Sonra bu dizinin değerine sıfırıncı indeksinden başlayarak dördüncü indeksine kadar ‘Hello’ kelimesinin harflerini ve beşinci indeksine ASCII tablosunda ondalık değeri 0 (sıfır) olan NULL karakterini atıyoruz. Bir karakter dizisinin string (metin) olabilmesi için NULL karakter ile sonlandırılması gerekir.

“Hello” ifadesi tüm bu işlemleri ifade etmenin kısa yoludur. Yalnız ikinci ifade önce bir hafıza adresini str2 ismiyle ilişkilendirip o adresten başlayarak altı baytlık yer ayırır; sonra bu yere karakter değerlerini atar. İlk ifade ise str1 diye isimlendirme yapmadan önce hafızada bir adres belirler ve aynı şekilde karakter değerlerini o adresten başlayarak yazar. Daha sonra o adresi str1 ismiyle ilişkilendirir. İlkinde dizinin hafızada kaç karakter kaplayacağı sonradan belirlenirken, ikincisinde önce ne kadar alan alacağını veriyoruz. Sonuçta yapılan şey şudur: str1 ve str2 değişkenlerine değer olarak bir karakter dizisinin hafızadaki yerinin başladığı adres atanır.

İçine adres atanan değişkenlere pointer (işaretçi) denir. Dizi tanımlanırken değişken isminin önündeki * operatörü o değişkenin böyle bir işaretçi olarak kullanılacağı anlamına gelir. Artık str1 değişkeninde değer olarak hafızadaki bir adres bulunmaktadır. O adrese gittiğimizde ise char tipinde bir değer olan H harfini buluruz. Peki str1 değişkeni içinde hafızadaki bir adres tutuluyorsa bu adresteki değere nasıl ulaşacağız? Yine * operatörü ile:

...
return str1; // bir adres döndürür.
...
return *str1; // 'H' karakterini döndürür.
...
return str1[0] != *str2; // 0 döndürür.
...

Bu değişkenleri tanımlarken ikisinin de eşdeğer olduklarını söylemiştik. Yukarıdaki son ifadede iki dizinin de ilk karakterleri karşılaştırılıyor. Eşit oldukları için farklı mı (!=) karşılaştırması 0 (sıfır) yani false döndürür.

Karakter dizileri tanımlayarak hafıza kapısından girdik. Şimdi hafıza üzerinde işlemler yapmaya başlayalım. Önceki yazımızda ve bu yazımızın ilk bölümünde bazı karakter fonksiyonlarını inceledik. Yazımızın devamında ise karakter dizileriyle işlem yapan fonksiyonları incelemeye başlayacağız. İnceleyeceğimiz ilk fonksiyon: memset.

void *memset(void *dest, int c, size_t len);

Bu fonksiyon string.h kütüphanesinde tanımlanmıştır. Fonksiyon tanımlanırken isminin önüne konulan * operatörü fonksiyonun geri dönüş değerinin hafızadaki bir adres olduğunu ifade eder. Baştaki void ise o adreste bulunan değerin veri türünün önemli olmadığını, yani her hangi bir veri türü olabileceğini ifade eder. Fonksiyon üç argümana sahiptir. İlki yine veri türü önemli olmayan bir işaretçi yani bir hafıza adresidir. İkincisi o adresten başlayarak hafızaya yazmak istediğimiz bir karakterin ondalık değerini verebilmemiz için bir tam sayıdır. Son argümanın veri tipi size_t olarak verilmiştir ve o karakterden kaç tane yazmak istediğimizi bu parametreyle veririz.

Son argümanın veri tipi unistd.h kütüphanesinde bir makro olarak tanımlanmıştır.

#define size_t unsigned long

Yani unsigned long veri tipine verilen kısa bir isimdir. Hafıza adresleri integer veri tipine sığmayacak kadar büyük, pozitif tam sayılardır. Bu nedenle çok büyük miktarda bir hafıza alanına erişebilmek için unsigned long veri tipi kullanılır.

Tanımından anlaşılacağı üzere memset fonksiyonu dest adresinden başlayarak len baytlık hafıza alanındaki her bir bayta c karakterini yazar. Yeni memory (hafıza) üzerindeki istediğimiz bir alanı istediğimiz bir karakter ile set eder. Bunu nasıl yaptığını anlamak için kendi memset fonksiyonumuzu yazalım ve ismini ft_memset koyalım.

#include <unistd.h>
#include <string.h>
#include <stdio.h>

void *ft_memset(void *dest, int c, size_t len) {
for (size_t i = 0; i < len; ++i)
((char *)dest)[i] = c; // char * cast
return dest;
}

int main(void) {
char test[6] = {43, 43, 43, 43, 43, 43};
char ft_test[6] = {43, 43, 43, 43, 43, 43};

printf("*memset(test, 42, 5): %s\n", (char *) memset(test, 42, 5));
printf("*ft_memset(test, 42, 5): %s\n", (char *) ft_memset(ft_test, 42, 5));
return 0;
}

Veri tipi belli olmayan dest adresine bayt bayt karakter yazabilmek için önce veri tipini bir karakter işaretçi olarak dönüştürüyoruz (cast). Sonra sıfırıncı indeksten başlayarak len-1 indeksine kadar len tane c karakteri atamak için bir döngü oluşturuyoruz. Fonksiyonun geri dönüş değeri ise dest adresi oluyor.

Fonksiyonu test etmek için altı karakterden oluşan iki dizi tanımladık. İkisini de ‘+’ karakteriyle doldurduk. Sonra birinin orijinal fonksiyonla, diğerinin ise kendi fonksiyonumuz ile ilk beş karakterini ‘*’ ile değiştirdik. Uygulamayı çalıştırdığımızda aşağıdaki ekran çıktısı üretilir.

*memset(test, 42, 5): *****+
*ft_memset(ft_test, 42, 5): *****+

Böylece hafızada ilk işlemimizi yapmış olduk. Fetih serimizin bir sonraki yazısında libc kütüphanesinden bazı string fonksiyonlarını incelemeye devam edeceğiz. Her fonksiyonda hafıza üzerinde işlem yapmanın farklı yollarını göreceğiz. Bu şekilde öğrenme yolculuğumu paylaşmaktan çok memnunum.

--

--

Mehmed Fatih Bayraktar

Master in Computer Engineering, working for Turkcell as Principal Data Analytics Developer and instructor at Turkcell Akademi