Sự khác biệt giữa Cấu trúc và Liên minh


411

Có ví dụ nào hay để đưa ra sự khác biệt giữa a structvà a unionkhông? Về cơ bản tôi biết rằng structsử dụng tất cả bộ nhớ của thành viên và unionsử dụng không gian bộ nhớ thành viên lớn nhất. Có sự khác biệt cấp độ hệ điều hành nào khác không?

Câu trả lời:


677

Với một liên minh, bạn chỉ được sử dụng một trong các yếu tố, vì tất cả chúng đều được lưu trữ tại cùng một điểm. Điều này làm cho nó hữu ích khi bạn muốn lưu trữ một cái gì đó có thể là một trong một số loại. Mặt khác, một cấu trúc có một vị trí bộ nhớ riêng cho từng thành phần của nó và tất cả chúng có thể được sử dụng cùng một lúc.

Để đưa ra một ví dụ cụ thể về việc sử dụng chúng, tôi đã làm việc với một trình thông dịch Scheme cách đây ít lâu và về cơ bản tôi đã phủ các kiểu dữ liệu Scheme lên các kiểu dữ liệu C. Điều này liên quan đến việc lưu trữ trong một cấu trúc enum chỉ ra loại giá trị và liên kết để lưu trữ giá trị đó.

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

chỉnh sửa: Nếu bạn đang tự hỏi cài đặt xb thành 'c' nào sẽ thay đổi giá trị của xa thành, về mặt kỹ thuật, nó không được xác định. Trên hầu hết các máy hiện đại, char là 1 byte và int là 4 byte, do đó, cung cấp cho xb giá trị 'c' cũng cho byte đầu tiên của xa có cùng giá trị:

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

in

99, 99

Tại sao hai giá trị giống nhau? Vì 3 byte cuối cùng của int 3 đều bằng 0, nên nó cũng được đọc là 99. Nếu chúng ta đặt một số lớn hơn cho xa, bạn sẽ thấy rằng điều này không phải lúc nào cũng đúng:

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

in

387427, 99

Để xem kỹ hơn các giá trị bộ nhớ thực tế, hãy thiết lập và in ra các giá trị trong hex:

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

in

deadbe22, 22

Bạn có thể thấy rõ nơi 0x22 ghi đè lên 0xEF.

NHƯNG

Trong C, thứ tự các byte trong một int không được xác định.Chương trình này ghi đè 0xEF bằng 0x22 trên máy Mac của tôi, nhưng có những nền tảng khác sẽ ghi đè lên 0xDE thay vì thứ tự các byte tạo nên int bị đảo ngược. Do đó, khi viết chương trình, bạn không bao giờ nên dựa vào hành vi ghi đè dữ liệu cụ thể trong một liên minh vì nó không khả dụng.

Để đọc thêm về thứ tự của byte, hãy kiểm tra endianness .


1
sử dụng ví dụ này, trong union, nếu xb = 'c' cái gì được lưu trữ trong xa? nó có phải là tài liệu tham khảo của char không?
kylex

1
hy vọng điều đó giải thích chi tiết hơn những gì được lưu trữ trong xa khi bạn đặt xb
Kyle Cronin

1
@KyleCronin Tôi nghĩ rằng tôi nhận được nó. Trong trường hợp của bạn, bạn có một nhóm các loại, biết rằng bạn sẽ chỉ cần sử dụng một loại nhưng bạn không biết loại nào cho đến khi chạy - vì vậy, liên minh cho phép bạn làm điều đó. Cảm ơn
user12345613

2
@ user12345613 công đoàn có thể được sử dụng như một loại lớp cơ sở cho các cấu trúc. Bạn có thể mô phỏng hệ thống phân cấp OO bằng cách sử dụng các liên kết cấu trúc
Morten Jensen

1
Thứ tự @Lazar Byte trong các loại nhiều byte phụ thuộc vào tuổi thọ. Tôi đề nghị đọc bài viết Wikipedia về nó.
Kyle Cronin

83

Đây là câu trả lời ngắn: một cấu trúc là một cấu trúc bản ghi: mỗi phần tử trong cấu trúc phân bổ không gian mới. Vì vậy, một cấu trúc như

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

phân bổ ít nhất (sizeof(int)+sizeof(long)+sizeof(double)+sizeof(long double)) byte trong bộ nhớ cho mỗi phiên bản. ("Ít nhất" bởi vì các ràng buộc căn chỉnh kiến ​​trúc có thể buộc trình biên dịch đệm cấu trúc.)

Mặt khác,

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

phân bổ một khối bộ nhớ và cung cấp cho nó bốn bí danh. Vì vậy sizeof(union foobarbazquux_u) ≥ max((sizeof(int),sizeof(long),sizeof(double),sizeof(long double)), một lần nữa với khả năng bổ sung cho sự sắp xếp.


53

Có ví dụ nào hay để đưa ra sự khác biệt giữa 'struct' và 'union' không?

Một giao thức truyền thông tưởng tượng

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

Trong giao thức tưởng tượng này, người ta đã xác định rằng, dựa trên "loại thông báo", vị trí sau trong tiêu đề sẽ là số yêu cầu hoặc mã bốn ký tự, nhưng không phải cả hai. Nói tóm lại, các hiệp hội cho phép cùng một vị trí lưu trữ đại diện cho nhiều loại dữ liệu, trong đó đảm bảo rằng bạn sẽ chỉ muốn lưu trữ một trong các loại dữ liệu bất kỳ lúc nào.

Các hiệp hội phần lớn là một chi tiết cấp thấp dựa trên di sản của C là ngôn ngữ lập trình hệ thống, trong đó các vị trí lưu trữ "chồng chéo" đôi khi được sử dụng theo cách này. Đôi khi bạn có thể sử dụng các công đoàn để lưu bộ nhớ trong đó bạn có cấu trúc dữ liệu trong đó chỉ một trong số một số loại sẽ được lưu cùng một lúc.

Nói chung, HĐH không quan tâm hoặc không biết về cấu trúc và công đoàn - cả hai chỉ đơn giản là các khối bộ nhớ cho nó. Một cấu trúc là một khối bộ nhớ lưu trữ một số đối tượng dữ liệu, trong đó các đối tượng đó không trùng nhau. Liên minh là một khối bộ nhớ lưu trữ một số đối tượng dữ liệu, nhưng chỉ lưu trữ cho phần lớn nhất trong số đó và do đó chỉ có thể lưu trữ một trong các đối tượng dữ liệu bất kỳ lúc nào.


1
Vâng. Điều này giải thích một trường hợp sử dụng tốt!
gideon

1
giả sử bạn có packetheader ph;cách truy cập requestnumber? ph.request.requestnumber?
justin.m.chase

Giải thích tốt nhất! Cảm ơn.
84RR1573R

39

Như bạn đã nêu trong câu hỏi của mình, sự khác biệt chính giữa unionstructunioncác thành viên chồng lên bộ nhớ của nhau sao cho kích thước của một liên minh là một, trong khi structcác thành viên được đặt chồng lên nhau (với phần đệm tùy chọn ở giữa). Ngoài ra, một liên minh đủ lớn để chứa tất cả các thành viên của nó và có sự liên kết phù hợp với tất cả các thành viên của nó. Vì vậy, giả sử intchỉ có thể được lưu trữ tại các địa chỉ 2 byte và rộng 2 byte và dài chỉ có thể được lưu trữ tại các địa chỉ 4 byte và dài 4 byte. Công đoàn sau

union test {
    int a;
    long b;
}; 

có thể có một sizeof trong 4 và yêu cầu căn chỉnh là 4. Cả một liên minh và một cấu trúc có thể có phần đệm ở cuối, nhưng không phải ở đầu của chúng. Viết vào một cấu trúc chỉ thay đổi giá trị của thành viên được viết vào. Viết cho một thành viên của một liên minh sẽ làm cho giá trị của tất cả các thành viên khác không hợp lệ. Bạn không thể truy cập chúng nếu bạn chưa viết thư cho họ trước, nếu không thì hành vi không được xác định. GCC cung cấp dưới dạng phần mở rộng mà bạn thực sự có thể đọc từ các thành viên của liên minh, mặc dù gần đây bạn chưa viết thư cho họ. Đối với Hệ thống vận hành, không có vấn đề gì cho dù chương trình người dùng ghi vào liên minh hay cấu trúc. Đây thực sự chỉ là một vấn đề của trình biên dịch.

Một thuộc tính quan trọng khác của union và struct là, chúng cho phép một con trỏ tới chúng có thể trỏ đến các loại của bất kỳ thành viên nào . Vì vậy, sau đây là hợp lệ:

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

some_test_pulum có thể trỏ đến int*hoặc double*. Nếu bạn cast một địa chỉ của loại testđể int*, nó sẽ trỏ đến thành viên đầu tiên của mình, a, trên thực tế. Điều này cũng đúng với một công đoàn. Do đó, bởi vì một liên minh sẽ luôn có sự liên kết đúng, bạn có thể sử dụng một liên minh để làm cho việc trỏ đến một số loại hợp lệ:

union a {
    int a;
    double b;
};

Liên minh đó thực sự sẽ có thể trỏ đến một int và gấp đôi:

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

là thực sự hợp lệ, như đã nêu trong tiêu chuẩn C99:

Một đối tượng sẽ có giá trị được lưu trữ chỉ được truy cập bằng biểu thức lvalue có một trong các loại sau:

  • một loại tương thích với loại hiệu quả của đối tượng
  • ...
  • một loại tổng hợp hoặc liên minh bao gồm một trong các loại nói trên giữa các thành viên của nó

Trình biên dịch sẽ không tối ưu hóa v->a = 10;vì nó có thể ảnh hưởng đến giá trị của *some_int_pointer(và hàm sẽ trả về 10thay vì 5).


18

A unionlà hữu ích trong một vài kịch bản. unioncó thể là một công cụ để thao tác ở mức rất thấp như viết trình điều khiển thiết bị cho kernel.

Một ví dụ về điều đó là mổ xẻ một floatsố bằng cách sử dụng uniona structvới bitfield và a float. Tôi lưu một số trong floatvà sau đó tôi có thể truy cập các phần cụ thể floatthông qua đó struct. Ví dụ cho thấy cách unionsử dụng để có các góc khác nhau để xem dữ liệu.

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

Hãy xem mô tả chính xác duy nhất trên wikipedia. Tôi đã sử dụng ví dụ và số ma thuật 0.15625 từ đó.


unioncũng có thể được sử dụng để thực hiện một kiểu dữ liệu đại số có nhiều lựa chọn thay thế. Tôi đã tìm thấy một ví dụ về điều đó trong cuốn sách "Real World Haskell" của O'Sullivan, Stewart và Goerzen. Kiểm tra nó trong phần Công đoàn bị phân biệt đối xử .

Chúc mừng!


11

" union " và " struct " là cấu trúc của ngôn ngữ C. Nói về sự khác biệt "cấp độ hệ điều hành" giữa chúng là không phù hợp, vì đó là trình biên dịch tạo mã khác nhau nếu bạn sử dụng một hoặc một từ khóa khác.


11

Nói không kỹ thuật có nghĩa là:

Giả định: cái ghế = khối bộ nhớ, người = biến

Cấu trúc : Nếu có 3 người họ có thể ngồi trên ghế có kích thước tương ứng.

Liên minh : Nếu có 3 người thì chỉ có một chiếc ghế để ngồi, tất cả đều cần sử dụng cùng một chiếc ghế khi họ muốn ngồi.

Nói về mặt kỹ thuật có nghĩa là:

Các chương trình được đề cập dưới đây cung cấp cho một lặn sâu vào cấu trúc và kết hợp với nhau.

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

Tổng MAIN_STRVEL size = sizeof (UINT64) cho bufferaddr + sizeof (UNIT32) cho union + 32 bit cho phần đệm (phụ thuộc vào kiến ​​trúc bộ xử lý) = 128 bit. Đối với cấu trúc, tất cả các thành viên có được khối bộ nhớ liền kề nhau.

Union nhận được một khối bộ nhớ của thành viên kích thước tối đa (Ở đây là 32 bit). Bên trong liên kết thêm một cấu trúc nữa (INNER_STRVEL) các thành viên của nó có được một khối bộ nhớ có tổng kích thước 32 bit (16 + 8 + 8). Trong liên kết, có thể truy cập thành viên INNER_STRVEL (32 bit) hoặc dữ liệu (32 bit).


Giải thích tuyệt vời. Chúc mừng!
Prem

11

Có, sự khác biệt chính giữa struct và union giống như bạn đã nêu. Struct sử dụng tất cả bộ nhớ của thành viên và liên minh sử dụng không gian bộ nhớ thành viên lớn nhất.

Nhưng tất cả sự khác biệt nằm ở nhu cầu sử dụng của bộ nhớ. Việc sử dụng tốt nhất của liên minh có thể được nhìn thấy trong các quy trình của unix nơi chúng tôi sử dụng tín hiệu. giống như một quá trình có thể chỉ hành động theo một tín hiệu tại một thời điểm. Vì vậy, tuyên bố chung sẽ là:

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

Trong trường hợp này, quá trình chỉ sử dụng bộ nhớ cao nhất trong tất cả các tín hiệu. nhưng nếu bạn sử dụng struct trong trường hợp này, việc sử dụng bộ nhớ sẽ là tổng của tất cả các tín hiệu. Làm cho rất nhiều sự khác biệt.

Để tóm tắt, Union nên được chọn nếu bạn biết rằng bạn truy cập bất kỳ một thành viên nào tại một thời điểm.


10

Bạn có nó, đó là tất cả. Nhưng như vậy, về cơ bản, quan điểm của công đoàn là gì?

Bạn có thể đặt trong cùng một nội dung vị trí của các loại khác nhau. Bạn phải biết loại của những gì bạn đã lưu trữ trong liên minh (vì vậy, thường thì bạn đặt nó trong một structthẻ loại ...).

Sao nó lại quan trọng? Không thực sự cho không gian tăng. Có, bạn có thể đạt được một số bit hoặc thực hiện một số phần đệm, nhưng đó không còn là điểm chính nữa.

Vì sự an toàn của kiểu này, nó cho phép bạn thực hiện một số loại 'gõ động': trình biên dịch biết rằng nội dung của bạn có thể có ý nghĩa khác nhau và ý nghĩa chính xác về cách bạn diễn giải nó tùy thuộc vào thời gian chạy. Nếu bạn có một con trỏ có thể trỏ đến các loại khác nhau, bạn PHẢI sử dụng liên kết, nếu không, mã của bạn có thể không chính xác do các vấn đề răng cưa (trình biên dịch tự nói "ồ, chỉ con trỏ này mới có thể trỏ đến loại này, vì vậy tôi có thể tối ưu hóa ra những truy cập đó ... ", và những điều tồi tệ có thể xảy ra).


9

Một cấu trúc phân bổ tổng kích thước của tất cả các yếu tố trong đó.

Một liên minh chỉ phân bổ nhiều bộ nhớ như thành viên lớn nhất của nó yêu cầu.


2
Bạn cũng có thể muốn thêm rằng các thành viên công đoàn "chồng" lẫn nhau trong đó tất cả họ bắt đầu tại địa chỉ bắt đầu của "cấu trúc" công đoàn được phân bổ.
Jim Buck

4

sự khác biệt giữa cấu trúc và công đoàn là gì?

Câu trả lời ngắn gọn là: Sự trì hoãn nằm trong phân bổ bộ nhớ. Giải thích: Trong cấu trúc, không gian bộ nhớ sẽ được tạo cho tất cả các thành viên bên trong cấu trúc. Trong không gian bộ nhớ kết hợp sẽ chỉ được tạo cho một thành viên cần không gian bộ nhớ lớn nhất. Hãy xem xét các mã sau đây:

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

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

Ở đây có hai thành viên bên trong struct và union: int và long int. Không gian bộ nhớ cho int là: 4 byte và Không gian bộ nhớ cho int dài là: 8 trong hệ điều hành 32 bit.

Vì vậy, với struct 4 + 8 = 12 byte sẽ được tạo trong khi 8 byte sẽ được tạo cho union

Mã ví dụ:

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

Tham chiếu: http://www.codingpractise.com/home/c-programming/structure-and-union/


3

Việc sử dụng các nghiệp đoàn được sử dụng thường xuyên khi cần các cuộc hội thoại kiểu chuyên biệt. Để có được một ý tưởng về sự hữu ích của công đoàn. Thư viện chuẩn c / c định nghĩa không có chức năng nào được thiết kế đặc biệt để ghi các số nguyên ngắn vào một tệp. Sử dụng fwrite () phát sinh chi phí quá cao cho hoạt động đơn giản. Tuy nhiên, bằng cách sử dụng liên kết, bạn có thể dễ dàng tạo một hàm ghi nhị phân của một số nguyên ngắn thành một tệp một byte mỗi lần. Tôi giả sử rằng số nguyên ngắn dài 2 byte

VÍ DỤ:

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

mặc dù putw () tôi đã gọi với số nguyên ngắn, nhưng nó có thể sử dụng putc () và fwrite (). Nhưng tôi muốn đưa ra một ví dụ để thống trị cách sử dụng một liên minh


3

cấu trúc là tập hợp các kiểu dữ liệu khác nhau trong đó loại dữ liệu khác nhau có thể nằm trong đó và mỗi loại có một khối bộ nhớ riêng

chúng tôi thường sử dụng union khi chúng tôi chắc chắn rằng chỉ một trong số các biến sẽ được sử dụng cùng một lúc và bạn muốn sử dụng đầy đủ bộ nhớ hiện tại vì nó chỉ nhận được một khối bộ nhớ tương đương với loại lớn nhất.

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

tổng bộ nhớ nó nhận được => 5 byte

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

tổng bộ nhớ nó nhận được = 4 byte


2

Các hiệp hội có ích trong khi viết một hàm thứ tự byte được đưa ra dưới đây. Điều đó là không thể với các cấu trúc.

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

Liên minh khác với cấu trúc khi Liên minh lặp lại so với các cấu trúc khác: nó xác định lại cùng một bộ nhớ trong khi cấu trúc xác định từng bộ nhớ không có sự chồng chéo hoặc xác định lại.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.