Bir Yapı ve Birlik Arasındaki Fark


411

A structve a arasındaki farkı vermek için iyi bir örnek var mı union? Temelde struct, üyesinin tüm belleğini ve unionen büyük üye bellek alanını kullandığını biliyorum . İşletim sistemi düzeyinde başka bir fark var mı?

Yanıtlar:


677

Bir birleşimde, yalnızca bir yerden kullanmanız gerekir, çünkü hepsi aynı yerde depolanır. Bu, çeşitli türlerden biri olabilecek bir şeyi saklamak istediğinizde kullanışlı hale getirir. Öte yandan, bir yapı, elemanlarının her biri için ayrı bir hafıza konumuna sahiptir ve hepsi aynı anda kullanılabilir.

Kullanımlarına somut bir örnek vermek için, bir süre önce bir Şema yorumlayıcısı üzerinde çalışıyordum ve aslında Şema veri türlerini C veri türlerine bindiriyordum. Bu, değerin türünü ve bu değeri saklamak için birliği belirten bir enumun bir yapıda depolanmasını içeriyordu.

union foo {
  int a;   // can't use both a and b at once
  char b;
} foo;

struct bar {
  int a;   // can use both a and b simultaneously
  char b;
} bar;

union foo x;
x.a = 3; // OK
x.b = 'c'; // NO! this affects the value of x.a!

struct bar y;
y.a = 3; // OK
y.b = 'c'; // OK

edit: Eğer xb'yi 'c' olarak ayarlamanın xa değerini değiştirdiğini merak ediyorsanız, teknik olarak tanımsızdır. Çoğu modern makinede bir karakter 1 bayt ve bir int 4 bayttır, bu nedenle xb'ye 'c' değeri vermek de xa'nın ilk baytını aynı değerle verir:

union foo x;
x.a = 3;
x.b = 'c';
printf("%i, %i\n", x.a, x.b);

baskılar

99, 99

İki değer neden aynı? İnt 3'ün son 3 baytı sıfır olduğu için 99 olarak da okunur. Xa için daha büyük bir sayı koyarsak, bunun her zaman böyle olmadığını görürsünüz:

union foo x;
x.a = 387439;
x.b = 'c';
printf("%i, %i\n", x.a, x.b);

baskılar

387427, 99

Gerçek bellek değerlerine daha yakından bakmak için, değerleri onaltılı olarak yazıp basalım:

union foo x;
x.a = 0xDEADBEEF;
x.b = 0x22;
printf("%x, %x\n", x.a, x.b);

baskılar

deadbe22, 22

0x22'nin 0xEF'in üzerine nerede yazdığını açıkça görebilirsiniz.

FAKAT

C'de, int içindeki baytların sırası tanımlanmamıştır.Bu program Mac'imde 0xEF'in üzerine 0xEF yazdı, ancak int'i oluşturan baytların sırası tersine çevrildiğinden bunun yerine 0xDE'nin üzerine yazacağı başka platformlar da var. Bu nedenle, bir program yazarken, bir birlikteki belirli verilerin üzerine yazma davranışına asla güvenmemelisiniz, çünkü taşınabilir değildir.

Baytların sıralaması hakkında daha fazla bilgi için endianiteye göz atın .


1
bu örneği kullanarak, birleşimde, xb = 'c' ise xa'da ne saklanır? karakter referans numarası mı?
kylex

1
Umarım bu xb'yi ayarladığınızda xa'da nelerin depolandığını daha ayrıntılı olarak açıklar
Kyle Cronin

1
@KyleCronin Sanırım anladım. Sizin durumunuzda, bir tür grubunuz var, sadece birini kullanmanız gerektiğini biliyorsunuz, ancak çalışma zamanına kadar hangisini bilmiyorsunuz - böylece sendika bunu yapmanıza izin veriyor. Teşekkürler
user12345613 21:12

2
@ user12345613 sendikalar yapılar için bir tür temel sınıf olarak kullanılabilir. Yapı birlikleri kullanarak bir OO hiyerarşisini taklit edebilirsiniz
Morten Jensen

1
@ Çok baytlı tiplerde Lazar Bayt sırası endianiteye bağlıdır. Üzerinde Wikipedia makalesini okumanızı tavsiye ederim.
Kyle Cronin

83

Kısa cevap şudur: Bir yapı kayıt yapısıdır: yapıdaki her öğe yeni alan ayırır. Yani,

struct foobarbazquux_t {
    int foo;
    long bar;
    double baz; 
    long double quux;
}

(sizeof(int)+sizeof(long)+sizeof(double)+sizeof(long double))her örnek için bellekte en az bayt ayırır. ("En azından" çünkü mimari hizalama kısıtlamaları derleyiciyi yapıyı doldurmaya zorlayabilir.)

Diğer yandan,

union foobarbazquux_u {
    int foo;
    long bar;
    double baz; 
    long double quux;
}

bir yığın bellek ayırır ve dört takma ad verir. Yani sizeof(union foobarbazquux_u) ≥ max((sizeof(int),sizeof(long),sizeof(double),sizeof(long double)), yine hizalamalar için bazı ekleme olasılığı ile.


53

'Yapı' ve 'birlik' arasındaki farkı vermek için iyi bir örnek var mı?

Hayali bir iletişim protokolü

struct packetheader {
   int sourceaddress;
   int destaddress;
   int messagetype;
   union request {
       char fourcc[4];
       int requestnumber;
   };
};

Bu hayali protokolde, "ileti türüne" dayanarak, başlıktaki aşağıdaki konumun ya bir istek numarası ya da dört karakterli bir kod olacağı ancak her ikisinin birden olmayacağı ayrılmıştır. Kısacası, sendikalar aynı depolama konumunun birden fazla veri türünü temsil etmesine izin verir, burada herhangi bir zamanda yalnızca veri türlerinden birini saklamak isteyeceğiniz garanti edilir.

Sendikalar, C'nin bir sistem programlama dili olarak mirasını temel alan düşük seviyeli bir ayrıntıdır ve burada "çakışan" depolama konumlarının bazen bu şekilde kullanılır. Bazen, aynı anda birkaç türden yalnızca birinin kaydedileceği bir veri yapınızın olduğu yerden bellek kaydetmek için sendikaları kullanabilirsiniz.

Genel olarak, işletim sistemi yapıları ve sendikaları umursamıyor veya bilmiyor - ikisi de sadece onun için bellek blokları. Yapı, bu nesnelerin örtüşmediği birkaç veri nesnesini depolayan bir bellek bloğudur. Birleşme, birkaç veri nesnesini depolayan, ancak bunların en büyüğü için yalnızca depolama alanına sahip olan ve böylece herhangi bir anda veri nesnelerinden birini depolayabilen bir bellek bloğudur.


1
Evet. Bu bir kullanım durumunu iyi açıklar!
gideon

1
varsayalım, packetheader ph;requestnumber'a nasıl erişiyorsunuz? ph.request.requestnumber?
justin.m.chase

En iyi açıklama! Teşekkürler.
84RR1573R

39

Sorunuzu zaten devlet olarak, arasındaki temel fark unionve structolmasıdır unionederken üyeleri, bir birliğin sizeof biri olduğunu bu yüzden birbirlerine hafızasını bindirmek structüyeleri arasında (opsiyonel dolgu ile) birbirlerine ardına yerleştirilir. Ayrıca bir birlik, tüm üyelerini içerecek kadar büyüktür ve tüm üyelerine uyan bir hizalamaya sahiptir. Diyelim ki intsadece 2 baytlık adreslerde ve 2 bayt genişliğinde, uzun ise sadece 4 baytlık adreslerde ve 4 bayt uzunluğunda saklanabilir. Aşağıdaki birlik

union test {
    int a;
    long b;
}; 

Bir olabilirdi sizeof4 ve 4. Bir birliğin Hem bir hizalama icabı ve yapı onların başında sonunda dolgu var, ama olamaz. Bir yapıya yazmak yalnızca üzerine yazılan üyenin değerini değiştirir. Birliğin bir üyesine yazmak, diğer tüm üyelerin değerini geçersiz kılacaktır. Onlara daha önce yazmadıysanız bunlara erişemezsiniz, aksi takdirde davranış tanımsızdır. GCC, son zamanlarda yazmamış olsanız bile, bir sendika üyelerinden gerçekten okuyabileceğiniz bir uzantıdır. İşletim Sistemi için, bir kullanıcı programının bir birliğe veya bir yapıya yazması önemli değildir. Aslında bu sadece derleyicinin meselesidir.

Birlik ve yapının bir diğer önemli özelliği, onlara bir işaretçinin herhangi bir üye türünü işaret edebilmesine izin vermesidir . Yani aşağıdakiler geçerlidir:

struct test {
    int a;
    double b;
} * some_test_pointer;

some_test_pointer işaret edebilir int*ya da double*. Eğer türünde bir adres döküm Eğer testiçin int*, onun ilk üyesi işaret eder aaslında. Aynısı sendika için de geçerlidir. Böylece, bir birlik her zaman doğru hizalamaya sahip olacağından, bir türün işaretini geçerli kılmak için bir birlik kullanabilirsiniz:

union a {
    int a;
    double b;
};

Bu birlik aslında bir int ve bir çifte işaret edebilir:

union a * v = (union a*)some_int_pointer;
*some_int_pointer = 5;
v->a = 10;
return *some_int_pointer;    

aslında C99 standardında belirtildiği gibi geçerlidir:

Bir nesnenin depolanmış değerine yalnızca aşağıdaki türlerden birine sahip bir değer değeri ifadesiyle erişilir:

  • nesnenin etkili türüyle uyumlu bir tür
  • ...
  • üyeleri arasında yukarıda belirtilen türlerden birini içeren bir toplu veya birleşim türü

Derleyici v->a = 10;, değerini etkileyebileceği için optimize edilmeyecektir *some_int_pointer(ve işlev 10bunun yerine dönecektir 5).


18

A union, birkaç senaryoda yararlıdır. unionbir çekirdek için aygıt sürücüleri yazmak gibi çok düşük düzeyli manipülasyon için bir araç olabilir.

Buna bir örnek bir floatsayıyı kullanarakunion a structbit alanlara ve birlikte float. Ben bir numara kaydetmek floatve daha sonra ben belirli kısımlarını erişebilirsiniz floato aracılığıyla struct. Örnek, unionverilere bakmak için farklı açıların nasıl kullanıldığını gösterir .

#include <stdio.h>                                                                                                                                       

union foo {
    struct float_guts {
        unsigned int fraction : 23;
        unsigned int exponent : 8;
        unsigned int sign     : 1;
    } fg;
    float f;
};

void print_float(float f) {
    union foo ff;
    ff.f = f;
    printf("%f: %d 0x%X 0x%X\n", f, ff.fg.sign, ff.fg.exponent, ff.fg.fraction);

}

int main(){
    print_float(0.15625);
    return 0;
}

Vikipedi ile ilgili tek bir açıklamaya göz atın . Oradan örnek ve büyü numarası 0.15625'i kullandım.


unionçoklu alternatifleri olan bir cebirsel veri türünü uygulamak için de kullanılabilir. O'Sullivan, Stewart ve Goerzen'in "Gerçek Dünya Haskell" kitabında buna bir örnek buldum. Bunu göz atın ayrımcılığa birlik bölümünde.

Şerefe!


11

" birlik " ve " yapı " C dilinin yapılarıdır . Aralarında bir "İşletim Sistemi düzeyi" farkından bahsetmek uygun değildir, çünkü bir veya başka bir anahtar kelime kullanırsanız farklı kod üreten derleyici budur.


11

Teknik olarak konuşmayanlar:

Varsayım: sandalye = hafıza bloğu, insanlar = değişken

Yapı : 3 kişi varsa, buna karşılık olarak kendi boyutlarında sandalyeye oturabilirler.

Sendika : 3 kişi varsa sadece bir oturmak için orada sandalye olacak, oturmak istediklerinde hepsinin aynı sandalyeyi kullanması gerekiyor.

Teknik olarak konuşmak şu anlama gelir:

Aşağıdaki program birlikte yapıya ve birliğe derinlemesine bir dalış yapmaktadır.

struct MAIN_STRUCT
{
UINT64 bufferaddr;   
union {
    UINT32 data;
    struct INNER_STRUCT{
        UINT16 length;  
        UINT8 cso;  
        UINT8 cmd;  
           } flags;
     } data1;
};

Toplam MAIN_STRUCT boyutu = bufferaddr için sizeof (UINT64) + birleşim için sizeof (UNIT32) + dolgu için 32 bit (işlemci mimarisine bağlıdır) = 128 bit. Yapı için tüm üyeler bellek bloğunu bitişik olarak alır.

Birlik, maksimum boyut üyesinden bir bellek bloğu alır (İşte 32 bit). Birlik içinde bir yapı daha var (INNER_STRUCT) üyeleri toplam 32 bitlik (16 + 8 + 8) bir hafıza bloğu alır. Bağlantı halinde INNER_STRUCT (32 bit) üye veya verilere (32 bit) erişilebilir.


Harika bir açıklama. Şerefe!
Prem

11

Evet, yapı ve sendika arasındaki temel fark belirttiğiniz ile aynıdır. Struct, üyesinin tüm belleğini kullanır ve sendika en büyük üyelerin bellek alanını kullanır.

Ancak tüm fark, belleğin kullanım ihtiyacından kaynaklanmaktadır. Birliğin en iyi kullanımı, sinyalleri kullandığımız unix süreçlerinde görülebilir. bir süreç her seferinde sadece bir sinyal üzerinde etkili olabilir. Böylece genel beyan şöyle olacaktır:

union SIGSELECT
{
  SIGNAL_1 signal1;
  SIGNAL_2 signal2;
  .....
};

Bu durumda, işlem tüm sinyallerin yalnızca en yüksek belleğini kullanır. ancak bu durumda struct kullanırsanız, bellek kullanımı tüm sinyallerin toplamı olacaktır. Çok fark yaratıyor.

Özetlemek gerekirse, üyelerden herhangi birine aynı anda eriştiğinizi biliyorsanız Birlik seçilmelidir.


10

Sende var, hepsi bu. Ama temelde sendikaların anlamı nedir?

Farklı konumlarda aynı konum içeriğini koyabilirsiniz. Yapmak zorunda biliyorum sen birliğe sakladığınız ne tür (sık sık bir koy struct... bir tür etiketiyle).

Bu neden önemli? Gerçekten yer kazanmak için değil. Evet, biraz bit kazanabilir veya biraz dolgu yapabilirsiniz, ancak artık ana nokta bu değil.

Tür güvenliği içindir, bir çeşit 'dinamik yazım' yapmanızı sağlar: derleyici içeriğinizin farklı anlamları olabileceğini ve çalışma zamanında size nasıl yorum yaptığının kesin anlamını biliyor olabilir. Farklı türlere işaret edebilen bir işaretçiniz varsa, bir birleşim KULLANMALISINIZ, aksi takdirde takma sorunları nedeniyle kodunuz yanlış olabilir (derleyici kendi kendine "oh, yalnızca bu işaretçi bu türe işaret edebilir, bu yüzden optimize edebilirim ... ve kötü şeyler olabilir).


9

Bir yapı, içindeki tüm öğelerin toplam boyutunu tahsis eder.

Birlik yalnızca en büyük üyenin gerektirdiği kadar bellek ayırır.


2
Ayrıca sendika üyelerinin, hepsinin ayrılan sendika "yapısının" başlangıç ​​adresinden başlaması için birbirini "kapladığını" da eklemek isteyebilirsiniz.
Jim Buck

4

structure ve union arasındaki fark nedir?

Kısayol cevabı: Gecikme bellek tahsisinde. Açıklama: Yapıda, yapı içindeki tüm üyeler için bellek alanı oluşturulacaktır. Birlik içinde bellek alanı yalnızca en büyük bellek alanına ihtiyaç duyan bir üye için oluşturulacaktır. Aşağıdaki kodu göz önünde bulundurun:

struct s_tag
{
   int a; 
   long int b;
} x;

union u_tag
{
   int a; 
   long int b;
} y;

Burada yapı ve birlik içinde iki üye vardır: int ve long int. İnt için bellek alanı: 4 bayt ve uzun int için bellek alanı: 32 bit işletim sisteminde 8.

Böylece yapı 4 + 8 = 12 bayt, sendika için 8 bayt oluşturulacak

Kod örneği:

#include<stdio.h>
struct s_tag
{
  int a;
  long int b;
} x;
union u_tag
{
     int a;
     long int b;
} y;
int main()
{
    printf("Memory allocation for structure = %d", sizeof(x));
    printf("\nMemory allocation for union = %d", sizeof(y));
    return 0;
}

Ref: http://www.codingpractise.com/home/c-programming/sttruc-and-union/


3

Sendika Birliklerinin kullanımı, özel tip konuşmalara ihtiyaç duyulduğunda sıklıkla kullanılır. Birliğin yararlılığı hakkında fikir sahibi olmak. C / c standart kitaplığı, bir dosyaya kısa tamsayılar yazmak için özel olarak tasarlanmış bir işlev tanımlamaz. Fwrite () incurs kullanarak basit bir işlem için aşırı ek yüke neden olur. Ancak bir birleşim kullanarak, bir kerede bir baytlık bir dosyaya kısa bir tam sayının ikilisini yazan bir işlev kolayca oluşturabilirsiniz. Kısa tam sayıların 2 bayt uzunluğunda olduğunu düşünüyorum

ÖRNEK:

#include<stdio.h>
union pw {
short int i;
char ch[2];
};
int putw(short int num, FILE *fp);
int main (void)
{
FILE *fp;
fp fopen("test.tmp", "wb ");
putw(1000, fp); /* write the value 1000 as an integer*/
fclose(fp);
return 0;
}
int putw(short int num, FILE *fp)
{
pw word;
word.i = num;
putc(word.c[0] , fp);
return putc(word.c[1] , fp);
}    

Her ne kadar putw () kısa tamsayı ile çağırdı, putc () ve fwrite () kullanmak mümkün oldu. Ama bir birliğin nasıl kullanılabileceğine hakim olmak için bir örnek göstermek istedim


3

yapı, içinde farklı veri türlerinin bulunduğu ve her birinin kendi bellek bloğunu aldığı farklı veri türlerinin toplanmasıdır

değişkenden yalnızca birinin aynı anda kullanılacağından ve mevcut belleğin tam olarak kullanılmasını istediğinizden emin olduğumuzda genellikle birleşme kullandık çünkü en büyük türe eşit olan tek bir bellek bloğu alır.

struct emp
{
    char x;//1 byte
    float y; //4 byte
} e;

aldığı toplam hafıza => 5 bayt

union emp
{
    char x;//1 byte
    float y; //4 byte
} e;

aldığı toplam hafıza = 4 bayt


2

Sendikalar, aşağıda verilen bir bayt sıralama işlevi yazarken kullanışlı hale gelir. Yapılarla mümkün değil.

int main(int argc, char **argv) {
    union {
        short   s;
        char    c[sizeof(short)];
    } un;

    un.s = 0x0102;

    if (sizeof(short) == 2) {
        if (un.c[0] == 1 && un.c[1] == 2)
            printf("big-endian\n");
        else if (un.c[0] == 2 && un.c[1] == 1)
            printf("little-endian\n");
        else
            printf("unknown\n");
    } else
        printf("sizeof(short) = %d\n", sizeof(short));

    exit(0);
}
// Program from Unix Network Programming Vol. 1 by Stevens.

1

Birlik, diğerlerinin üzerinde tekrarladığı gibi, bir yapıdan farklıdır: yapı, üst üste binme veya yeniden tanımlama olmadan birbiri ardına tanımlarken aynı belleği yeniden tanımlar.

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.