Perbedaan antara Struktur dan Serikat

411

Apakah ada contoh yang baik untuk memberikan perbedaan antara a structdan a union? Pada dasarnya saya tahu bahwa structmenggunakan semua memori anggota dan unionmenggunakan ruang memori anggota terbesar. Apakah ada perbedaan level OS lainnya?

gagneet
sumber

Jawaban:

677

Dengan penyatuan, Anda hanya boleh menggunakan salah satu elemen, karena semuanya disimpan di tempat yang sama. Ini berguna ketika Anda ingin menyimpan sesuatu yang bisa menjadi salah satu dari beberapa jenis. Sebuah struct, di sisi lain, memiliki lokasi memori yang terpisah untuk masing-masing elemennya dan semuanya dapat digunakan sekaligus.

Untuk memberikan contoh konkret tentang penggunaannya, saya sedang mengerjakan juru bahasa Skema beberapa waktu yang lalu dan pada dasarnya saya overlay tipe data Skema ke tipe data C. Ini melibatkan penyimpanan dalam sebuah enum struktural yang menunjukkan jenis nilai dan gabungan untuk menyimpan nilai itu.

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

sunting: Jika Anda bertanya-tanya pengaturan xb ke 'c' apa yang mengubah nilai xa menjadi, secara teknis itu tidak terdefinisi. Pada kebanyakan mesin modern, char adalah 1 byte dan int adalah 4 byte, sehingga memberikan xb nilai 'c' juga memberikan byte pertama xa dengan nilai yang sama:

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

cetakan

99, 99

Mengapa kedua nilainya sama? Karena 3 byte terakhir dari int 3 semuanya nol, jadi itu juga dibaca sebagai 99. Jika kita memasukkan angka yang lebih besar untuk xa, Anda akan melihat bahwa ini tidak selalu terjadi:

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

cetakan

387427, 99

Untuk melihat lebih dekat nilai-nilai memori aktual, mari kita atur dan cetak nilai-nilai dalam hex:

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

cetakan

deadbe22, 22

Anda dapat dengan jelas melihat di mana 0x22 menimpa 0xEF.

TAPI

Di C, urutan byte dalam int tidak ditentukan.Program ini menimpa 0xEF dengan 0x22 pada Mac saya, tetapi ada platform lain di mana ia akan menimpa 0xDE sebagai gantinya karena urutan byte yang membentuk int dibatalkan. Oleh karena itu, ketika menulis sebuah program, Anda tidak boleh mengandalkan perilaku menimpa data tertentu dalam serikat karena itu tidak portabel.

Untuk membaca lebih lanjut tentang urutan byte, lihat endianness .

Kyle Cronin
sumber
1
menggunakan contoh ini, dalam penyatuan, jika xb = 'c' apa yang disimpan di xa? apakah itu referensi # dari char?
Kylex
1
semoga itu menjelaskan secara lebih rinci apa yang disimpan dalam xa saat Anda mengatur xb
Kyle Cronin
1
@KyleCronin, saya rasa saya mengerti. Dalam kasus Anda, Anda memiliki grup jenis, mengetahui bahwa Anda hanya perlu menggunakan satu tetapi Anda tidak tahu yang mana sampai runtime - sehingga serikat memungkinkan Anda untuk melakukan itu. Terima kasih
user12345613
2
@ user12345613 serikat dapat digunakan sebagai semacam kelas dasar untuk struct. Anda dapat meniru hierarki OO menggunakan serikat pekerja struct
Morten Jensen
1
Urutan @Lazar Byte dalam tipe multi-byte tergantung pada endianness. Saya sarankan membaca artikel Wikipedia di atasnya.
Kyle Cronin
83

Inilah jawaban singkatnya: sebuah struct adalah struktur rekaman: setiap elemen dalam struct mengalokasikan ruang baru. Jadi, struct suka

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

mengalokasikan setidaknya (sizeof(int)+sizeof(long)+sizeof(double)+sizeof(long double))byte dalam memori untuk setiap instance. ("Setidaknya" karena kendala penyelarasan arsitektur dapat memaksa kompiler untuk memasang struct.)

Di samping itu,

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

mengalokasikan satu keping memori dan memberinya empat alias. Jadi sizeof(union foobarbazquux_u) ≥ max((sizeof(int),sizeof(long),sizeof(double),sizeof(long double)), sekali lagi dengan kemungkinan beberapa penambahan untuk keberpihakan.

Charlie Martin
sumber
53

Apakah ada contoh yang baik untuk memberikan perbedaan antara 'struct' dan 'union'?

Protokol komunikasi imajiner

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

Dalam protokol imajiner ini, telah ditentukan bahwa, berdasarkan "tipe pesan", lokasi berikut di header akan berupa nomor permintaan, atau kode empat karakter, tetapi tidak keduanya. Singkatnya, serikat pekerja memungkinkan lokasi penyimpanan yang sama untuk mewakili lebih dari satu tipe data, di mana dijamin Anda hanya ingin menyimpan salah satu jenis data pada satu waktu.

Serikat pekerja sebagian besar detail tingkat rendah yang berbasis pada warisan C sebagai bahasa pemrograman sistem, di mana lokasi penyimpanan "tumpang tindih" kadang-kadang digunakan dengan cara ini. Kadang-kadang Anda dapat menggunakan serikat pekerja untuk menghemat memori tempat Anda memiliki struktur data tempat hanya satu dari beberapa jenis yang akan disimpan sekaligus.

Secara umum, OS tidak peduli atau tahu tentang struct dan serikat - keduanya hanya blok memori untuk itu. Str adalah blok memori yang menyimpan beberapa objek data, di mana objek tersebut tidak tumpang tindih. Serikat adalah blok memori yang menyimpan beberapa objek data, tetapi hanya memiliki penyimpanan untuk yang terbesar dari ini, dan dengan demikian hanya dapat menyimpan salah satu objek data pada satu waktu.

cygil
sumber
1
Ya. Ini menjelaskan kasus penggunaan dengan baik!
gideon
1
misalkan Anda memiliki packetheader ph;bagaimana Anda mengakses nomor permintaan? ph.request.requestnumber?
justin.m.chase
Penjelasan terbaik! Terima kasih.
84RR1573R
39

Seperti yang sudah Anda nyatakan dalam pertanyaan Anda, perbedaan utama di antara uniondan structadalah bahwa unionanggota saling overlay memori sehingga ukuran serikat adalah satu, sementara structanggota diletakkan satu demi satu (dengan bantalan opsional di antaranya). Serikat pekerja juga cukup besar untuk menampung semua anggotanya, dan memiliki keberpihakan yang cocok dengan semua anggotanya. Jadi katakanlah inthanya dapat disimpan pada alamat 2 byte dan lebar 2 byte, dan panjang hanya dapat disimpan pada alamat 4 byte dan panjangnya adalah 4 byte. Persatuan berikut

union test {
    int a;
    long b;
}; 

dapat memiliki sizeof 4, dan persyaratan penyelarasan 4. Baik serikat dan struct dapat memiliki bantalan pada akhirnya, tetapi tidak pada awalnya. Menulis ke struct hanya mengubah nilai anggota yang ditulis. Menulis kepada anggota serikat akan membuat nilai dari semua anggota lainnya tidak valid. Anda tidak dapat mengaksesnya jika Anda belum menulisnya sebelumnya, jika tidak maka perilaku tidak akan ditentukan. GCC menyediakan ekstensi yang benar-benar dapat Anda baca dari anggota serikat, meskipun Anda belum menulis surat kepada mereka baru-baru ini. Untuk Sistem Operasi, tidak masalah apakah program pengguna menulis ke serikat pekerja atau ke struktur. Ini sebenarnya hanya masalah dari kompiler.

Properti penting lain dari union dan struct adalah, mereka memungkinkan penunjuk kepada mereka dapat menunjuk ke jenis anggota mana pun . Jadi yang berikut ini valid:

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

some_test_pointer dapat mengarah ke int*atau double*. Jika Anda memasukkan alamat tipe testke int*, itu akan menunjuk ke anggota pertamanya a,, sebenarnya. Hal yang sama juga berlaku untuk serikat pekerja. Dengan demikian, karena sebuah serikat pekerja akan selalu memiliki perataan yang benar, Anda dapat menggunakan serikat pekerja untuk membuat menunjuk ke beberapa jenis yang valid:

union a {
    int a;
    double b;
};

Serikat itu benar-benar dapat menunjuk ke int, dan ganda:

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

sebenarnya valid, sebagaimana dinyatakan oleh standar C99:

Objek harus memiliki nilai tersimpan diakses hanya oleh ekspresi lvalue yang memiliki salah satu dari jenis berikut:

  • tipe yang kompatibel dengan tipe objek yang efektif
  • ...
  • tipe agregat atau serikat yang mencakup salah satu tipe yang disebutkan di atas di antara para anggotanya

Kompiler tidak akan mengoptimalkan v->a = 10;karena dapat mempengaruhi nilai *some_int_pointer(dan fungsi akan kembali 10sebagai ganti 5).

Johannes Schaub - litb
sumber
18

A unionberguna dalam beberapa skenario. uniondapat menjadi alat untuk manipulasi tingkat yang sangat rendah seperti menulis driver perangkat untuk kernel.

Contoh yang membedah floatsejumlah dengan menggunakan unionsebuah structdengan bitfields dan float. Saya menyimpan nomor di float, dan kemudian saya dapat mengakses bagian tertentu dari floatitu struct. Contoh menunjukkan bagaimana uniondigunakan untuk memiliki sudut pandang yang berbeda untuk melihat data.

#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;
}

Lihatlah deskripsi presisi tunggal di wikipedia. Saya menggunakan contoh dan angka ajaib 0,15625 dari sana.


unionjuga dapat digunakan untuk mengimplementasikan tipe data aljabar yang memiliki banyak alternatif. Saya menemukan contohnya dalam buku "Real World Haskell" karya O'Sullivan, Stewart, dan Goerzen. Lihat di bagian Serikat yang didiskriminasi .

Bersulang!

Krzysztof Voss
sumber
11

" union " dan " struct " adalah konstruksi dari bahasa C. Berbicara tentang perbedaan "tingkat OS" di antara mereka tidak pantas, karena itu adalah kompiler yang menghasilkan kode yang berbeda jika Anda menggunakan satu atau kata kunci lain.

Gabriele D'Antona
sumber
11

Non teknis berbicara:

Asumsi: kursi = blok memori, orang = variabel

Struktur : Jika ada 3 orang, mereka dapat duduk di kursi sesuai ukuran mereka.

Persatuan : Jika ada 3 orang hanya satu kursi yang akan ada di sana untuk duduk, semua perlu menggunakan kursi yang sama ketika mereka ingin duduk.

Secara teknis berarti:

Program yang disebutkan di bawah ini memberikan penyelaman mendalam ke dalam struktur dan penyatuan bersama.

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

Total MAIN_STRUCT size = sizeof (UINT64) untuk bufferaddr + sizeof (UNIT32) untuk union + 32 bit untuk bantalan (tergantung pada arsitektur prosesor) = 128 bit. Untuk struktur semua anggota mendapatkan blok memori secara bersamaan.

Union mendapat satu blok memori dari anggota ukuran maksimal (Ini 32 bitnya). Di dalam persatuan satu lagi terletak struktur (INNER_STRUCT) anggotanya mendapatkan blok memori ukuran total 32 bit (16 + 8 + 8). Dalam serikat, INNER_STRUCT (32 bit) anggota atau data (32 bit) dapat diakses.

skanda93
sumber
Penjelasan yang bagus. Bersulang!
Prem
11

Ya, perbedaan utama antara struct dan union adalah sama seperti yang Anda nyatakan. Struct menggunakan semua memori anggota dan serikat menggunakan ruang memori anggota terbesar.

Namun semua perbedaannya terletak pada kebutuhan penggunaan memori. Penggunaan serikat yang terbaik dapat dilihat dalam proses unix di mana kami menggunakan sinyal. seperti suatu proses dapat bertindak hanya pada satu sinyal pada satu waktu. Jadi deklarasi umum adalah:

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

Dalam hal ini, proses hanya menggunakan memori tertinggi dari semua sinyal. tetapi jika Anda menggunakan struct dalam hal ini, penggunaan memori akan menjadi jumlah semua sinyal. Membuat banyak perbedaan.

Untuk meringkas, Union harus dipilih jika Anda tahu bahwa Anda mengakses salah satu anggota sekaligus.

Ravi Kanth
sumber
10

Anda memilikinya, itu saja. Namun, pada dasarnya, apa gunanya serikat pekerja?

Anda dapat menempatkan konten lokasi yang sama dari berbagai jenis. Anda harus mengetahui jenis dari apa yang telah Anda simpan di serikat (begitu sering Anda memasukkannya structdengan tag jenis ...).

Mengapa ini penting? Tidak benar-benar untuk keuntungan ruang. Ya, Anda bisa mendapatkan beberapa bit atau melakukan padding, tapi itu bukan poin utama lagi.

Ini untuk keamanan jenis, memungkinkan Anda untuk melakukan semacam 'pengetikan dinamis': kompiler tahu bahwa konten Anda mungkin memiliki arti yang berbeda dan makna yang tepat tentang bagaimana Anda menafsirkannya terserah Anda pada saat run-time. Jika Anda memiliki pointer yang dapat menunjuk ke tipe yang berbeda, Anda HARUS menggunakan gabungan, jika tidak, kode Anda mungkin salah karena masalah alias (kompiler mengatakan sendiri "oh, hanya pointer ini yang bisa menunjuk ke tipe ini, jadi saya bisa mengoptimalkan keluar akses itu ... ", dan hal-hal buruk dapat terjadi).

Piotr Lesnicki
sumber
9

Struktur mengalokasikan ukuran total semua elemen di dalamnya.

Serikat pekerja hanya mengalokasikan memori sebanyak yang dibutuhkan anggota terbesarnya.

CMS
sumber
2
Anda mungkin ingin juga menambahkan bahwa anggota serikat "saling overlay" di mana mereka semua mulai dari alamat awal "struktur" serikat yang dialokasikan.
Jim Buck
4

apa perbedaan antara struktur dan persatuan?

Jawaban pintasan adalah: Deferensi dalam alokasi memori. Penjelasan: Dalam struktur, ruang memori akan dibuat untuk semua anggota di dalam struktur. Dalam penyatuan ruang memori akan dibuat hanya untuk anggota yang membutuhkan ruang memori terbesar. Pertimbangkan kode berikut:

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

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

Di sini ada dua anggota di dalam struct dan union: int dan long int. Ruang memori untuk int adalah: 4 byte dan ruang memori untuk int panjang adalah: sistem operasi 8 in 32 bit.

Jadi untuk struct 4 + 8 = 12 byte akan dibuat sedangkan 8 byte akan dibuat untuk union

Contoh kode:

#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/structure-and-union/

Abdus Sattar Bhuiyan
sumber
3

Penggunaan serikat pekerja sering digunakan ketika percakapan jenis khusus dibutuhkan. Untuk mendapatkan gagasan tentang kegunaan serikat. Pustaka standar c / c tidak mendefinisikan fungsi yang dirancang khusus untuk menulis bilangan bulat pendek ke file. Menggunakan fwrite () menimbulkan biaya overhead yang berlebihan untuk operasi sederhana. Namun dengan menggunakan gabungan Anda dapat dengan mudah membuat fungsi yang menulis biner integer pendek ke file satu byte pada suatu waktu. Saya berasumsi bahwa bilangan bulat pendek adalah 2 byte

CONTOH:

#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);
}    

meskipun putw () saya panggil dengan integer pendek, itu mungkin untuk menggunakan putc () dan fwrite (). Tetapi saya ingin menunjukkan contoh untuk mendominasi bagaimana sebuah serikat dapat digunakan

Ahmed
sumber
3

struktur adalah kumpulan dari tipe data yang berbeda di mana tipe data yang berbeda dapat tinggal di dalamnya dan setiap orang mendapatkan blok memorinya sendiri

kami biasanya menggunakan gabungan ketika kami yakin bahwa hanya satu dari variabel yang akan digunakan sekaligus dan Anda ingin memanfaatkan sepenuhnya memori saat ini karena hanya mendapatkan satu blok memori yang sama dengan jenis terbesar.

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

total memori yang didapat => 5 byte

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

total memori yang didapat = 4 byte

Anurag Bhakuni
sumber
2

Serikat pekerja berguna saat menulis fungsi pemesanan byte yang diberikan di bawah ini. Itu tidak mungkin dengan struct.

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.
Aniket Suryavanshi
sumber
1

Union berbeda dari struct karena Union mengulangi yang lain: itu mendefinisikan ulang memori yang sama sementara struct mendefinisikan satu demi satu tanpa tumpang tindih atau redefinisi.

Dennis Ng
sumber