İçeriğe geç
academia.sh

Ders 15 / 15

Taşınabilirlik

Ortak tanımın bilerek basmadığı üç değer — adresler, char dışındaki tip boyutları, malloc(0) dönüşü — hedefe bağlıdır; bu ders bir değeri basmak yerine standardın garanti ettiği bir karşılaştırmayı basarak hedefe bağlı davranışı yalıtıyor ve kursun on beş dersini kapanış tablosunda topluyor.

Yayın Güncelleme

Bu derste

  • Adres, tip boyutu ve sıfır boyutlu ayırmanın neden hiç basılmadığını ayırt eder
  • Bir değer yerine standardın garanti ettiği bir karşılaştırmayı basarak hedefe bağlı davranışı yalıtır
  • Kursun on beş dersini sahiplik sınıfına göre tek bir tabloda toplar
İçindekiler

Önceki ders sinir parametresinin kaynakta değil çağıranda yazılı olduğunu ölçtü. Bu ders kursun kendisini kapatıyor ve geriye tek bir soru bırakıyor: ölçüm çekirdeği baştan beri neyi basmadı. sahiplik.c on üç gözlemi bastı, ama üç şeyi hiç basmadı — adresler, sizeof(char) dışındaki tip boyutları ve malloc(0)’ın boş dönüp dönmediği. Üçü de aynı nedenle dışarıda bırakıldı: üçü de hedefe bağlıdır, yani aynı kaynak metin iki farklı hedefte iki farklı sayı üretebilir.

Bu ders o üç değeri basmaya çalışmıyor. Onun yerine standardın garanti ettiği bir karşılaştırmayı basıyor. Aradaki fark şu: sizeof(int)’i basmak bir sayı verir ve o sayı hedefe göre değişir; sizeof(char) == 1 sorusunu sormak da bir sonuç verir, ama bu sonuç her hedefte aynıdır, çünkü standardın kendisi bunu garanti eder. Ölçülen şey aynı soruya iki farklı yaklaşım değil — biri hedefe bağlı bir değeri açığa çıkarır, öteki standardın hedeften bağımsız bıraktığı mantıksal sonucu açığa çıkarır. Bu dersin sahiplik sınıfı dildir: garantiyi veren taraf çağıran, çağrılan ya da sahipsiz kalan bir blok değil, dilin standardının kendisidir.

Üç Karşılaştırma, Tek Yalıtım Tekniği

Ölçüm çekirdeğinin sayaç birimi bu derste de kullanılıyor; kesiti aşağıda, yeniden yazılmadan.

// sayac.h — her ayirmayi ve her birakmayi sayan sarmalayici
#ifndef SAYAC_H
#define SAYAC_H

#include <stddef.h>

void *say_ayir(size_t boyut);
void *say_buyut(void *eski, size_t boyut);
void say_birak(void *blok);
int ayrilan_sayisi(void);
int birakilan_sayisi(void);

#endif
// sayac.c — sayaclar yalniz bu birimde tutulur
#include <stdlib.h>
#include "sayac.h"

static int ayrilan = 0;
static int birakilan = 0;

void *say_ayir(size_t boyut) {
    void *blok = malloc(boyut);
    if (blok != NULL) {
        ayrilan++;
    }
    return blok;
}

void *say_buyut(void *eski, size_t boyut) {
    int yeni_blok = (eski == NULL);
    void *yeni = realloc(eski, boyut);
    if (yeni != NULL && yeni_blok) {
        ayrilan++;
    }
    return yeni;
}

void say_birak(void *blok) {
    if (blok != NULL) {
        birakilan++;
    }
    free(blok);
}

int ayrilan_sayisi(void) {
    return ayrilan;
}

int birakilan_sayisi(void) {
    return birakilan;
}

SL10. sayac.c bu derste de aynı davranışı sürdürüyor: say_ayir yalnız dönüş NULL değilse sayacı artırıyor, say_birak yalnız NULL değilse sayacı artırıp yine de free’yi çağırıyor. Bu iki satır bu dersin son gözlemi için önceden kuruluyor — boyut sıfır olduğunda malloc’ın ne döndüreceği hedefe bağlı olsa da, sayac.c’nin bu iki satırının davranışı değişmiyor.

Yeni dosya üç karşılaştırmayı ve sıfır boyutlu ayırmayı bir arada ölçüyor. Hiçbir satır bir boyutu, bir sınır değerini ya da bir adresi basmıyor; her satır yalnız bir karşılaştırmanın sonucunu basıyor.

// tasinirlik.c — bir degeri basmak yerine standardin garantisini basar
#include <stdint.h>
#include <limits.h>
#include <stdio.h>
#include "sayac.h"

static void gozle(const char *ad, const char *sonuc) {
    printf("%-34s%s\n", ad, sonuc);
}

int main(void) {
    printf("%-34s%s\n", "gozlem", "sonuc");

    gozle("sabit genislikli tam sayi", sizeof(int32_t) * CHAR_BIT == 32 ? "garanti tutuyor" : "garanti tutmuyor");
    gozle("char boyutu", sizeof(char) == 1 ? "garanti tutuyor" : "garanti tutmuyor");
    gozle("int alt siniri", INT_MAX >= 32767 ? "garanti tutuyor" : "garanti tutmuyor");

    int birinci = 7;
    int ikinci = 7;
    int *aynisi = &birinci;
    int *tekrar = &birinci;
    int *ayrisi = &ikinci;
    gozle("ayni degiskenin iki adresi", aynisi == tekrar ? "esit" : "esit degil");
    gozle("iki ayri degiskenin adresi", aynisi == ayrisi ? "esit" : "esit degil");

    int once = ayrilan_sayisi() - birakilan_sayisi();
    void *sifir_boyut = say_ayir(0);
    say_birak(sifir_boyut);
    int sonra = ayrilan_sayisi() - birakilan_sayisi();
    gozle("sifir boyutlu ayirma sozlesmesi", sonra - once == 0 ? "sozlesme saglandi" : "sozlesme bozuldu");

    return 0;
}
gozlem                            sonuc
sabit genislikli tam sayi         garanti tutuyor
char boyutu                       garanti tutuyor
int alt siniri                    garanti tutuyor
ayni degiskenin iki adresi        esit
iki ayri degiskenin adresi        esit degil
sifir boyutlu ayirma sozlesmesi   sozlesme saglandi

SL11. İlk üç satır aynı kalıbı izliyor: bir tip boyutunu ya da bir sınır değerini doğrudan basmak yerine, standardın zaten garanti ettiği bir alt sınırla karşılaştırıyor. int32_t var olduğunda tam olarak otuz iki bit genişliğindedir — bu, tanımın kendisinden gelir, hedeften değil. Karşılaştırmanın bayt sayısıyla değil bit sayısıyla kurulmasının nedeni de budur: bir bayttaki bit sayısı (CHAR_BIT) standartta yalnız en az sekiz olarak garanti edilir, bu yüzden sizeof(int32_t) hedeften hedefe değişebilir, sizeof(int32_t) * CHAR_BIT ise değişmez. sizeof(char) her zaman birdir — bu da tanımdan gelir; kursun başından beri sizeof(char) dışındaki hiçbir boyutun basılmamasının nedeni tam olarak budur. INT_MAX standart tarafından en az 32767 olacak şekilde garanti edilir; gerçek değeri hedefe göre daha büyük olabilir, ama bu alt sınırın tutup tutmadığı her hedefte aynı sonucu verir.

SL12. Dördüncü ve beşinci satır adresleri basmadan işaretçi eşitliğini ölçüyor. aynisi ile tekrar aynı değişkenin adresini tutuyor; ikisinin eşitliği bir dil kuralından gelir — aynı ifade (&birinci) her değerlendirildiğinde aynı sonucu verir. aynisi ile ayrisi ise aynı anda yaşayan iki ayrı otomatik nesnenin adreslerini tutuyor; standart bu iki nesnenin çakışmayacağını garanti eder, bu yüzden eşitsizlik de bir uygulama tesadüfü değil, bir dil garantisidir. İki satır da adresin kendisini hiç göstermeden bu iki garantiyi ölçüyor.

SL13. Son satır ortak tanımın üçüncü basılmayanını ele alıyor: malloc(0)’ın boş döndürüp döndürmediği hedefe bağlıdır ve bu ders o soruyu yanıtlamıyor. Bunun yerine sayac.c’nin iki satırının bu belirsizlikten etkilenip etkilenmediğini ölçüyor. say_ayir(0) NULL dönerse sayaç hiç artmaz ve say_birak(NULL) da hiç artırmaz — fark sıfır kalır. say_ayir(0) geçerli bir işaretçi dönerse sayaç bir artar ve say_birak onu bırakırken sayaç da bir artar — fark yine sıfır kalır. malloc(0)’ın hangi dalı izlediği hiçbir zaman görünmüyor; görünen tek şey, sözleşmenin her iki dalda da aynı kaldığıdır.

Sabit Genişlikli Tiplerin Kendi Sınırı

<stdint.h>’ın sunduğu yalıtım kendi başına sınırsız değil. Standart, int32_t gibi tam genişlikli tipleri yalnız hedef platformda öyle bir tip varsa tanımlamayı zorunlu kılar; yokluğunda bu tipler hiç tanımlanmaz ve onları kullanan bir kaynak dosya derlenmez. Standart, her hedefte bulunmasını zorunlu kıldığı tipleri ayrı adlarla verir — int_least32_t en az otuz iki bit genişliğinde olmayı garanti eder, int32_t ise tam olarak otuz iki bit genişliğinde olmayı, ama yalnız var olduğunda.

SL14. Bu derste int32_t’nin varlığını sınamak yerine doğrudan kullanılması, dersin kendisinin bir varsayımı: dersin derlendiği ortamda bu tip var. Yalıtım tekniğinin kendisi de böylece bir varsayıma yaslanıyor — genişliğin değil, tipin varlığının varsayımına. Tam genişliğin gerekmediği bir yerde taşınabilir bir kaynak int_least32_t ya da int_fast32_t kullanır; bu ders int32_t’yi yalnız örnek için seçti.

Sınırlayıcı Ölçüm

Bir değeri basmak yerine bir garantiyi basmak, hedefe bağlı olanı ortadan kaldırmaz — yalnız onu gözlemin dışına taşır. sizeof(int32_t) * CHAR_BIT == 32 ifadesi her hedefte aynı sonucu verir, ama bunu yalnızca int32_t var olduğunda yapar; standardın kendisi bu tipin var olmasını zorunlu kılmaz. Aynı biçimde INT_MAX >= 32767 her hedefte tutar, ama gerçek INT_MAX değeri hedeften hedefe değişebilir — ders bu değeri hiç basmayarak taşınırlığı korur, ama sınamak istediği asıl soruyu (INT_MAX tam olarak kaçtır) yanıtsız bırakır. Bir karşılaştırma bir değerden daha taşınabilirdir, ama daha az bilgi taşır; taşınabilirlik ile bilgi arasındaki bu değiş tokuş, dilin garantisinin nerede bittiğini işaretler. Ölçüm çekirdeğinin dört sınıfından hiçbiri bu değiş tokuşu ortadan kaldırmaz — dil sınıfı da dahil, çünkü garantiyi veren dil, garantinin kapsamını da kendisi çizer.

Özet

  • Bu dersin ölçtüğü bellek sınıfı dildir: garantiyi veren taraf çağıran, çağrılan ya da sahipsiz kalan bir blok değil, dilin standardının kendisidir.
  • Ortak tanımın bilerek basmadığı üç değer — adresler, sizeof(char) dışındaki tip boyutları, malloc(0)’ın dönüşü — üçü de hedefe bağlıdır ve bu derste de basılmadı.
  • Bir değer yerine standardın garanti ettiği bir karşılaştırma basıldığında sonuç her hedefte aynı kalır; sizeof(int32_t) * CHAR_BIT == 32, sizeof(char) == 1 ve INT_MAX >= 32767 üçü de bu kalıbı izledi.
  • Aynı anda yaşayan iki otomatik nesnenin adresleri çakışmaz; bu eşitsizlik adresin kendisi hiç basılmadan, yalnız == karşılaştırmasıyla ölçüldü.
  • malloc(0)’ın boş dönüp dönmediği hedefe bağlı kalsa da, sayac.c’nin ayırma-bırakma sözleşmesi bu belirsizlikten etkilenmedi: fark her iki dalda da sıfır kaldı.
  • Bir garantiyi basmak hedefe bağlı olanı ortadan kaldırmaz, yalnız gözlemin dışına taşır — int32_t’nin kendisi de var olmayabilecek bir tiptir.

Kurs Kapanışı

Kurs boyunca tek bir soru soruldu: bu belleği kim sahipleniyor. On dört ders bu soruyu dört sınıfla yanıtladı — dil, çağıran, çağrılan, sahipsiz — ve bu ders, aynı sorunun dilin kendisine sorulduğunda ne söylediğini gösterdi: standardın garanti ettiği yerde dil sahiplenir, garantinin bittiği yerde gözlem hedefe bağlı kalır ve basılmaz.

Ders Ölçülen bellek Sahibi Sınırlayıcı ölçüm
İşaretçi Temelleri Yerel değişken, statik alan, dizgi değişmezi, yerel dizi dil Bir işaretçinin tipi kimin bırakacağını söylemez
İşaretçi Aritmetiği Dizi içi ilerleme ve eleman farkı dil Sınırın bir ötesini göstermek geçerlidir, dolaylamak tanımsızdır
Diziler ve İşaretçiler Dinamik olarak ayrılmış dizi çağıran Bozunma yalnız bir düzeyde çalışır, eleman sayısını taşımaz
Boş ve Sarkan İşaretçiler Bırakılmış bloğu gösteren işaretçi sahipsiz NULL atamak yalnız o işaretçiyi denetlenebilir kılar, aynı bloğu gösteren başka bir işaretçiyi etkilemez
void ve Opak İşaretçiler Opak kayıt (Kayit) çağrılan Opaklık yapının içini gizler, sahipliği gizlemez: bırakma yükümlülüğü yalnız yorumda yazılıdır
Fonksiyon İşaretçileri Geri çağrıya taşınan bağlam çağıran Bir imza davranışı garanti etmez, yalnız tip uyumunu denetler
Bellek Ayırma İşlevleri malloc/calloc/realloc ile ayrılan blok çağıran malloc(0) ve realloc(blok, 0) dönüşü hedefe bağlıdır, hiç basılmaz
Serbest Bırakma Disiplini Ödünç verilen ad ile döndürülen kopya çağrılan Sahiplik imzadan değil, başlık dosyasındaki yorumdan anlaşılır
Bellek Sızıntıları Sahiplenilmeyen iki blok sahipsiz Sızıntı çökme ya da uyarı üretmez, yalnız sayaç farkıyla görünür
Bellek Hata Ayıklama Araçları Dengeli ve dengesiz ayırma-bırakma çifti sahipsiz Bir araç yalnız eşleşmeyen çağrıyı sayar, sözleşme ihlalini okumaz
Bellek Düzeni Dört bellek bölgesindeki değişken dil Bölge sınırları ve hizalama basılmaz; öbek dışında sahiplik bölgeden değil sözleşmeden gelir
Tanımsız Davranış Dört tanımsız işlemin tanımlı karşılığı sahipsiz Çalışıyor görünmek tanımlı olduğunu göstermez
Hata Yönetimi Kalıpları Hata yolunda temizlenen blok çağrılan Kısmi başarısızlıkta kimin temizleyeceği her işlevde ayrı yazılmalıdır
Sınır Denetimi snprintf ile sınırlanan kopya çağıran İşlev yalnız kendisine söylenen sınırı denetler, yanlış bildirilen sınırı yakalayamaz
Taşınabilirlik Sabit genişlikli tip, sizeof(char), INT_MAX, sıfır boyutlu ayırma dil Yalıtım tekniğinin kendisi de bir garantiye dayanır: int32_t’nin var olması standart tarafından zorunlu değildir

Dersler sınıflara 4 dil · 4 çağıran · 3 çağrılan · 4 sahipsiz olarak dağılıyor — bu derslerin dağılımıdır ve çekirdeğin on üç gözleminin 4/4/3/2’siyle karıştırılmaz.

Dört sınıf on beş ders boyunca hiç değişmedi, ama her ders sınıfı farklı bir yüzden gösterdi: kimi zaman sahiplenen taraf bir işlevdi, kimi zaman bir sözleşmeydi, kimi zaman hiç kimseydi, kimi zaman da bu son derste görüldüğü gibi dilin kendisiydi. Bir işaretçinin tipi hiçbir zaman sahibini söylemedi; sahibi her seferinde ya kaynağın kendisi (bir & ifadesi, bir ayırma çağrısı) ya da başlık dosyasındaki yazılı sözleşme belirledi.

Sıradaki kurs C ile sistem programlamaya, işletim sistemi arayüzlerine bakıyor. Bu kursun sahiplik sorusu orada da geçerliliğini koruyor — bir dosya tanıtıcısını, bir süreci ya da bir sinyali kim sahipleniyor sorusu, bellek için sorulan soruyla aynı biçimdedir; ama bu kaynakların sınıflarını o kurs kendi ölçümüyle kuracak.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ ↵ Esc gezin · aç · kapat