C işaretçisi ile bit bildirimi ve operatör ile dizi bildirimi


9

Aşağıdaki kodu anlamak istiyorum:

//...
#define _C 0x20
extern const char *_ctype_;
//...
__only_inline int iscntrl(int _c)
{
    return (_c == -1 ? 0 : ((_ctype_ + 1)[(unsigned char)_c] & _C));
}

Obenbsd işletim sistemi kaynak kodundan ctype.h dosyasından kaynaklanır . Bu işlev, bir karakterin bir kontrol karakteri mi yoksa ascii aralığının içindeki yazdırılabilir bir harf olup olmadığını kontrol eder. Bu benim şu andaki düşünce zincirim:

  1. iscntrl ('a') çağrılır ve 'a' tam sayı değerine dönüştürülür
  2. önce _c'nin -1 olup olmadığını kontrol edin, sonra 0 döndürün ...
  3. adsız undef işaretçi puanları artırmak tarafından 1
  4. bu adresi bir uzunluk dizisine işaretçi olarak bildir (işaretsiz karakter) ((int) 'a')
  5. bitsel ve operatörü _C (0x20) ve diziye (???) uygulayın

Her nasılsa, garip bir şekilde çalışır ve 0 döndürüldüğünde her zaman verilen char _c yazdırılabilir bir karakter değildir. Aksi takdirde, yazdırılabilir durumdaysa işlev yalnızca özel bir ilgisi olmayan bir tamsayı değeri döndürür. Anlama problemim adım 3, 4 (biraz) ve 5'te.

Herhangi bir yardım için teşekkürler.


1
_ctype_aslında bir dizi bit maskesi. İlgilenilen karakterle endeksleniyor. Yani _ctype_['A']"alfa" ve "büyük harf" e _ctype_['a']karşılık gelen bitler içerecek, "alfa" ve "küçük harf" e karşılık gelen bitler _ctype_['1']içerecek, "basamak" a karşılık gelen bir bit içerecektir, vb. Görünüşe göre 0x20"kontrol" e karşılık gelen bit . Ancak bir nedenle _ctype_dizi 1 ile dengelenir, bu nedenle bitler 'a'gerçekten içeridedir _ctype_['a'+1]. (Bu muhtemelen EOFekstra test olmadan bile çalışmasına izin
Steve Summit

Oyuncular (unsigned char)karakterlerin imzalı ve olumsuz olma ihtimalini gözetmektir.
Steve Summit

Yanıtlar:


3

_ctype_sembol tablosunun kısıtlı bir dahili sürümü gibi görünüyor ve tahmin ediyorum ki yazdırılamadığı için bunun + 1kaydedilme indeksini rahatsız etmedi 0. Veya muhtemelen C'de özel olduğu gibi 0 dizinli yerine 1 dizinli bir tablo kullanıyorlar.

C standardı bunu tüm ctype.h işlevleri için belirler:

Her durumda argüman, değeri makrosu intolarak temsil edilecek unsigned charveya makronun değerine eşit olacak birEOF

Kodda adım adım ilerlemek:

  • int iscntrl(int _c)intTürleri gerçekten karakterlerdir, ancak tüm ctype.h fonksiyonları kolu için gerekli olan EOFonlar olmalı, böylece int.
  • Çek karşı -1karşı bir çek EOFo değere sahip olduğundan, -1.
  • _ctype+1 dizi öğesinin adresini almak için işaretçi aritmetiğidir.
  • [(unsigned char)_c]yalnızca, dizinin temsil edilebildiği parametrenin standart gereksinimini zorunlu kılmak için bu dizinin bir dizi erişimidir unsigned char. Not charBu savunma programlama yani aslında negatif bir değer tutabilir. []Dizi erişiminin sonucu, dahili sembol tablolarından tek bir karakterdir.
  • &Maskeleme sembol tablosundan karakter belirli bir grup almak için vardır. Görünüşe göre bit 5 setine sahip tüm karakterler (maske 0x20) kontrol karakterleri. Tabloyu görmeden bunun bir anlamı yok.
  • Bit 5 ayarlı herhangi bir şey, sıfır olmayan bir değer olan 0x20 ile maskelenen değeri döndürür. Bu, boolean true durumunda sıfırdan farklı olmayan işlevin gereksinimini karşılar.

Oyuncunun değerin temsil edilebileceği standart şartı yerine getirmesi doğru değildir unsigned char. Standart , rutin çağrıldığında zaten * değerinin unsigned chareşit veya eşit olarak temsil edilebilir olmasını gerektirir EOF. Oyuncu kadrosu yalnızca “savunma” programlama işlevi görür: Bir makro kullanırken bir değer iletmek için onus üzerlerinde işaretli char(veya a signed char) geçen bir programcının hatasını düzeltme . Şunu belirtmek gerekir ki, −1 kullanan bir uygulamada this1 değeri iletildiğinde hatayı düzeltemez . unsigned charctype.hcharEOF
Eric Postpischil

Bu da bir açıklama sunar + 1. Makro daha önce bu savunma ayarını içermiyorsa, yalnızca ((_ctype_+1)[_c] & _C)as1 ila 255 arasındaki ön ayar değerleri ile dizinlenmiş bir tabloya sahip olabilirdi. Böylece ilk giriş atlanmadı ve bir amaca hizmet etti. Birisi daha sonra defansif oyuncu kadrosunu eklediğinde, EOF−1'in değeri bu oyuncu kadrosuyla çalışmaz, bu yüzden özel olarak davranması için koşullu operatörü ekler.
Eric Postpischil

3

_ctype_, 257 baytlık küresel bir diziye bir işarettir. Ne _ctype_[0]için kullanıldığını bilmiyorum . _ctype_[1]yoluyla _ctype_[256]_sırasıyla 0,…, 255 karakter kategorilerini temsil eder: karakterin kategorisini _ctype_[c + 1]temsil eder c. Bu, _ctype_ + 1karakterin kategorisini (_ctype_ + 1)[c]temsil eden 256 karakterlik bir diziyi gösterdiğini söylemekle aynı şeydir c.

(_ctype_ + 1)[(unsigned char)_c]bir deklarasyon değildir. Dizi alt simge operatörünü kullanan bir ifadedir. Başlayan (unsigned char)_cdizinin konumuna erişiyor (_ctype_ + 1).

Kod atmalarını _cgelen intetmek unsigned charkesinlikle gerekli değildir: Ctype işlevleri için dökme kömür değerler alan unsigned char( charOpenBSD'deki imzalanmış): Doğru bir çağrıdır char c; … iscntrl((unsigned char)c). Arabellek taşması olmadığını garanti etme avantajına sahiptirler: uygulama iscntrl, aralığın dışında unsigned charve -1 olmayan bir değerle çağırırsa , bu işlev anlamlı olmayabilecek ancak en azından neden olmayacak bir değer döndürür dizi sınırları dışındaki adreste gerçekleşen bir kilitlenme veya özel veri sızıntısı. İşlev, -1 olmadığı char c; … iscntrl(c)sürece çağrılırsa değer bile doğrudur c.

-1 ile özel davanın nedeni bunun olmasıdır EOF. charÖrneğin getchar, a üzerinde çalışan birçok standart C işlevi , karakteri intpozitif bir aralığa sarılmış karakter değeri olan bir değer olarak temsil eder ve EOF == -1hiçbir karakterin okunamayacağını belirtmek için özel değeri kullanır. getchar, Gibi işlevler EOFiçin dosyanın sonunu, dolayısıyla e nd- o f- f ile adını gösterir . Eric Postpischil , kodun sadece adil return _ctype_[_c + 1]olduğunu ve muhtemelen doğru olduğunu söylüyor: _ctype_[0]EOF için değer olurdu. Bu daha basit uygulama, işlev kötüye kullanıldığında bir arabellek taşmasına neden olurken, mevcut uygulama yukarıda tartışıldığı gibi bundan kaçınır.

Eğer vdizisinde bulunan bir değerdir, v & _Cbiraz az ise test 0x20ayarlanır v. Dizideki değerler, karakterin bulunduğu kategorilerin maskeleridir: _Ckontrol karakterleri _Uiçin ayarlanır, büyük harfler için ayarlanır vb.


(_ctype_ + 1)[_c] olur C standardında belirtildiği şekilde ya da içinden geçirilmesi için kullanıcı sorumluluğu olduğu için, doğru dizi indekse EOFveya unsigned chardeğer. Diğer değerlerin davranışı C standardı tarafından tanımlanmamıştır. Oyuncular, C standardının gerektirdiği davranışı gerçekleştirmeye hizmet etmez. Negatif karakter değerlerini yanlış geçen programcıların neden olduğu hatalara karşı koruma sağlamak için kullanılan bir çözümdür. Ancak, eksik veya yanlıştır (ve düzeltilemez) çünkü −1 karakter değeri mutlaka olarak değerlendirilecektir EOF.
Eric Postpischil

Bu da bir açıklama sunar + 1. Makro daha önce bu savunma ayarını içermiyorsa, yalnızca ((_ctype_+1)[_c] & _C)as1 ila 255 arasındaki ön ayar değerleri ile dizinlenmiş bir tabloya sahip olabilirdi. Böylece ilk giriş atlanmadı ve bir amaca hizmet etti. Birisi daha sonra defansif oyuncu kadrosunu eklediğinde, EOF−1'in değeri bu oyuncu kadrosuyla çalışmaz, bu yüzden özel olarak davranması için koşullu operatörü ekler.
Eric Postpischil

2

Adım 3 ile başlayacağım:

adsız undef işaretçi puanları artırmak tarafından 1

İşaretçisi değildir tanımsız. Başka bir derleme biriminde tanımlanmıştır. Yani ne externparçası derleyici söyler. Dolayısıyla, tüm dosyalar birbirine bağlandığında, bağlayıcı ona yapılan referansları çözecektir.

Peki neye işaret ediyor?

Her karakter hakkında bilgi içeren bir diziyi gösterir. Her karakterin kendi girişi vardır. Giriş, karakter için karakteristiklerin bitmap temsilidir. Örneğin: Bit 5 ayarlanırsa, karakterin bir kontrol karakteri olduğu anlamına gelir. Başka bir örnek: Bit 0 ayarlanırsa, karakterin bir üst karakter olduğu anlamına gelir.

Yani benzer bir şey (_ctype_ + 1)['x']geçerli olan özellikleri alacaktır 'x'. Daha sonra bir bitsel ve bit 5'in ayarlanıp ayarlanmadığını kontrol etmek için yapılır, yani bunun bir kontrol karakteri olup olmadığını kontrol edin.

1 eklemenin nedeni büyük olasılıkla 0 gerçek endeksinin özel bir amaç için ayrılmasıdır.


1

Buradaki tüm bilgiler kaynak kodunu (ve programlama deneyimini) analiz etmeye dayanır.

Deklarasyon

extern const char *_ctype_;

derleyiciye const charadlı bir yere bir işaretçi olduğunu söyler _ctype_.

(4) Bu işaretçiye bir dizi olarak erişilir.

(_ctype_ + 1)[(unsigned char)_c]

Oyuncular (unsigned char)_c, dizin değerinin bir unsigned char(0..255) aralığında olmasını sağlar .

İşaretçi aritmetiği _ctype_ + 1, dizi konumunu etkili bir şekilde 1 öğe kaydırır. Diziyi neden bu şekilde uyguladıklarını bilmiyorum. Aralığını kullanarak _ctype_[1].. _ctype[256]karakter değerleri için 0.. 255yapraklar değeri _ctype_[0]bu işlev için kullanılmayan. (1'in ofseti birkaç alternatif yolla uygulanabilir.)

Dizi erişimi char, karakter değerini dizi dizini olarak kullanarak bir değer ( alan kazanmak için tür ) alır.

(5) Bitsel AND işlemi, değerden tek bir bit çıkarır.

Görünüşe göre dizideki değer, bit 5'in (0'dan en az anlamlı bit başlayarak saymak, = 0x20) "bir kontrol karakteri" için bir bayrak olduğu bir bit alanı olarak kullanılır . Böylece dizi, karakterlerin özelliklerini tanımlayan bit alanı değerleri içerir.


Sanırım bunun yerine + 1öğelere eriştiklerini netleştirmek için işaretçiye taşındılar . için örtük dönüşüm nedeniyle eşdeğer olurdu . Ve daha da açık ve öz olurdu. 1..2561..255,0_ctype_[1 + (unsigned char)_c]int_ctype_[(_c & 0xff) + 1]
cmaster

0

Buradaki anahtar , sonucun elde edilmesi için ifadenin ne yaptığını anlamaktır (_ctype_ + 1)[(unsigned char)_c](daha sonra bitsel ve operasyona beslenir & 0x20!

Kısa cevap: _c + 1Dizinin işaret ettiği öğeyi döndürür _ctype_.

Nasıl?

İlk olarak, tanımsız olduğunu düşünmenize rağmen _ctype_, aslında değil! Üstbilgi bunu harici bir değişken olarak bildirir - ancak programınızı oluşturduğunuzda bağlandığı çalışma zamanı kitaplıklarından birinde (neredeyse kesin olarak) tanımlanır.

Sözdiziminin dizi indekslemesine nasıl karşılık geldiğini göstermek için aşağıdaki kısa program üzerinde çalışmayı (hatta derlemeyi) deneyin:

#include <stdio.h>
int main() {
    // Code like the following two lines will be defined somewhere in the run-time
    // libraries with which your program is linked, only using _ctype_ in place of _qlist_ ...
    const char list[] = "abcdefghijklmnopqrstuvwxyz";
    const char* _qlist_ = list;
    // These two lines show how expressions like (a)[b] and (a+1)[b] just boil down to
    // a[b] and a[b+1], respectively ...
    char p = (_qlist_)[6];
    char q = (_qlist_ + 1)[6];
    printf("p = %c  q = %c\n", p, q);
    return 0;
}

Daha fazla açıklama ve / veya açıklama istemekten çekinmeyin.


0

Bildirilen işlevler ctype.htürdeki nesneleri kabul eder int. Bağımsız değişken olarak kullanılan karakterler için, türün ön dökümleri olduğu varsayılır unsigned char. Bu karakter, karakterin karakteristiğini belirleyen bir tabloda dizin olarak kullanılır.

Kontrol değeri değerini içerdiği _c == -1durumlarda kullanılır gibi görünüyor . Değilse, _c ifadeyle gösterilen tabloda dizin olarak kullanılan imzasız karakter türüne dökülür . Ve maske tarafından belirtilen bit ayarlanmışsa, karakter bir kontrol sembolüdür._cEOFEOF_ctype_ + 10x20

İfadeyi anlamak için

(_ctype_ + 1)[(unsigned char)_c]

dizi aboneliğinin aşağıdaki gibi tanımlanan bir postfix operatörü olduğunu göz önünde bulundurun

postfix-expression [ expression ]

Gibi yazamazsın

_ctype_ + 1[(unsigned char)_c]

çünkü bu ifade şuna eşdeğerdir:

_ctype_ + ( 1[(unsigned char)_c] )

Böylece _ctype_ + 1birincil ifadeyi almak için ifade parantez içine alınır.

Yani aslında

pointer[integral_expression]

integral_expressionişaretçi olduğu ifade olarak hesaplanan dizindeki bir dizinin nesnesini veren (gereği işaretçi (_ctype_ + 1)aritmetucudur) ve integral_expressiondizin ise ifadedir (unsigned char)_c.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.