C ve C ++ 'da Sendikaların Amacı


254

Sendikaları daha önce rahatça kullandım; Ben okuduğumda bugün alarma edildi bu yazı ve bu kodun bilmek geldi

union ARGB
{
    uint32_t colour;

    struct componentsTag
    {
        uint8_t b;
        uint8_t g;
        uint8_t r;
        uint8_t a;
    } components;

} pixel;

pixel.colour = 0xff040201;  // ARGB::colour is the active member from now on

// somewhere down the line, without any edit to pixel

if(pixel.components.a)      // accessing the non-active member ARGB::components

aslında tanımlanmamış bir davranıştır. Sendikaların amaçlanan kullanımı bu değilse, nedir? Bazıları bunu ayrıntılı olarak açıklayabilir mi?

Güncelleme:

Birkaç şeyi açıklığa kavuşturmak istedim.

  • Sorunun cevabı C ve C ++ için aynı değildir; cahil genç ben onu hem C hem de C ++ olarak etiketledi.
  • C ++ 11'in standardını inceledikten sonra, aktif olmayan bir sendika üyesine erişmenin / denetlemenin tanımsız / belirtilmemiş / uygulama tanımlı olduğunu kesin olarak söyleyemedim. Bütün bulabildiğim §9.5 / 1 idi:

    Standart mizanpaj birliği, ortak bir başlangıç ​​sırasını paylaşan birkaç standart mizanpaj yapısı içeriyorsa ve bu standart mizanpajlı birleştirme türündeki bir nesne, standart mizanpaj yapılarından birini içeriyorsa, herhangi birinin ortak başlangıç ​​sırasının incelenmesine izin verilir standart yapı yapı üyelerinden oluşur. §9.2 / 19: Karşılık gelen üyelerin mizanpaj türlerine sahip olması ve her iki üyenin bir bit alanı olmaması veya her ikisinin de bir veya daha fazla başlangıç ​​sekansı için aynı genişliğe sahip bit alanları olması durumunda, iki standart yerleşim yapısı ortak bir başlangıç ​​sırasını paylaşır üyeler.

  • C'deyken ( C99 TC3 - DR 283 sonrası) bunu yapmak yasaldır ( Pascal Cuoq'a bunu getirdiği için teşekkürler ). Bununla birlikte, okunan değer okunduğu tür için geçersiz ("tuzak gösterimi" olarak adlandırılır) olursa, bunu yapmaya çalışmak tanımsız davranışlara yol açabilir . Aksi takdirde, okunan değer uygulama tarafından tanımlanır.
  • C89 / 90 bunu belirtilmemiş davranışlar altında ifade etti (Ek J) ve K & R'nin kitabı uygulamanın tanımlandığını söylüyor. K&R'den Alıntı:

    Birliğin amacı budur - çeşitli türlerden herhangi birini meşru bir şekilde tutabilen tek bir değişken. [...] kullanım tutarlı olduğu sürece: alınan tür en son kaydedilen tür olmalıdır. Şu anda bir sendikada hangi türün depolandığını takip etmek programcının sorumluluğundadır; bir şey bir tür olarak saklanır ve başka bir tür olarak çıkarılırsa sonuçlar uygulamaya bağlıdır.

  • Stroustrup'un TC ++ PL'sinden alıntı (vurgu mayını)

    Sendikaların kullanımı bazen "tip dönüşümü " için yanlış kullanılan verilerin [...] uyumluluğunda gerekli olabilir .

Her şeyden önce, bu soru (sorumu sorandan beri değişmeden kalan) sendikaların amacını anlamak niyetinde ve standardın neye izin verdiğine değil, örneğin kodun yeniden kullanımı için mirasın kullanılmasına elbette C ++ standardı tarafından izin verildiğini, ancak mirasın C ++ dil özelliği olarak kullanılmasının amacı ya da orijinal amacı değildi . Andrey'nin cevabı kabul edilen cevap olarak kalmaya devam etmesinin nedeni budur.


11
Basitçe ifade etmek gerekirse, derleyicilerin bir yapıdaki elemanlar arasına dolgu eklemesine izin verilir. Bu nedenle, b, g, r,ve abitişik olmayabilir ve bu nedenle bir düzenini eşleşmeyen uint32_t. Bu, başkalarının belirttiği Endianess sorunlarına ek olarak.
Thomas Matthews

8
Bu yüzden C ve C ++ sorularını etiketlememelisiniz. Yanıtlar farklıdır, ancak yanıt verenler hangi etiketi yanıtladıklarını bile söylemediklerinden (hatta biliyorlar mı?), Çöp alırsınız.
Pascal Cuoq

5
@downvoter Açıklamadığınız için teşekkürler, sihirli bir şekilde
yakınlığınızı anlamamı

1
Sahip asıl niyetini İlişkin birliği akılda, ayı yıllarca C standart sonrası tarih C sendikalar söyledi. Unix V7'ye hızlı bir bakış, sendikalar aracılığıyla birkaç tür dönüşümü gösterir.
ninjalj

3
scouring C++11's standard I couldn't conclusively say that it calls out accessing/inspecting a non-active union member is undefined [...] All I could find was §9.5/1...Gerçekten mi? paragrafın başındaki ana noktayı değil , bir istisna notu belirtirsiniz : "Bir birliktelikte, statik olmayan veri üyelerinden en fazla biri herhangi bir zamanda etkin olabilir, yani en fazla statik olmayan veri üyeleri herhangi bir zamanda bir birlik içinde saklanabilir. " - ve p4'e kadar: "Genel olarak, bir sendikanın aktif üyesini değiştirmek için açık yıkıcı çağrıları kullanmalı ve yeni operatörler yerleştirilmelidir "
underscore_d

Yanıtlar:


409

Sendikaların amacı oldukça açıktır, ancak bazı nedenlerden dolayı insanlar onu sık sık özlüyorlar.

Birleşmenin amacı, farklı nesneleri farklı zamanlarda saklamak için aynı bellek bölgesini kullanarak bellek tasarrufu yapmaktır. Bu kadar.

Oteldeki bir oda gibidir. Farklı insanlar üst üste binmeyen sürelerde yaşarlar. Bu insanlar asla buluşmazlar ve genellikle birbirleri hakkında hiçbir şey bilmezler. Odaların zaman paylaşımını düzgün bir şekilde yöneterek (yani, farklı insanların aynı anda bir odaya atanmadığından emin olarak), nispeten küçük bir otel, nispeten çok sayıda insana konaklama sağlayabilir, bu da oteller içindir.

Sendika tam da bunu yapıyor. Programınızdaki birkaç nesnenin çakışan değer ömürleri olmayan değerleri tuttuğunu biliyorsanız, bu nesneleri bir birleşim içinde "birleştirebilir" ve böylece bellek tasarrufu yapabilirsiniz. Tıpkı bir otel odasında her an için en fazla bir "aktif" kiracı olduğu gibi, bir birlik de program zamanının her anında en fazla bir "aktif" üyeye sahiptir. Yalnızca "aktif" üye okunabilir. Diğer üyeye yazarak "etkin" durumunu diğer üyeye geçirirsiniz.

Bir nedenden ötürü, birliğin bu orijinal amacı tamamen farklı bir şeyle "geçersiz kılındı": bir birliğin bir üyesini yazmak ve daha sonra başka bir üye aracılığıyla teftiş etmek. Bu tür bir bellek yeniden yorumu (diğer bir deyişle "tip punning") sendikaların geçerli bir kullanımı değildir. Genellikle tanımlanmamış davranış C89 / 90'da uygulama tanımlı davranış üretme olarak tanımlanır.

DÜZENLEME: Sendikaların punning tipi (yani bir üye yazma ve sonra başka bir okuma) amacıyla kullanılması için C99 standardına göre Teknik Düzeltmelerden birinde daha ayrıntılı bir tanım verilmiştir (bakınız DR # 257 ve DR # 283 ). Ancak, bunun bir tuzak temsilini okumaya çalışarak sizi tanımlanmamış davranışa girmekten resmi olarak korumadığını unutmayın.


37
Ayrıntılı olmak, basit bir pratik örnek vermek ve sendikaların mirası hakkında söylemek için +1!
legends2k

6
Bu cevapla ilgili sorun gördüğüm çoğu OS bu tam bir şey yapmak başlık dosyaları olmasıdır. Örneğin <time.h>, hem Windows hem de Unix'in eski (64 bit) sürümlerinde gördüm . Bu şekilde çalışan kodu anlamak için çağrılırsam, "geçerli değil" ve "tanımsız" olarak reddetmek gerçekten yeterli değildir.
TED

31
@AndreyT “Çok yakın zamana kadar tip kurnazlık için sendikalar kullanmak hiç yasal olmamıştı”: 2004 “çok yeni değil”, özellikle de başlangıçta sakarca ifade edilen ve tanımlanamayan sendikalar aracılığıyla tip-kurnazlık yaptığı ortaya çıkan C99 olduğu düşünüldüğünde. Gerçekte, sendikalar olsa da, sendikalar C89'da yasal, C11'de yasaldır ve komitenin yanlış ifadeleri düzeltmesi ve daha sonra TC3'ün serbest bırakılması 2004 yılına kadar sürmesine rağmen, C99'da yasaldı. open-std.org/jtc1/sc22/wg14/www/docs/dr_283.htm
Pascal Cuoq

6
@ legends2k Programlama dili standart olarak tanımlanmıştır. C99 standardının Teknik Düzeltme 3, sizi kendiniz için okumaya davet ettiğim 82 no'lu dipnotunda açık bir şekilde yazışmaya izin verir. Rock yıldızlarıyla röportaj yapılan ve iklim değişikliği hakkındaki görüşlerini ifade ettiği TV değil. Stroustrup'un görüşü, C standardının söyledikleri üzerinde sıfır etkiye sahiptir.
Pascal Cuoq

6
@ legends2k " Herhangi bir bireyin fikrinin önemli olmadığını biliyorum ve sadece standart " Derleyici yazarlarının görüşü (son derece zayıf) dil "belirtiminden" çok daha önemli.
curiousguy

38

Sendikaları, sendikanın hangi bileşeninin gerçekten kullanıldığını gösteren bir alan içeren aşağıdaki gibi yapılar oluşturmak için kullanabilirsiniz:

struct VAROBJECT
{
    enum o_t { Int, Double, String } objectType;

    union
    {
        int intValue;
        double dblValue;
        char *strValue;
    } value;
} object;

Tamamen katılıyorum, tanımlanmamış davranış kaosuna girmeden, belki de bu düşünebileceğim sendikaların en iyi tasarlanmış davranışıdır; ama olmaz atık alanı sadece söylemek, kullanıyorum zaman olduğu intveya char*nesnenin 10 öğeler için []; bu durumda, aslında her veri türü için VAROBJECT yerine ayrı yapılar bildirebilir miyim? Dağınıklığı azaltmaz ve daha az yer kaplamaz mı?
legends2k

3
efsaneler: Bazı durumlarda bunu yapamazsınız. Java'da Object kullandığınızda aynı durumlarda C'de VAROBJECT gibi bir şey kullanırsınız.
Erich Kitzmueller

Etiketli sendikaların veri yapısı, açıkladığınız gibi sendikaların tek meşru kullanımı gibi görünmektedir.
legends2k

Ayrıca değerlerin nasıl kullanılacağına dair bir örnek verin.
Ciro Santilli 法轮功 at 病 六四 事件 法轮功

1
@CiroSantilli ++ part might 六四 事件 法轮功C ++ Primer örneğinin bir kısmı yardımcı olabilir. wandbox.org/permlink/cFSrXyG02vOSdBk2
Rick

34

Davranış dil açısından tanımlanmamıştır. Farklı platformların bellek hizalaması ve endianness için farklı kısıtlamaları olabileceğini düşünün. Büyük bir endian ile küçük bir endian makinesinin kodu yapıdaki değerleri farklı şekilde güncelleyecektir. Dildeki davranışı düzeltmek, tüm uygulamaların kullanımı sınırlandıran (ve bellek hizalama kısıtlamalarını ...) kullanmasını gerektirir.

C ++ kullanıyorsanız (iki etiket kullanıyorsunuz) ve taşınabilirliği gerçekten önemsiyorsanız, yalnızca yapıyı kullanabilir uint32_tve alanları alan ve bitmask işlemleriyle uygun şekilde ayarlayan bir ayarlayıcı sağlayabilirsiniz . Aynı şey bir fonksiyonla C de yapılabilir.

Düzenleme : AProgrammer'ın oy vermek ve kapatmak için bir cevap yazmasını bekliyordum. Bazı yorumların belirttiği gibi, her uygulamanın ne yapılacağına karar vermesine izin vererek standardın diğer bölümlerinde endianness ele alınır ve hizalama ve dolgu da farklı şekilde ele alınabilir. Şimdi, AProgrammer'ın örtük olarak ifade ettiği katı takma kuralları burada önemli bir noktadır. Derleyicinin değişkenlerin değiştirilmesi (veya değiştirilmemesi) hakkında varsayımlarda bulunmasına izin verilir. Birleşme durumunda, derleyici talimatları yeniden sıralayabilir ve her renk bileşeninin okumasını yazma üzerine renk değişkenine taşıyabilir.


Açık ve basit cevap için +1! Taşınabilirlik için 2. paragrafta verdiğiniz yöntemin iyi olduğunu kabul ediyorum; ancak kodum tek bir mimariye bağlıysa (protability fiyatını ödeyerek) soruya koyduğum şekilde kullanabilir miyim, çünkü her piksel değeri için 4 bayt kaydeder ve bu işlevi çalıştırırken biraz zaman kazandırır ?
legends2k

Endian sorunu, standardı tanımlanmamış davranış olarak bildirmeye zorlamaz - reinterpret_cast aynı endian sorunlarına sahiptir, ancak uygulama tanımlı davranışa sahiptir.
JoeG

1
@ legends2k, sorun şu ki, optimizer bir uint32_t'nin bir uint8_t'ye yazarak değiştirilmediğini varsayabilir ve bu nedenle optimize edilmiş bu varsayımı kullandığınızda yanlış değer elde edersiniz ... @Joe, tanımlanmamış davranış, işaretçi (biliyorum, bazı istisnalar var).
AProgrammer

1
@ legends2k / AProgrammer: reinterpret_cast'in sonucu uygulama tanımlıdır. Döndürülen işaretçiyi kullanmak, yalnızca uygulama tanımlı davranışta tanımlanmamış davranışla sonuçlanmaz. Başka bir deyişle, davranış tutarlı ve tanımlanmış olmalıdır, ancak taşınabilir değildir.
JoeG

1
@ legends2k: herhangi bir iyi optimizer, bir baytın tamamını seçen ve baytın okunması / yazılması için birleştirme ile aynı fakat iyi tanımlanmış (ve taşınabilir) kod üreten bitsel işlemleri tanıyacaktır. örneğin uint8_t getRed () const {dönüş rengi & 0x000000FF; } void setRed (uint8_t r) {color = (renk & ~ 0x000000FF) | r; }
Ben Voigt

22

Düzenli olarak karşılaştığım en yaygın kullanım takma addır .union

Aşağıdakileri göz önünde bulundur:

union Vector3f
{
  struct{ float x,y,z ; } ;
  float elts[3];
}

Bu ne yapar? Bir Vector3f vec;üyeye şu iki adla da temiz ve düzenli erişim sağlar :

vec.x=vec.y=vec.z=1.f ;

veya diziye tamsayı erişimi ile

for( int i = 0 ; i < 3 ; i++ )
  vec.elts[i]=1.f;

Bazı durumlarda, ada göre erişim yapabileceğiniz en net şeydir. Diğer durumlarda, özellikle eksen programlı olarak seçildiğinde, eksene sayısal endeksle erişmek için - x için 0, y için 1 ve z için 2.


3
Bu aynı zamanda type-punningsoruda da belirtilmiştir. Sorudaki örnek de benzer bir örneği göstermektedir.
legends2k

4
Bu tür bir kurnazlık değil. Örneğimde, türler eşleşiyor , bu yüzden "pun" yok, sadece takma ad.
bobobobo

3
Evet, ancak yine de, dil standardının mutlak bakış açısından, yazılan ve okunan üye farklıdır, bu da soruda belirtildiği gibi tanımlanmamıştır.
legends2k

3
Gelecekteki bir standardın, bu özel durumu "ortak ilk alt sıra" kuralı altında izin vermesini düzeltecağını umuyorum. Ancak, diziler mevcut ifadeye göre bu kurala katılmazlar.
Ben Voigt

3
@curiousguy: Yapı elemanlarının keyfi dolgu olmadan yerleştirilmesine kesinlikle gerek yoktur. Yapı-üye yerleşimi veya yapı boyutu için kod testleri, erişim doğrudan sendika üzerinden yapılırsa kod çalışmalıdır, ancak Standardın sıkı bir şekilde okunması, bir birleşim veya yapı üyesinin adresini almanın kullanılamayan bir işaretçi verdiğini gösterir. kendi türünde bir işaretçi olarak, ancak önce ek türü veya bir karakter türüne geri döndürülmesi gerekir. Uzaktan çalıştırılabilir herhangi bir derleyici, daha fazla şey çalıştırarak dili genişletir ...
supercat

10

Söylediğiniz gibi, bu kesinlikle tanımlanmamış bir davranıştır, ancak birçok platformda "çalışacaktır". Sendikaları kullanmanın asıl nedeni değişken kayıtlar oluşturmaktır.

union A {
   int i;
   double d;
};

A a[10];    // records in "a" can be either ints or doubles 
a[0].i = 42;
a[1].d = 1.23;

Tabii ki, varyantın gerçekten ne içerdiğini söylemek için bir çeşit ayrımcıya da ihtiyacınız var. Ve C ++ sendikalarında çok fazla işe yaramadığına dikkat edin, çünkü sadece POD tiplerini içerebilirler - etkili bir şekilde kurucu ve yıkıcı olmayanlar.


Böylece (sorudaki gibi) kullandınız mı ?? :)
legends2k

Biraz bilgiçlik, ama ben "varyant kayıtları" tam olarak kabul etmiyorum. Yani, eminim akıllarındaydılar, ancak bir öncelik olsaydı neden onlara vermiyorsunuz? “Yapı taşını sağlayın çünkü başka şeyler de inşa etmek faydalı olabilir” sadece sezgisel olarak daha muhtemel görünüyor. Özellikle muhtemelen aklında oldu en azından bir uygulamamız daha verilmiş - bellek giriş ve çıkış kayıtları (örtüşen iken) kendi isimleri ile farklı varlıklardır G / Ç kayıtlarını, vs. tiplerini eşlenen
Steve314

@ Stev314 Akıllarında kullandıkları kullanım buysa, tanımsız davranışlar haline getirmemiş olabilirler.

@Neil: Tanımlanmamış davranışa çarpmadan gerçek kullanım hakkında ilk söylenen +1. Sanırım onlar diğer tip punning işlemleri (reinterpret_cast, vb.) Gibi tanımlanmış uygulama yapmış olabilir. Ama sorduğum gibi, onu yazlık olarak kullandın mı?
legends2k

@Neil - bellek eşlemeli kayıt örneği tanımsız değil, normal endian / etc bir kenara bırakılmış ve bir "geçici" bayrak verilmiştir. Bu modeldeki bir adrese yazmak, aynı adresi okumakla aynı yazmacıya gönderme yapmaz. Bu nedenle, geri okumadığınız için "ne okuyorsunuz" sorunu yoktur - bu adrese yazdığınız her çıktı, okuduğunuzda sadece bağımsız bir girdi okursunuz. Tek sorun, birleşimin giriş tarafını okuduğunuzdan ve çıkış tarafını yazdığınızdan emin olmaktır. Gömülü şeyler yaygındı - muhtemelen hala.
Steve314

8

C'de, bir varyant gibi bir şeyi uygulamak için güzel bir yoldu.

enum possibleTypes{
  eInt,
  eDouble,
  eChar
}


struct Value{

    union Value {
      int iVal_;
      double dval;
      char cVal;
    } value_;
    possibleTypes discriminator_;
} 

switch(val.discriminator_)
{
  case eInt: val.value_.iVal_; break;

Küçük bellek zamanlarında bu yapı tüm üyeye sahip bir yapıdan daha az bellek kullanır.

Bu arada C,

    typedef struct {
      unsigned int mantissa_low:32;      //mantissa
      unsigned int mantissa_high:20;
      unsigned int exponent:11;         //exponent
      unsigned int sign:1;
    } realVal;

bit değerlerine erişmek için.


Her iki örneğiniz de standartta mükemmel bir şekilde tanımlanmış olsa da; ama, hey, bit alanlarını kullanmak emin edilemez kod vuruldu, değil mi?
legends2k

Hayır değil. Bildiğim kadarıyla yaygın olarak destekleniyor.
ta Totonga

1
Derleyici desteği taşınabilir hale gelmez. C Kitabı : C (böylece C ++) , makine sözcükleri içindeki alanların sıralanması konusunda hiçbir garanti vermez, bu nedenle bunları ikinci nedenle kullanırsanız, program yalnızca taşınabilir değil, derleyiciye de bağımlı olacaktır.
efsane2k

5

Bu kesinlikle tanımlanmamış bir davranış olmasına rağmen, pratikte hemen hemen her derleyici ile çalışacaktır. Bu kadar yaygın kullanılan bir paradigma, kendine saygılı herhangi bir derleyicinin bu gibi durumlarda "doğru olanı" yapması gerekecektir. Kesinlikle bazı derleyicilerle kırık kod üretebilecek tip-punning tercih edilmelidir.


2
Endian sorunu yok mu? "Undefined" ile karşılaştırıldığında nispeten kolay bir düzeltme, ancak bazı projeler için dikkate alınmaya değer.
Steve314

5

C ++ 'da, Boost Variant , tanımlanmamış davranışı mümkün olduğunca önlemek için tasarlanmış birliğin güvenli bir sürümünü uygular.

Performansları yapı ile aynıdır enum + union(yığın ayrılmış vb.) Ancak enum:) yerine türlerin bir şablon listesini kullanır.


5

Davranış tanımsız olabilir, ancak bu sadece bir "standart" olmadığı anlamına gelir. Tüm iyi derleyiciler , paketleme ve hizalamayı kontrol etmek için #pragmalar sunar, ancak farklı varsayılanlara sahip olabilirler. Varsayılanlar, kullanılan optimizasyon ayarlarına bağlı olarak da değişecektir.

Ayrıca sendikalar sadece yer kazanmak için değil . Tip punning ile modern derleyicilere yardımcı olabilirler. Eğer reinterpret_cast<>her şeyi yaparsanız , derleyici ne yaptığınız hakkında varsayımlarda bulunamaz. Türünüz hakkında bildiklerini atmak ve tekrar başlamak zorunda kalabilir (bu günleri CPU saat hızına kıyasla çok verimsiz olan belleğe yazmayı zorlamak).


4

Teknik olarak tanımlanmamıştır, ancak gerçekte çoğu (tümü?) Derleyiciler bunu reinterpret_castbir türden diğerine kullanmakla aynı şekilde ele alır , bunun sonucu uygulama olarak tanımlanır. Mevcut kodunuzda uykunuzu kaybetmezdim.


msgstr " bir türden diğerine reinterpret_cast, sonucu uygulama tanımlı. " Hayır, öyle değil. Uygulamaların onu tanımlaması gerekmez ve çoğu bunu tanımlamaz. Ayrıca, bir işaretçiye rastgele bir değer verme izin verilen uygulama tanımlı davranışı ne olurdu?
curiousguy

4

Sendikaların gerçek kullanımına bir örnek daha için, CORBA çerçevesi nesneleri etiketli birleşim yaklaşımını kullanarak serileştirir. Tüm kullanıcı tanımlı sınıflar bir (dev) birliğin üyesidir ve bir tamsayı tanımlayıcısı demarshaller'a birliği nasıl yorumlayacağını söyler.


4

Diğerleri mimari farklılıklardan bahsettiler (küçük - büyük endian).

Değişkenler için bellek paylaşıldığından, birisine yazarak diğerlerinin değiştiği ve türlerine bağlı olarak değerin anlamsız olabileceği problemini okudum.

Örneğin. birlik {float f; int i; } x;

Eğer xf'den okursanız, xi'ye yazmak anlamsız olurdu - şamandıranın işaretine, üssüne veya mantis bileşenlerine bakmak için amaçladığınız sürece.

Ben de bir hizalama sorunu olduğunu düşünüyorum: Bazı değişkenler kelime hizalanmış olması gerekiyorsa o zaman beklenen sonucu elde olmayabilir.

Örneğin. birlik {char c [4]; int i; } x;

Varsayımsal olarak, bazı makinelerde bir karakterin kelime hizalaması gerekiyorsa, c [0] ve c [1], depolamayı i [c] [2] ve c [3] ile paylaşamaz, ancak paylaşamazdı.


Sözcük hizalanması gereken bir bayt mı? Bu hiç mantıklı değil. Bir baytın tanım gereği hizalama gereksinimi yoktur.
curiousguy

Evet, muhtemelen daha iyi bir örnek kullanmalıydım. Teşekkürler.
philcolbourn

@curiousguy: Bayt dizilerinin kelime hizalaması olmasını isteyebileceğiniz birçok durum vardır. Birinin örneğin 1024 baytlık birçok dizisi varsa ve sık sık diğerini kopyalamak isterse, sözcüklerin hizalanması birçok sistemde a'nın hızını memcpy()birinden diğerine iki katına çıkarabilir. Bazı sistemler bu ve diğer nedenlerle yapıların / birliklerin dışında meydana gelenchar[] tahsisleri spekülatif olarak hizalayabilir . Mevcut örnekte, öğelerinin tümü için üst üste binecek varsayımı taşınabilir değildir, ancak bunun bir garantisi yoktur . ic[]sizeof(int)==4
supercat

4

1974'te belgelendiği şekliyle C dilinde, tüm yapı üyeleri ortak bir ad alanını paylaştı ve "ptr-> üye" nin anlamı , üyenin "ptr" e yer değiştirmesini eklemek ve üye türünü kullanarak ortaya çıkan adrese erişmek olarak tanımlandı . Bu tasarım, farklı yapı tanımlarından alınan üye isimleriyle aynı ptr'in kullanılmasını mümkün kılmıştır, ancak aynı ofset ile; programcılar bu yeteneği çeşitli amaçlar için kullandılar.

Yapı üyelerine kendi ad alanları verildiğinde, aynı yer değiştirmeye sahip iki yapı üyesi beyan etmek imkansız hale geldi. Dile sendika eklemek, dilin önceki sürümlerinde mevcut olan aynı semantiğe ulaşmayı mümkün kılmıştır (yine de kapalı bir bağlama aktarılan adların bulunamaması, foo-> üyesini değiştirmek için bir bul / değiştir kullanarak gerekli olabilir. foo-> type1.member). Önemli olan, sendikaları ekleyen insanların akıllarında herhangi bir belirli hedef kullanımı olduğu kadar çok değildi, aksine, önceki semantiğe güvenen programcıların, hangi amaç için olursa olsun , yapmak için farklı bir sözdizimi kullanmak zorunda olsalar bile aynı anlambilim.


Tarih dersini takdir edin, ancak K & R kitabının tek "standart" olduğu geçmiş C döneminde böyle olmayan ve tanımlanmayan standart tanımlayan standart ile, herhangi bir amaç için kullanmadığınızdan emin olmak gerekir ve UB topraklarına girin.
legends2k

2
@ legends2k: Standart yazıldığında, C uygulamalarının çoğu sendikalara aynı şekilde muamele etti ve bu tür bir tedavi faydalı oldu. Ancak, birkaçı bunu yapmadı ve Standardın yazarları mevcut uygulamaları "uygunsuz" olarak markalamaktan nefret ediyorlardı. Bunun yerine, uygulayıcıların Standart'a bir şey yapmalarını ( zaten yaptıkları gerçeği ile kanıtlandığı gibi) söylemeleri gerekmediği takdirde , belirtilmemiş veya tanımsız bırakılmasının statükoyu koruyacağını düşündüler . Standart yazılmadan önce işleri daha az tanımlaması gerektiği fikri ...
supercat

2
... çok daha yeni bir yenilik gibi görünüyor. Tüm bunlar için özellikle üzücü olan, ileri teknoloji uygulamaları hedefleyen derleyici yazarlarının, yalnızca " "Uygulamaların% 90'ı, sonuç hiper-modern C'den daha iyi ve daha güvenilir performans gösterebilecek bir dil olacaktır
supercat

2

Sen edebilirsiniz kullanmak iki ana nedenden dolayı aa birliği:

  1. Örneğinizde olduğu gibi aynı verilere farklı şekillerde erişmenin kullanışlı bir yolu
  2. Sadece bir tanesi 'aktif' olabilecek farklı veri üyeleri olduğunda yerden tasarruf etmenin bir yolu

1 Hedef sistemin bellek mimarisinin nasıl çalıştığını bildiğiniz temelde kısayol yazma koduna C tarzı bir kesmek daha. Daha önce de belirtildiği gibi, aslında birçok farklı platformu hedeflemezseniz normalde bundan kurtulabilirsiniz. Bazı derleyicilerin paketleme direktiflerini de kullanmanıza izin verebileceğine inanıyorum (yapılarda yaptıklarını biliyorum)?

COM için yaygın olarak kullanılan VARIANT tipinde 2. iyi bir örnek bulunabilir .


2

Diğerlerinin de belirttiği gibi, numaralandırmalarla birleştirilmiş ve yapılara sarılmış sendikalar etiketli sendikaları uygulamak için kullanılabilir. Pratik bir kullanım, Result<T, E>başlangıçta saf kullanılarak uygulanan Rust'ları uygulamaktır enum(Rust, numaralandırma varyantlarında ek veriler tutabilir). İşte bir C ++ örneği:

template <typename T, typename E> struct Result {
    public:
    enum class Success : uint8_t { Ok, Err };
    Result(T val) {
        m_success = Success::Ok;
        m_value.ok = val;
    }
    Result(E val) {
        m_success = Success::Err;
        m_value.err = val;
    }
    inline bool operator==(const Result& other) {
        return other.m_success == this->m_success;
    }
    inline bool operator!=(const Result& other) {
        return other.m_success != this->m_success;
    }
    inline T expect(const char* errorMsg) {
        if (m_success == Success::Err) throw errorMsg;
        else return m_value.ok;
    }
    inline bool is_ok() {
        return m_success == Success::Ok;
    }
    inline bool is_err() {
        return m_success == Success::Err;
    }
    inline const T* ok() {
        if (is_ok()) return m_value.ok;
        else return nullptr;
    }
    inline const T* err() {
        if (is_err()) return m_value.err;
        else return nullptr;
    }

    // Other methods from https://doc.rust-lang.org/std/result/enum.Result.html

    private:
    Success m_success;
    union _val_t { T ok; E err; } m_value;
}
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.