Jawaban:
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 .
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.
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.
packetheader ph;
bagaimana Anda mengakses nomor permintaan? ph.request.requestnumber
?
Seperti yang sudah Anda nyatakan dalam pertanyaan Anda, perbedaan utama di antara union
dan struct
adalah bahwa union
anggota saling overlay memori sehingga ukuran serikat adalah satu, sementara struct
anggota 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 int
hanya 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 test
ke 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 10
sebagai ganti 5
).
A union
berguna dalam beberapa skenario.
union
dapat menjadi alat untuk manipulasi tingkat yang sangat rendah seperti menulis driver perangkat untuk kernel.
Contoh yang membedah float
sejumlah dengan menggunakan union
sebuah struct
dengan bitfields dan float
. Saya menyimpan nomor di float
, dan kemudian saya dapat mengakses bagian tertentu dari float
itu struct
. Contoh menunjukkan bagaimana union
digunakan 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.
union
juga 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!
" 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.
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.
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.
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 struct
dengan 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).
Struktur mengalokasikan ukuran total semua elemen di dalamnya.
Serikat pekerja hanya mengalokasikan memori sebanyak yang dibutuhkan anggota terbesarnya.
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/
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
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
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.