Neden bir bit alanına bir değer atamak aynı değeri geri vermiyor?


96

Bu Quora gönderisinde aşağıdaki kodu gördüm :

#include <stdio.h>

struct mystruct { int enabled:1; };
int main()
{
  struct mystruct s;
  s.enabled = 1;
  if(s.enabled == 1)
    printf("Is enabled\n"); // --> we think this to be printed
  else
    printf("Is disabled !!\n");
}

Hem C hem de C ++ 'da, kodun çıktısı beklenmediktir ,

Devre dışı bırakıldı !!

O yazıda "işaret biti" ile ilgili açıklama verilmiş olsa da, bir şeyi nasıl ayarladığımızı ve sonra olduğu gibi yansıtmamasının nasıl mümkün olduğunu anlayamıyorum.

Birisi daha ayrıntılı bir açıklama yapabilir mi?


Not : Her iki etiket & bit alanlarını açıklamak için standartları biraz farklı olduğundan, gereklidir. C spesifikasyonu ve C ++ spesifikasyonu için yanıtlara bakın .


46
Saklayıcısında yana olarak bildirilmiş intben yalnızca değerleri tutabilir düşünmek 0ve -1.
Osiris

6
int -1'in nasıl depolandığını bir düşünün. Tüm bitler 1'e ayarlanmıştır. Dolayısıyla, sadece bir bitiniz varsa, açıkça -1 olmalıdır. Yani 1 bit int'teki 1 ve -1 aynıdır. Denetimi 'if (s.enabled! = 0)' olarak değiştirin ve çalışır. Çünkü 0 olamaz.
Jürgen

3
Bu kuralların C ve C ++ 'da aynı olduğu doğrudur. Ancak etiket kullanım politikalarına göre, bunu yalnızca C olarak etiketlemeli ve gerekmediğinde çapraz etiketlemekten kaçınmalıyız. C ++ bölümünü kaldıracağım, gönderilen yanıtları etkilememelidir.
Lundin

8
Olarak değiştirmeyi denediniz struct mystruct { unsigned int enabled:1; };mi?
ChatterOne

4
Lütfen okumak C ve C ++ etiketi politikaları , ilgili özellikle kısmını çapraz etiketleme C ve C ++ hem toplum uzlaşma yoluyla kurulan burada . Bir geri dönüş savaşına girmeyeceğim, ancak bu soru yanlış bir şekilde C ++ olarak etiketlendi. Dillerin çeşitli TC'ler nedeniyle küçük bir farkları olsa bile, C ve C ++ arasındaki fark hakkında ayrı bir soru sorun.
Lundin

Yanıtlar:


78

Bit alanları, standart tarafından inanılmaz derecede zayıf tanımlanmıştır. Bu kod göz önüne alındığında struct mystruct {int enabled:1;};, o zaman yok biliyoruz:

  • Bunun ne kadar yer kapladığı - dolgu bitleri / baytları varsa ve bunların bellekte nerede bulunduğu.
  • Bitin bellekte bulunduğu yer. Tanımlanmamıştır ve aynı zamanda dayanıklılığa da bağlıdır.
  • Bir int:nbit alanının imzalı veya işaretsiz olarak kabul edilip edilmeyeceği.

Son bölümle ilgili olarak, C17 6.7.2.1/10 diyor ki:

Bir bit alanı, belirtilen sayıda bit 125'ten oluşan işaretli veya işaretsiz bir tam sayı türüne sahip olarak yorumlanır )

Yukarıdakileri açıklayan normatif olmayan not:

125) Yukarıda 6.7.2'de belirtildiği gibi, kullanılan gerçek tip belirleyicisi intveya bir typedef-adı ise int, o zaman bit alanının imzalı veya işaretsiz olup olmadığı uygulama tanımlıdır.

signed intBit alanı olarak kabul edilecekse ve biraz boyut yaparsanız 1, o zaman veri için yer kalmaz, sadece işaret biti için. Programınızın bazı derleyicilerde garip sonuçlar vermesinin nedeni budur.

İyi pratik:

  • Bit alanlarını asla herhangi bir amaç için kullanmayın.
  • intHerhangi bir bit işleme biçimi için işaretli tür kullanmaktan kaçının .

5
İş yerinde, doldurulmadıklarından emin olmak için bit alanlarının boyutu ve adresleri üzerinde static_asserts var. Donanım yazılımımızda donanım kayıtları için bit alanları kullanıyoruz.
Michael

4
@Lundin: # define-d maskeleri ve ofsetleri ile ilgili çirkin şey, kodunuzun vardiyalar ve bit bazında VE / VEYA operatörleriyle dolmasıdır. Bitfields ile derleyici bunu sizin için halleder.
Michael

4
@Michael Bitfields ile derleyici sizin için ilgilenir. Pekala, eğer "bununla ilgilenmek" için standartlarınız "taşınabilir değil" ve "öngörülemez" ise sorun değil. Benimki bundan daha yüksek.
Andrew Henle

3
@AndrewHenle Leushenko, yalnızca C standardının bakış açısından, x86-64 ABI'yi takip edip etmemesinin uygulamaya bağlı olduğunu söylüyor .
mtraceur

3
@AndrewHenle Doğru, her iki noktada da katılıyorum. Demek istediğim, Leushenko ile olan anlaşmazlığınızın, "tanımlanmış uygulama" yı yalnızca C standardı tarafından kesin olarak tanımlanmamış veya ABI platformu tarafından kesin olarak tanımlanmış şeylere atıfta bulunmak için kullandığınız gerçeğine dayandığını düşünüyorum ve o bunu başvurmak için kullanıyor. sadece C standardı ile kesin olarak tanımlanmamış herhangi bir şeye.
mtraceur

58

Anlayamıyorum, bir şeyi ayarlamamız nasıl mümkün oluyor ve sonra olduğu gibi görünmüyor.

Neden derlediğini mi soruyorsunuz, size hata mı veriyor?

Evet, ideal olarak size bir hata vermelidir. Ve derleyicinizin uyarılarını kullanırsanız yapar. GCC'de -Werror -Wall -pedantic:

main.cpp: In function 'int main()':
main.cpp:7:15: error: overflow in conversion from 'int' to 'signed char:1' 
changes value from '1' to '-1' [-Werror=overflow]
   s.enabled = 1;
           ^

Bunun neden uygulama tanımlı olarak bırakıldığına ve bir hataya bırakıldığının gerekçesinin daha çok, bir döküm gerektirmenin eski kodu kırmak anlamına geldiği tarihsel kullanımlarla ilgisi olabilir. Standardın yazarları, uyarıların ilgilileri rahatlatmak için yeterli olduğuna inanabilir.

Bir miktar kuralcılık eklemek için, @ Lundin'in ifadesini yankılayacağım: "Bit alanlarını asla herhangi bir amaç için kullanmayın." Bellek düzeni ayrıntılarınız hakkında düşük seviyeli ve spesifik olmak için, sizi ilk etapta bit alanlarına ihtiyacınız olduğunu düşünmeye sevk edecek türden iyi nedenleriniz varsa, neredeyse kesinlikle sahip olduğunuz diğer ilgili gereksinimler, eksik belirtimlerine karşı çıkacaktır.

(TL; DR - Bit alanlarına meşru bir şekilde "ihtiyaç duyacak" kadar bilgiliyseniz, bunlar size hizmet edecek kadar iyi tanımlanmamışlardır.)


15
Standardın yazarları, bit alanı bölümünün tasarlandığı gün tatildeydi. Yani hademe yapmak zorunda kaldı. Hakkında hiç bir mantığı vardır şeyden bit alanları nasıl tasarlandığı konusunda.
Lundin

9
Tutarlı bir teknik gerekçe yok. Ancak bu beni siyasi bir mantık olduğu sonucuna götürüyor : mevcut kod veya uygulamaları yanlış yapmaktan kaçınmak. Ancak sonuç, bit alanları hakkında güvenebileceğiniz çok az şey olmasıdır.
John Bollinger

6
@JohnBollinger C90'a çok zarar veren kesinlikle bir politika vardı. Bir keresinde komite üyelerinden biriyle konuştum ve bu saçmalıkların çoğunun kaynağını açıkladı - ISO standardının bazı mevcut teknolojileri desteklemesine izin verilemezdi. Bu nedenle, 1'in tamamlayıcısı ve işaretli büyüklüğü, uygulama tanımlı imzası, char8 bit olmayan bayt desteği vb. Gibi aptalca şeylerle sıkışıp kaldık. Aptal bilgisayarlara pazar dezavantajı vermelerine izin verilmedi.
Lundin

1
@Lundin Ödünleşmelerin yanlışlıkla yapıldığına inanan insanlardan ve neden ölümden sonra yazılanlardan ve otopsilerden oluşan bir koleksiyon görmek ilginç olurdu. Merak ediyorum, "bunu geçen sefer yaptık ve işe yaramadı / işe yaramadı" üzerine ne kadar çok çalışma, insanların kafasındaki hikayelere karşılık böyle bir sonraki vakayı bilgilendirmek için kurumsal bilgi haline geldi.
HostileFork,

1
Bu hala nokta no olarak listelenmiştir. C2x Tüzüğündeki C'nin orijinal ilkelerinden 1'i: "Mevcut kod önemlidir, mevcut uygulamalar değildir." ... "C'yi tanımlamak için hiçbir uygulama örnek olarak gösterilmemiştir: Mevcut tüm uygulamaların Standarda uymak için bir şekilde değişmesi gerektiği varsayılmaktadır."
Leushenko

23

Bu, uygulama tanımlı davranıştır. Bunu çalıştırdığınız makinelerin iki-iltifatlı işaretli tamsayılar kullandığını ve intbu durumda if ifadesinin doğru parçasıysa neden girmediğinizi açıklamak için işaretli bir tamsayı olarak davrandığını varsayıyorum .

struct mystruct { int enabled:1; };

enable1 bitlik bir alan olarak bildirir . İmzalı olduğu için geçerli değerler -1ve şeklindedir 0. Alanın 1, bitin geri döndüğü taşmalara ayarlanması -1(bu tanımsız bir davranıştır)

İmzalı bit alanı ile uğraşırken Esasen maksimum değerdir 2^(bits - 1) - 1olan 0bu durumda.


"İmzalandıktan sonra geçerli değerler -1 ve 0'dır". İmzalı olduğunu kim söyledi? Tanımlanmış değil, uygulama tanımlı bir davranış. İmzalanmışsa, geçerli değerler -ve şeklindedir +. 2'nin tamamlayıcısı önemli değil.
Lundin

5
@Lundin 1 bitlik bir ikili tamamlayıcı sayısının yalnızca iki olası değeri vardır. Bit ayarlanmışsa, işaret biti olduğu için -1'dir. Ayarlanmamışsa, o zaman "pozitif" 0 olur. Bunun uygulama tanımlı olduğunu biliyorum, sadece sonuçları en yaygın implantasyonu kullanarak açıklıyorum
NathanOliver

1
Buradaki anahtar daha ziyade, 2'nin tümleyeni veya herhangi bir başka imzalı formun tek bir bit ile çalışamayacağıdır.
Lundin

1
@JohnBollinger Bunu anlıyorum. Bu nedenle, bunun uygulamanın tanımlandığına dair feragat edene sahibim. En azından büyük 3 için hepsi intbu davada imzalanmış gibi davranıyor . Bit alanlarının bu kadar az belirtilmesi utanç verici. Temelde bu özellik burada, nasıl kullanılacağı konusunda derleyicinize danışın.
NathanOliver

1
@Lundin, standardın işaretli tamsayıların temsiline yönelik ifadesi, izin verilen üç alternatifin en az ikisinde sıfır değerli bitlerin olduğu durumu mükemmel bir şekilde ele alabilir. Bu , algoritmik bir yorumlama yerine bitleri işaretlemek için (negatif) yer değerleri atadığı için işe yarar.
John Bollinger

10

Bunu, 2'nin tamamlama sisteminde en soldaki bit işaret biti olarak düşünebilirsiniz. En soldaki bit kümesine sahip herhangi bir işaretli tamsayı bu nedenle negatif bir değerdir.

1 bitlik işaretli bir tamsayınız varsa, sadece işaret bitine sahiptir. Yani 1bu tek bite atama sadece işaret bitini ayarlayabilir. Dolayısıyla, tekrar okurken, değer negatif olarak yorumlanır ve dolayısıyla -1'dir.

1 bitlik işaretli bir tamsayının tutabileceği değerler -2^(n-1)= -2^(1-1)= -2^0= -1ve2^n-1= 2^1-1=0


8

Gereği C ++ standart n4713 , çok benzer bir kod parçacığını sağlanır. Kullanılan tür BOOL(özel), ancak her tür için geçerli olabilir.

12.2.4

4 Doğru veya yanlış değeribool, herhangi bir boyuttaki (bir bitlik alan dahil)tipteki bir bit alanına saklanırsa, orijinalbooldeğer ve bit alanının değeri eşit olarak karşılaştırılmalıdır. Numaralandırıcının değeri aynı numaralandırma türündeki bir bit alanında depolanırsa ve bit alanındaki bit sayısı, bu numaralandırma türünün (10.2) tüm değerlerini, orijinal numaralandırıcı değeri ve bit alanının değeri eşit karşılaştırılacaktır . [ Misal:

enum BOOL { FALSE=0, TRUE=1 };
struct A {
  BOOL b:1;
};
A a;
void f() {
  a.b = TRUE;
  if (a.b == TRUE)    // yields true
    { /* ... */ }
}

- son örnek]


İlk bakışta kalın kısım yoruma açık görünür. Bununla birlikte, doğru niyet açık hale enum BOOLtüretilir int.

enum BOOL : int { FALSE=0, TRUE=1 }; // ***this line
struct mystruct { BOOL enabled:1; };
int main()
{
  struct mystruct s;
  s.enabled = TRUE;
  if(s.enabled == TRUE)
    printf("Is enabled\n"); // --> we think this to be printed
  else
    printf("Is disabled !!\n");
}

Yukarıdaki kod ile şunların olmadığı bir uyarı verir -Wall -pedantic:

uyarı: 'mystruct :: enabled', 'enum BOOL'un tüm değerlerini tutamayacak kadar küçük struct mystruct { BOOL enabled:1; };

Çıktı:

Devre dışı bırakıldı !! (kullanırken enum BOOL : int)

Eğer enum BOOL : intbasit yapılır enum BOOL, daha sonra çıkış yukarıdaki standart pasage belirttiği gibidir:

Etkinleştirildi (kullanırken enum BOOL)


Bu nedenle, diğer birkaç yanıtın da sahip olduğu gibi, bu türün "1" değerini yalnızca tek bir bit alanında depolayacak kadar büyük olmadığı sonucuna varılabilir int.


0

Sizin görebildiğim bitfields anlayışınızda yanlış bir şey yok. Gördüğüm şey, benim yapımı ilk önce struct mystruct {int enabled: 1; } ve sonra struct mystruct s olarak; . Kodlamanız gereken şey şuydu:

#include <stdio.h>

struct mystruct { int enabled:1; };
int main()
{
    mystruct s; <-- Get rid of "struct" type declaration
    s.enabled = 1;
    if(s.enabled == 1)
        printf("Is enabled\n"); // --> we think this to be printed
    else
        printf("Is disabled !!\n");
}
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.