มีตัวอย่างที่ดีที่จะให้ความแตกต่างระหว่าง a struct
และ a union
หรือไม่? โดยทั่วไปฉันรู้ว่าstruct
ใช้หน่วยความจำทั้งหมดของสมาชิกและunion
ใช้พื้นที่หน่วยความจำสมาชิกที่ใหญ่ที่สุด มีระดับความแตกต่างของระบบปฏิบัติการอื่น ๆ บ้างไหม?
มีตัวอย่างที่ดีที่จะให้ความแตกต่างระหว่าง a struct
และ a union
หรือไม่? โดยทั่วไปฉันรู้ว่าstruct
ใช้หน่วยความจำทั้งหมดของสมาชิกและunion
ใช้พื้นที่หน่วยความจำสมาชิกที่ใหญ่ที่สุด มีระดับความแตกต่างของระบบปฏิบัติการอื่น ๆ บ้างไหม?
คำตอบ:
ด้วยการรวมกันคุณควรใช้องค์ประกอบอย่างใดอย่างหนึ่งเท่านั้นเนื่องจากพวกมันถูกเก็บไว้ที่จุดเดียวกัน สิ่งนี้ทำให้มีประโยชน์เมื่อคุณต้องการเก็บบางสิ่งที่อาจเป็นหนึ่งในหลาย ๆ ประเภท ในทางกลับกัน Struct มีตำแหน่งหน่วยความจำแยกต่างหากสำหรับแต่ละองค์ประกอบและสามารถใช้งานได้ทันที
เพื่อให้ตัวอย่างที่เป็นรูปธรรมเกี่ยวกับการใช้งานของพวกเขาฉันกำลังทำงานกับล่าม Scheme เมื่อไม่นานมานี้และฉันได้วางซ้อนชนิดข้อมูล Scheme ลงในประเภทข้อมูล C เรื่องนี้เกี่ยวข้องกับการจัดเก็บในโครงสร้าง enum ระบุประเภทของค่าและสหภาพในการจัดเก็บค่าที่
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
แก้ไข:หากคุณสงสัยว่าการตั้งค่า xb เป็น 'c' จะเปลี่ยนค่าของ xa เป็นอย่างไรโดยทางเทคนิคแล้วการพูดนั้นไม่ได้กำหนดไว้ บนเครื่องจักรที่ทันสมัยที่สุดถ่านคือ 1 ไบต์และ int คือ 4 ไบต์ดังนั้นการให้ xb ค่า 'c' ยังให้ไบต์แรกของ xa ที่ค่าเดียวกัน:
union foo x;
x.a = 3;
x.b = 'c';
printf("%i, %i\n", x.a, x.b);
พิมพ์
99, 99
ทำไมค่าทั้งสองจึงเหมือนกัน? เนื่องจาก 3 ไบต์สุดท้ายของ int 3 เป็นศูนย์ทั้งหมดดังนั้นจึงอ่านได้เป็น 99 ถ้าเราใส่ xa จำนวนมากคุณจะเห็นว่านี่ไม่ใช่กรณีเสมอไป:
union foo x;
x.a = 387439;
x.b = 'c';
printf("%i, %i\n", x.a, x.b);
พิมพ์
387427, 99
หากต้องการดูค่าหน่วยความจำจริงอย่างใกล้ชิดลองตั้งค่าและพิมพ์ค่าเป็น hex:
union foo x;
x.a = 0xDEADBEEF;
x.b = 0x22;
printf("%x, %x\n", x.a, x.b);
พิมพ์
deadbe22, 22
คุณสามารถเห็นได้อย่างชัดเจนว่าที่ไหน 0x22 เขียนทับ 0xEF
แต่
ใน C ลำดับของไบต์ใน int ไม่ได้ถูกกำหนดไว้ โปรแกรมนี้เขียนทับ 0xEF ด้วย 0x22 บน Mac ของฉัน แต่มีแพลตฟอร์มอื่น ๆ ที่มันจะเขียนทับ 0xDE แทนเพราะลำดับของไบต์ที่ประกอบขึ้นเป็น int ถูกสลับกลับ ดังนั้นเมื่อเขียนโปรแกรมคุณไม่ควรพึ่งพาพฤติกรรมของการเขียนทับข้อมูลเฉพาะในสหภาพเพราะมันไม่สามารถพกพาได้
สำหรับการอ่านเพิ่มเติมเกี่ยวกับการสั่งซื้อของไบต์ตรวจสอบendianness
นี่คือคำตอบสั้น ๆ : โครงสร้างเป็นโครงสร้างบันทึก: องค์ประกอบในโครงสร้างแต่ละจัดสรรพื้นที่ใหม่ ดังนั้นโครงสร้างเช่น
struct foobarbazquux_t {
int foo;
long bar;
double baz;
long double quux;
}
จัดสรร(sizeof(int)+sizeof(long)+sizeof(double)+sizeof(long double))
หน่วยความจำอย่างน้อยไบต์สำหรับแต่ละอินสแตนซ์ ("อย่างน้อย" เพราะข้อ จำกัด การวางแนวสถาปัตยกรรมอาจบังคับให้คอมไพเลอร์บีบอัด struct)
ในทางกลับกัน,
union foobarbazquux_u {
int foo;
long bar;
double baz;
long double quux;
}
จัดสรรหนึ่งหน่วยความจำและให้สี่นามแฝง ดังนั้นsizeof(union foobarbazquux_u) ≥ max((sizeof(int),sizeof(long),sizeof(double),sizeof(long double))
อีกครั้งด้วยความเป็นไปได้ของการเพิ่มบางอย่างสำหรับการจัดตำแหน่ง
มีตัวอย่างที่ดีที่จะให้ความแตกต่างระหว่าง 'struct' และ 'สหภาพ' หรือไม่?
โปรโตคอลการสื่อสารในจินตนาการ
struct packetheader {
int sourceaddress;
int destaddress;
int messagetype;
union request {
char fourcc[4];
int requestnumber;
};
};
ในโปรโตคอลจินตภาพนี้มีการแยกว่าตาม "ประเภทข้อความ" ตำแหน่งต่อไปนี้ในส่วนหัวจะเป็นหมายเลขคำขอหรือรหัสตัวอักษรสี่ตัว แต่ไม่ใช่ทั้งสองอย่าง กล่าวโดยสรุปคือสหภาพอนุญาตให้ที่เก็บข้อมูลเดียวกันแสดงประเภทข้อมูลได้มากกว่าหนึ่งประเภทโดยมีการรับประกันว่าคุณจะต้องการจัดเก็บข้อมูลประเภทใดประเภทหนึ่งในแต่ละครั้ง
สหภาพแรงงานส่วนใหญ่เป็นรายละเอียดในระดับต่ำซึ่งมีพื้นฐานมาจากมรดกของ C ในฐานะภาษาการเขียนโปรแกรมระบบซึ่งบางครั้งมีการใช้สถานที่เก็บข้อมูล "ที่ทับซ้อนกัน" ด้วยวิธีนี้ บางครั้งคุณสามารถใช้สหภาพเพื่อบันทึกหน่วยความจำที่คุณมีโครงสร้างข้อมูลที่จะบันทึกเพียงหนึ่งประเภทเท่านั้นในคราวเดียว
โดยทั่วไปแล้วระบบปฏิบัติการไม่สนใจหรือรู้เกี่ยวกับ structs และสหภาพ - ทั้งคู่เป็นเพียงหน่วยความจำของมัน struct คือบล็อกของหน่วยความจำที่เก็บวัตถุข้อมูลหลายอย่างโดยที่วัตถุเหล่านั้นไม่ทับซ้อนกัน การรวมกันเป็นบล็อกของหน่วยความจำที่เก็บวัตถุข้อมูลหลายอย่าง แต่มีที่เก็บข้อมูลสำหรับวัตถุที่มีขนาดใหญ่ที่สุดเท่านั้นและสามารถเก็บวัตถุข้อมูลได้เพียงครั้งเดียวในแต่ละครั้ง
packetheader ph;
การเข้าถึงขอหมายเลข? ph.request.requestnumber
?
ในขณะที่คุณพูดถึงคำถามของคุณความแตกต่างที่สำคัญระหว่างunion
และstruct
คือunion
สมาชิกซ้อนทับความทรงจำของกันและกันเพื่อให้ขนาดของสหภาพเป็นหนึ่งในขณะที่struct
สมาชิกวางกันหลังจากนั้น สหภาพมีขนาดใหญ่พอที่จะมีสมาชิกทั้งหมดและมีการจัดตำแหน่งที่เหมาะกับสมาชิกทั้งหมด สมมุติว่าint
สามารถเก็บได้ที่ 2 ไบต์เท่านั้นและกว้าง 2 ไบต์และยาวสามารถเก็บได้ที่ 4 ไบต์เท่านั้นและยาว 4 ไบต์ สหภาพดังต่อไปนี้
union test {
int a;
long b;
};
อาจมีค่าsizeof
4 และข้อกำหนดการจัดตำแหน่งของ 4 ทั้งสหภาพและโครงสร้างสามารถมีช่องว่างภายในท้ายที่สุด แต่ไม่ใช่จุดเริ่มต้น การเขียนไปที่ struct จะเปลี่ยนเฉพาะค่าของสมาชิกที่เขียนไป การเขียนถึงสมาชิกของสหภาพจะทำให้มูลค่าของสมาชิกอื่น ๆ ทั้งหมดไม่ถูกต้อง คุณไม่สามารถเข้าถึงพวกเขาหากคุณไม่ได้เขียนถึงพวกเขามาก่อนมิฉะนั้นพฤติกรรมจะไม่ได้กำหนด GCC เป็นส่วนเสริมที่คุณสามารถอ่านได้จริงจากสมาชิกของสหภาพแม้ว่าคุณจะไม่ได้เขียนถึงพวกเขาล่าสุด สำหรับระบบปฏิบัติการนั้นไม่จำเป็นต้องกังวลว่าโปรแกรมผู้ใช้จะเขียนไปยังสหภาพหรือโครงสร้าง นี่เป็นเพียงปัญหาของคอมไพเลอร์
อีกคุณสมบัติที่สำคัญของสหภาพและ struct คือพวกเขาอนุญาตให้ตัวชี้ไปยังพวกเขาสามารถชี้ไปที่ประเภทใด ๆ ของสมาชิก ดังนั้นสิ่งต่อไปนี้ที่ถูกต้อง:
struct test {
int a;
double b;
} * some_test_pointer;
some_test_pointer สามารถชี้ไปหรือint*
double*
ถ้าคุณโยนอยู่ของประเภทtest
การint*
ก็จะชี้ไปยังสมาชิกคนแรกของตนa
จริง เช่นเดียวกันสำหรับสหภาพด้วย ดังนั้นเนื่องจากสหภาพจะมีการจัดตำแหน่งที่ถูกต้องเสมอคุณสามารถใช้สหภาพเพื่อให้การชี้ไปที่ประเภทที่ถูกต้อง:
union a {
int a;
double b;
};
การรวมกันนั้นจะสามารถชี้ไปที่ int และ double:
union a * v = (union a*)some_int_pointer;
*some_int_pointer = 5;
v->a = 10;
return *some_int_pointer;
ถูกต้องจริงตามที่ระบุไว้โดยมาตรฐาน C99:
วัตถุต้องมีค่าที่เก็บไว้เข้าถึงได้โดยนิพจน์ lvalue ที่มีประเภทใดประเภทหนึ่งต่อไปนี้:
- ชนิดที่เข้ากันได้กับชนิดของวัตถุที่มีประสิทธิภาพ
- ...
- ประเภทรวมหรือสหภาพที่มีหนึ่งในประเภทดังกล่าวในหมู่สมาชิก
คอมไพเลอร์จะไม่เพิ่มประสิทธิภาพออกv->a = 10;
เป็นมันอาจส่งผลกระทบต่อมูลค่าของ*some_int_pointer
(และฟังก์ชั่นจะกลับมา10
แทน5
)
A union
มีประโยชน์ในบางสถานการณ์
union
สามารถเป็นเครื่องมือสำหรับการจัดการในระดับต่ำมากเช่นการเขียนโปรแกรมควบคุมอุปกรณ์สำหรับเคอร์เนล
ตัวอย่างของการที่มีการผ่าfloat
จำนวนโดยใช้union
ของstruct
ที่มี bitfields float
และ ฉันบันทึกหมายเลขไว้ในfloat
และหลังจากนั้นฉันสามารถเข้าถึงบางส่วนของที่float
ผ่านstruct
ได้ ตัวอย่างแสดงวิธีการunion
ใช้มุมมองต่างๆในการดูข้อมูล
#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;
}
ลองดูที่คำอธิบายความแม่นยำเดียวในวิกิพีเดีย ฉันใช้ตัวอย่างและหมายเลขเวทย์มนตร์ 0.15625 จากที่นั่น
union
นอกจากนี้ยังสามารถใช้ในการดำเนินการประเภทข้อมูลเกี่ยวกับพีชคณิตที่มีหลายทางเลือก ฉันพบตัวอย่างของสิ่งนั้นในหนังสือ "Real World Haskell" โดย O'Sullivan, Stewart และ Goerzen ตรวจสอบในส่วนสหภาพแยกปฏิบัติ
ไชโย!
" union " และ " struct " เป็นโครงสร้างของภาษา C การพูดถึงความแตกต่างระหว่าง "ระดับระบบปฏิบัติการ" นั้นไม่เหมาะสมเนื่องจากเป็นคอมไพเลอร์ที่สร้างรหัสที่แตกต่างกันหากคุณใช้คำหลักหนึ่งคำหรือคำอื่น
เทคนิคการพูดที่ไม่ได้หมายถึง:
อัสสัมชัญ: chair = บล็อกหน่วยความจำ, คน = ตัวแปร
โครงสร้าง : ถ้ามี 3 คนพวกเขาสามารถนั่งในเก้าอี้ขนาดเดียวกัน
ยูเนี่ยน : ถ้ามี 3 คนมีเก้าอี้เดียวเท่านั้นที่จะนั่งทุกคนต้องใช้เก้าอี้ตัวเดิมเมื่อพวกเขาต้องการนั่ง
เทคนิคการพูดหมายถึง:
โปรแกรมที่กล่าวถึงด้านล่างให้การดำน้ำลึกเข้าไปในโครงสร้างและการรวมกัน
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) สำหรับ bufferaddr + sizeof (UNIT32) สำหรับ union + 32 บิตสำหรับการขยาย (ขึ้นอยู่กับสถาปัตยกรรมหน่วยประมวลผล) = 128 บิต สำหรับโครงสร้างสมาชิกทั้งหมดจะได้รับบล็อกหน่วยความจำต่อเนื่อง
ยูเนี่ยนได้หนึ่งบล็อกหน่วยความจำของสมาชิกขนาดสูงสุด (นี่คือ 32 บิต) ภายในการรวมโครงสร้างอีกหนึ่งการโกหก (INNER_STRUCT) สมาชิกจะได้รับบล็อกหน่วยความจำขนาดรวม 32 บิต (16 + 8 + 8) ในการรวมสมาชิก INNER_STRUCT (32 บิต) หรือข้อมูล (32 บิต) สามารถเข้าถึงได้
ใช่ความแตกต่างหลักระหว่าง struct และ union นั้นเหมือนกับที่คุณระบุไว้ โครงสร้างใช้หน่วยความจำทั้งหมดของสมาชิกและสหภาพใช้พื้นที่หน่วยความจำสมาชิกที่ใหญ่ที่สุด
แต่ความแตกต่างทั้งหมดขึ้นอยู่กับการใช้งานที่ต้องการของหน่วยความจำ การใช้งานที่ดีที่สุดของสหภาพสามารถเห็นได้ในกระบวนการของยูนิกซ์ที่เราใช้สัญญาณ เช่นเดียวกับกระบวนการสามารถกระทำได้โดยสัญญาณเดียวเท่านั้นในแต่ละครั้ง ดังนั้นการประกาศทั่วไปจะเป็น:
union SIGSELECT
{
SIGNAL_1 signal1;
SIGNAL_2 signal2;
.....
};
ในกรณีนี้กระบวนการใช้เฉพาะหน่วยความจำสูงสุดของสัญญาณทั้งหมด แต่ถ้าคุณใช้ struct ในกรณีนี้การใช้หน่วยความจำจะเป็นผลรวมของสัญญาณทั้งหมด สร้างความแตกต่างอย่างมาก
ในการสรุปควรเลือก Union หากคุณทราบว่าคุณเข้าถึงสมาชิกคนใดคนหนึ่งในแต่ละครั้ง
คุณมีมันคือทั้งหมดที่ แต่โดยทั่วไปแล้วประเด็นของสหภาพคืออะไร
คุณสามารถใส่เนื้อหาตำแหน่งเดียวกันกับประเภทที่แตกต่างกัน คุณต้องรู้ประเภทของสิ่งที่คุณเก็บไว้ในสหภาพ (บ่อยครั้งที่คุณใส่ไว้ในstruct
ด้วยแท็กประเภท ... )
ทำไมสิ่งนี้จึงสำคัญ ไม่ใช่เพื่อกำไรจากอวกาศ ใช่คุณสามารถรับบิตหรือแพ็ดดิ้งได้ แต่นั่นไม่ใช่ประเด็นหลักอีกต่อไป
เพื่อความปลอดภัยประเภทนี้ช่วยให้คุณสามารถ 'พิมพ์แบบไดนามิก' บางชนิด: คอมไพเลอร์รู้ว่าเนื้อหาของคุณอาจมีความหมายแตกต่างกันและความหมายที่แม่นยำของการตีความของคุณขึ้นอยู่กับคุณในเวลาทำงาน หากคุณมีตัวชี้ที่สามารถชี้ไปที่ประเภทที่แตกต่างกันคุณต้องใช้สหภาพมิฉะนั้นรหัสคุณอาจไม่ถูกต้องเนื่องจากปัญหานามแฝง (คอมไพเลอร์บอกกับตัวเองว่า "โอ้ตัวชี้นี้เท่านั้นที่สามารถชี้ไปที่ประเภทนี้ได้ เข้าถึงสิ่งเหล่านั้น ... "และสิ่งเลวร้ายก็เกิดขึ้นได้)
โครงสร้างจะจัดสรรขนาดทั้งหมดขององค์ประกอบทั้งหมดในนั้น
สหภาพจะจัดสรรหน่วยความจำให้มากที่สุดเท่าที่สมาชิกต้องการมากที่สุด
ความแตกต่างระหว่างโครงสร้างและสหภาพคืออะไร?
คำตอบทางลัดคือ: การเคารพนั้นอยู่ในการจัดสรรหน่วยความจำ คำอธิบาย: ในโครงสร้างพื้นที่หน่วยความจำจะถูกสร้างขึ้นสำหรับสมาชิกทั้งหมดภายในโครงสร้าง ในพื้นที่หน่วยความจำร่วมจะถูกสร้างขึ้นสำหรับสมาชิกที่ต้องการพื้นที่หน่วยความจำที่ใหญ่ที่สุดเท่านั้น พิจารณารหัสต่อไปนี้:
struct s_tag
{
int a;
long int b;
} x;
union u_tag
{
int a;
long int b;
} y;
ที่นี่มีสมาชิกสองคนใน struct และ union: int และ long int พื้นที่หน่วยความจำสำหรับ int คือ: 4 ไบต์และพื้นที่หน่วยความจำสำหรับ int ยาวคือ 8 ในระบบปฏิบัติการ 32 บิต
ดังนั้นสำหรับ struct 4 + 8 = 12 ไบต์จะถูกสร้างขึ้นในขณะที่ 8 ไบต์จะถูกสร้างขึ้นสำหรับการรวมกัน
ตัวอย่างรหัส:
#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/
การใช้สหภาพถูกใช้บ่อย ๆ เมื่อต้องการการสนทนาประเภทพิเศษ เพื่อให้เข้าใจถึงประโยชน์ของสหภาพ ไลบรารีมาตรฐาน c / c กำหนดว่าไม่มีฟังก์ชั่นที่ออกแบบมาเป็นพิเศษเพื่อเขียนจำนวนเต็มสั้น ๆ ไปยังไฟล์ การใช้ fwrite () จะเกิดค่าใช้จ่ายมากเกินไปสำหรับการใช้งานที่ง่าย อย่างไรก็ตามการใช้สหภาพคุณสามารถสร้างฟังก์ชั่นที่เขียนเลขฐานสองของจำนวนเต็มแบบสั้นไปยังไฟล์ทีละหนึ่งไบต์ ฉันคิดว่าจำนวนเต็มสั้น ๆ นั้นยาว 2 ไบต์
ตัวอย่าง:
#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);
}
แม้ว่า putw () ฉันเรียกด้วยจำนวนเต็มสั้น ๆ มันเป็นไปได้ที่จะใช้ putc () และ fwrite () แต่ฉันต้องการแสดงตัวอย่างเพื่อควบคุมการใช้สหภาพ
โครงสร้างคือการรวบรวมประเภทข้อมูลที่แตกต่างกันซึ่งประเภทของข้อมูลที่แตกต่างกันสามารถอยู่ในนั้นและทุกคนได้รับบล็อกหน่วยความจำของตัวเอง
เรามักจะใช้ยูเนี่ยนเมื่อเราแน่ใจว่าจะใช้เพียงหนึ่งตัวแปรในครั้งเดียวและคุณต้องการใช้ประโยชน์เต็มที่จากหน่วยความจำปัจจุบันเพราะมันจะได้รับหน่วยความจำเพียงหนึ่งบล็อกซึ่งเท่ากับประเภทที่ใหญ่ที่สุด
struct emp
{
char x;//1 byte
float y; //4 byte
} e;
หน่วยความจำทั้งหมดที่ได้รับ => 5 ไบต์
union emp
{
char x;//1 byte
float y; //4 byte
} e;
หน่วยความจำทั้งหมดที่ได้รับ = 4 ไบต์
สหภาพมีประโยชน์ในขณะที่เขียนฟังก์ชั่นการสั่งซื้อแบบไบท์ซึ่งได้รับด้านล่าง มันเป็นไปไม่ได้กับ structs
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.
ยูเนี่ยนจะแตกต่างจากโครงสร้างที่ยูเนี่ยนซ้ำกว่าคนอื่น ๆ : มันนิยามหน่วยความจำเดียวกันในขณะที่โครงสร้างกำหนดหนึ่งหลังจากที่อื่น ๆ โดยไม่ทับซ้อนกันหรือนิยามใหม่