2010-08-12 31 views
6

Từ một câu trả lời cho một số câu hỏi ngày hôm qua, tôi đã học được rằng nó không thể nhập và không an toàn để viết vào một thành viên công đoàn và đọc giá trị từ một thành viên khác của một loại khác nhau, giả định sự căn chỉnh của các thành viên. Vì vậy, sau khi một số nghiên cứu tôi tìm thấy một nguồn văn bản lặp lại yêu cầu này và chỉ định một ví dụ phổ biến - sử dụng liên minh int và float để tìm biểu diễn nhị phân của phao.Công đoàn ANSI C - chúng thực sự hữu ích?

Vì vậy, hiểu rằng giả định này không an toàn, tôi tự hỏi - ngoại trừ việc tiết kiệm bộ nhớ (duh ...) những gì sử dụng thực sự là có cho công đoàn?

Lưu ý: đó là, theo tiêu chuẩn C. Rõ ràng, để thực hiện cụ thể, các quy tắc được biết trước và có thể được tận dụng.

CHỈNH SỬA: từ "không an toàn", do sự kết hợp của những năm gần đây, có lẽ là lựa chọn không đúng về từ ngữ, nhưng tôi nghĩ ý định rõ ràng.

CHỈNH SỬA 2: Vì điểm này lặp lại trong câu trả lời - bộ nhớ lưu là đối số hợp lệ. Tôi muốn biết nếu có điều gì đó vượt ra khỏi đó.

+0

vui lòng liên kết đến câu hỏi được trích dẫn và tới nguồn văn bản –

+0

Tôi thực sự không nhớ chính câu hỏi đó (đây không phải là chủ đề ở đó, nhưng đã nêu lên một trong các nhận xét). Nguồn là '" C - Hướng dẫn tham chiếu ", 5th Ed., Harbison/Steele' – ysap

+0

Đây có phải là nó không? http://stackoverflow.com/questions/3443751/converting-a-set-of-booleans-to-a-number/3443912#3443912 Nếu vậy, hãy xem nhận xét của tôi dưới cùng một câu trả lời đó. – wilhelmtell

Trả lời

10

Có.

Cung cấp cách tạo vùng chứa chung. Mặc dù, để có được hành vi đa hình, bạn phải tự thực hiện chuyển đổi vtable hoặc loại ...

, tuy nhiên, một trong những tính năng bạn chỉ sử dụng khi cần và cần ít hơn.

+0

OK, tôi nghĩ tôi hiểu ý của bạn là gì. Tuy nhiên, vì bạn được yêu cầu tự mình thực hiện cơ chế đa hình, nên nó có lợi thế gì khi khai báo các biến của các kiểu khác nhau và truy cập chúng thông qua một con trỏ được chuyển bởi cơ chế (không phải để lưu bộ nhớ)? – ysap

+0

Ngoài ra, đây có phải là ý định của các nhà văn tiêu chuẩn, hay nó là một trường hợp sử dụng thông minh được phát triển sau này? – ysap

+0

Tiết kiệm bộ nhớ (và đáng để suy nghĩ về đặc điểm của các máy 'lớn' trong khoảng thời gian c được viết: bộ nhớ chính được đo bằng kilowords trên máy chia sẻ), đơn giản hóa việc quản lý cấu trúc phức tạp, ít ký tự gõ trên mỗi truy cập, * khả năng * để sử dụng bộ nhớ dù sao bạn muốn, và không bỏ bê khả năng làm những điều "không xác định" vì không phải mọi chương trình cần phải được di động. – dmckee

3

Có, các công đoàn có thể không thể di chuyển và không an toàn nhưng có sử dụng. Ví dụ, nó có thể tăng tốc mọi thứ bằng cách loại bỏ sự cần thiết phải đúc uint32 thành char [4]. Điều này có thể có ích nếu bạn đang cố định tuyến bằng địa chỉ IP trong SW, nhưng sau đó trình xử lý của bạn phải là thứ tự mạng. Hãy suy nghĩ của các công đoàn như là một thay thế cho đúc, với hướng dẫn máy ít hơn. Đúc có những hạn chế tương tự.

+1

Như tôi đã đề cập trong câu hỏi, các công đoàn rõ ràng có thể rất hữu ích cho việc triển khai ** cụ thể **. Bạn đang giả định các giả định cụ thể về lưu trữ cơ bản. Câu hỏi của tôi là nếu chúng hữu ích dưới góc nhìn ** C ** chuẩn. * dmckee * đưa ra một lý lẽ hợp lý cho điều đó. – ysap

+0

+1 cho giải thích công đoàn như một loại đúc – Robert

2

Một cách để sử dụng công đoàn mà tôi gặp phải là ẩn dữ liệu.

Giả sử bạn có một cấu trúc đó là bộ đệm

sau đó bằng cách cho phép công đoàn trên struct trong một số mô-đun bạn có thể truy cập vào các nội dung của bộ đệm theo những cách khác nhau hay không ở tất cả tùy thuộc vào sự kết hợp khai báo trong đó đặc biệt mô-đun.

EDIT: đây là một ví dụ

struct X 
{ 
    int a; 
}; 

struct Y 
{ 
    int b; 
}; 

union Public 
{ 
    struct X x; 
    struct Y y; 
}; 

đây bất cứ ai sử dụng đoàn XY có thể đúc XY hoặc là struct X hoặc Y

để cung cấp một chức năng:

void foo(Public* arg) 
{ 
... 

bạn có thể truy cập cả struct X hoặc struct Y

nhưng sau đó bạn muốn giới hạn quyền truy cập để người dùng không ' t biết về X

tên công đoàn giữ nguyên nhưng phần struct X là không có sẵn (thông qua tiêu đề)

void foo(Public* arg) 
{ 
    // Public is still available but struct X is gone, 
    // user can only cast to struct Y 

    struct Y* p = (struct Y*)arg; 
... 
+0

:: nhấp nháy :: Đó là * thông minh *, nhưng ... nó cũng bảo mật thông qua sự tối tăm, vì vậy có lẽ không phải là một ý tưởng tuyệt vời. Cũng nhạy cảm với các vấn đề đóng gói, do đó, thực hiện phụ thuộc. – dmckee

+0

Tôi không chắc tôi thấy quan điểm của bạn. Bạn có thể đưa ra một ví dụ cho định nghĩa của bộ đệm như vậy không? – ysap

+0

@dmckee - bạn đã chấp thuận nghi ngờ rằng việc triển khai IS usecase này cụ thể! – ysap

4

Thậm chí nếu union s không cung cấp nhiều tính hữu dụng ngay lập tức (giảm sử dụng bộ nhớ ngoài), một lợi thế của việc sử dụng union khi đổ tất cả các thành viên của nó vào một struct là nó làm cho ngữ nghĩa dự định rõ ràng: chỉ một giá trị (hoặc bộ giá trị nếu đó là union của struct s) hợp lệ tại bất kỳ thời điểm nào. Nó tự tài liệu tốt hơn.

Tính độc quyền lẫn nhau của các thành viên sẽ ít rõ ràng hơn nếu bạn thay thế tất cả thành viên riêng biệt của union thành viên struct. Ngoài ra, bạn vẫn có cùng một vấn đề về hành vi không xác định nếu bạn đọc một thành viên mà trước đó chưa được viết, nhưng bây giờ bạn cũng cần tính đến ngữ nghĩa của ứng dụng (đã khởi tạo tất cả các thành viên chưa sử dụng thành 0? nó để lại chúng như rác?), vì vậy trong ý nghĩa đó, tại sao sẽ không bạn sử dụng một công đoàn?

+0

Tôi nghĩ rằng đó chỉ là một phần chính xác - sự hiểu biết của tôi là các thành viên cùng loại được bảo đảm để che phủ eachother, do đó, không có mutex ở đây. – ysap

+0

@ysap: Đó là sự thật, nhưng nó là một chi tiết thực hiện của các công đoàn không liên quan đến quan điểm của tôi. – jamesdlin

+0

@jamesdin - cũng quan điểm của bạn là "do sự sắp xếp không xác định của các thành viên, giả sử độc quyền truy cập" (hay không?). Nhận xét của tôi là điều này chỉ đúng một phần. – ysap

2

Câu hỏi đặt ra có chứa một hạn chế mà có thể không cho phép một câu trả lời hợp lệ ...

Bạn hỏi về việc sử dụng thực theo tiêu chuẩn, nhưng "sử dụng thực tế" có thể được cho phép một lập trình viên có kiến ​​thức để khai thác thực hiện hành vi quy định tại cách mà Ủy ban tiêu chuẩn không muốn dự đoán hoặc liệt kê. Và tôi không có nghĩa là ủy ban tiêu chuẩn đã có một hành vi cụ thể trong tâm trí, nhưng họ rõ ràng muốn để lại khả năng có được khai thác một cách hữu ích.

Nói cách khác: Các công đoàn không phải là hữu ích cho hành vi được xác định chuẩn hữu ích nói chung, chúng có thể đơn giản ở đó để cho phép ai đó khai thác quirks của máy mục tiêu mà không cần lắp ráp. Có thể có hàng triệu cách hữu ích để sử dụng chúng trên các máy khác nhau có sẵn trong các cách xác định và không có cách nào hữu ích để sử dụng chúng một cách nghiêm túc, nhưng hàng triệu cách sử dụng được xác định là đủ lý do để chuẩn hóa sự tồn tại của họ.

Tôi hy vọng điều đó có ý nghĩa.

+0

Vâng, nó có ý nghĩa, cảm ơn. Tôi không hoàn toàn chắc chắn tại thời điểm này mà tôi đồng ý với điều này như trả lời câu hỏi của tôi. – ysap

+0

@ysap - nó không thực sự, nhưng tôi nghĩ rằng nó là một câu trả lời nhiều hơn một bình luận :) – detly

3

Thậm chí giảm giá cho một triển khai cụ thể nơi liên kết và đóng gói được biết, các công đoàn vẫn có thể hữu ích.

Chúng cho phép bạn lưu trữ là một trong nhiều giá trị vào một khối duy nhất của bộ nhớ, dọc theo dòng:

typedef struct { 
    int type; 
    union { 
     type1 one; 
     type2 two; 
    } 
} unioned_type; 

Và vâng, nó không cầm tay để mong đợi để có thể lưu trữ của bạn dữ liệu vào one và đọc từ two. Nhưng nếu bạn chỉ cần sử dụng type để chỉ định biến cơ bản là gì, bạn có thể dễ dàng truy cập vào biến đó mà không phải truyền.

Nói cách khác:

unioned_type ut; 
ut.type = 1; 
ut.one = myOne; 
// Don't use ut.two here unless you know the underlying details. 

là tốt giả sử bạn sử dụng type để quyết định rằng một biến type1 được lưu trữ ở đó.

+0

Nếu tôi hiểu ví dụ của bạn, nó thực sự tương tự như ví dụ được đưa ra trong cuốn sách tham khảo tôi đã đề cập ở trên. Sau đó, đối số sẽ chuyển thành * lưu bộ nhớ *. – ysap

+0

+1 - Đây có lẽ là cách hữu ích nhất để sử dụng một liên minh trong giới hạn của tiêu chuẩn. – detly

+0

OK, sau khi bạn cập nhật, tôi chắc chắn nó thực sự tương tự như ví dụ đó. – ysap

2

Sử dụng một liên minh cho loại xảo quyệt là không di động (mặc dù không đặc biệt ít di động hơn bất kỳ phương pháp loại punning nào khác).

OTOH, một trình phân tích cú pháp, trong một ví dụ, thường có một liên minh để biểu diễn các giá trị trong biểu thức. [Chỉnh sửa: Tôi đang thay thế mẫu phân tích cú pháp bằng một ví dụ tôi hy vọng dễ hiểu hơn một chút]:

Hãy xem xét tệp tài nguyên Windows. Bạn có thể sử dụng nó để xác định các tài nguyên như menu, hộp thoại, biểu tượng, v.v. Một cái gì đó như thế này:

#define mn1 2 

mn1 MENU 
{ 
    MENUITEM "File", -1, MENUBREAK 
} 

ico1 "junk.ico" 

dlg1 DIALOG 100, 0, 0, 100, 100 
BEGIN 
    FONT 14, "Times New Roman" 
    CAPTION "Test Dialog Box" 
    ICON ico1, 700, 20, 20, 20, 20 
    TEXT "This is a string", 100, 0, 0, 100, 10 
    LTEXT "This is another string", 200, 0, 10, 100, 10 
    RTEXT "Yet a third string", 300, 0, 20, 100, 10 
    LISTBOX 400, 20, 20, 100, 100 
    CHECKBOX "A combobox", 500, 100, 100, 200, 10 
    COMBOBOX 600, 100, 210, 200, 100 
    DEFPUSHBUTTON "OK", 75, 200, 200, 50, 15 
END 

Phân tích cú pháp MENU cho định nghĩa menu; phân tích cú pháp DIALOG cho một định nghĩa hộp thoại và vân vân. Trong trình phân tích cú pháp, chúng tôi biểu thị rằng dưới dạng công đoàn:

%union { 
     struct control_def { 
       char window_text[256]; 
       int id; 
       char *class; 
       int x, y, width, height; 
       int ctrl_style; 
     } ctrl; 

     struct menu_item_def { 
       char text[256]; 
       int identifier; 
     } item; 

     struct menu_def { 
       int identiifer; 
       struct menu_item_def items[256]; 
     } mnu; 

     struct font_def { 
       int size; 
       char filename[256]; 
     } font; 

     struct dialog_def { 
       char caption[256]; 
       int id; 
       int x, y, width, height; 
       int style; 
       struct menu_def *mnu; 
       struct control_def ctrls[256]; 
       struct font_def font; 
     } dlg; 

     int value; 
     char text[256]; 
}; 

Sau đó, chúng tôi chỉ định loại sẽ được tạo bằng cách phân tích một loại biểu thức cụ thể. Ví dụ, một định nghĩa font chữ trong file sẽ trở thành một thành viên font của công đoàn:

%type <font> font 

Chỉ cần làm rõ, phần <font> đề cập đến các thành viên công đoàn đó là sản xuất và lần thứ hai "font" đề cập đến một quy tắc phân tích cú pháp mà sẽ mang lại kết quả của loại đó. Dưới đây là các quy tắc cho trường hợp cụ thể này:

font: T_FONT T_NUMBER "," T_STRING { 
    $$.size = $2; 
    strcpy($$.filename,$4); 
}; 

Vâng, về mặt lý thuyết chúng ta có thể sử dụng một struct thay vì một công đoàn ở đây - nhưng ngoài lãng phí bộ nhớ, nó chỉ không có ý nghĩa. Định nghĩa phông chữ trong tệp chỉ xác định phông chữ. Nó sẽ không có ý nghĩa để có nó tạo ra một cấu trúc bao gồm một định nghĩa menu, định nghĩa biểu tượng, số, chuỗi, vv ngoài các font nó thực sự xác định. [cuối sửa]

Tất nhiên, việc sử dụng công đoàn để tiết kiệm bộ nhớ hiếm khi rất quan trọng nữa. Mặc dù thông thường nó có vẻ hơi tầm thường, nhưng khi RAM 64 Kb là rất nhiều, tiết kiệm bộ nhớ có ý nghĩa nhiều hơn.

+0

Xin lỗi, Jerry, tôi không hiểu ví dụ của bạn. Bạn có thể vui lòng đưa ra một ví dụ cụ thể về cách liên minh bạn định nghĩa có liên quan đến các biểu thức mà nó được cho là đại diện không? – ysap

+0

@ysap: Tôi sợ điều đó - Tôi sẽ cố gắng viết lên một ngữ pháp * nhỏ * để hiển thị thêm về cách hoạt động của nó. –

+0

cảm ơn vì đã làm rõ việc sử dụng. Tôi nghĩ tôi đã hiểu được thông điệp. Bạn về cơ bản sử dụng công đoàn như một container chung. Sau đó, * type * của đối tượng thực sự chiếm công đoàn đang được lưu trữ trong một thẻ * * (tôi giả sử nó là thành viên 'value' hoặc' text'). Vì vậy, giống như các ví dụ khác trong các câu trả lời khác, đây là cơ bản một thực hiện rõ ràng của đa hình. – ysap

3

Dưới đây là một trong những sử dụng di động hợp pháp của công đoàn:

struct arg { 
    enum type t; 
    union { 
     intmax_t i; 
     uintmax_t u; 
     long double f; 
     void *p; 
     void (*fp)(void); 
    } v; 
}; 

Cùng với loại thông tin trong t, struct arg thể portably chứa bất kỳ giá trị số hoặc con trỏ. Toàn bộ cấu trúc có thể có kích thước 16-32 byte, so với 40-80 byte nếu một công đoàn chưa được sử dụng.Sự khác biệt thậm chí còn cực đoan hơn nếu tôi muốn giữ riêng từng loại số nguyên gốc (ký char, ngắn, int, dài, dài, unsigned char, unsigned short, ...) thay vì chuyển đổi chúng thành ký lớn nhất/unsigned/floating point type trước khi lưu trữ chúng.

Ngoài ra, trong khi nó không phải là "xách tay" để giả định bất cứ điều gì về các đại diện của các loại khác hơn unsigned char, nó được cho phép theo tiêu chuẩn để sử dụng một sự kết hợp với unsigned char hoặc đúc một con trỏ đến unsigned char * và truy cập dữ liệu tùy ý phản đối cách mà . Nếu bạn viết thông tin đó vào đĩa, nó sẽ không thể di chuyển sang các hệ thống khác sử dụng các biểu diễn khác nhau, nhưng nó vẫn có thể hữu ích khi chạy - ví dụ, triển khai bảng băm để lưu trữ các giá trị double. Nếu không có gì khác, nó có thể được sử dụng để thực hiện memcpy (không phải rất hữu ích vì thư viện chuẩn cung cấp cho bạn một triển khai tốt hơn nhiều) hoặc (thú vị hơn) một hàm memswap có thể hoán đổi hai vật thể có kích thước tùy ý với không gian tạm thời bị chặn. Điều này đã nhận được một chút bên ngoài miền sử dụng của các đoàn thể bây giờ và vào unsigned char * lãnh thổ diễn viên, nhưng nó liên quan chặt chẽ.

+0

R, nửa đầu của câu trả lời là rõ ràng - tiết kiệm bộ nhớ. Những gì tôi không chắc tôi hiểu là nửa thứ hai. Làm cách nào một thành viên * unsigned char * có thể được sử dụng để truy cập các thành viên khác theo cách dự đoán được? – ysap

+1

Các giá trị không phải là "có thể dự đoán" mà không biết triển khai, nhưng chúng được xác định thực hiện. Miễn là mã của bạn không đưa ra giả định về những giá trị đó là gì, nhưng chỉ sử dụng chúng một cách nội bộ, bạn ổn. Một ứng dụng có thể khác sẽ tạo ra một hàm so sánh từng byte để sử dụng với 'qsort' khi bạn không quan tâm rằng sắp xếp có bất kỳ liên quan đến thứ tự số nguyên của kiểu gốc, chỉ là nó được định nghĩa rõ ràng và kết quả có thể tái sản xuất. –

0

Xem xét Đăng ký kiểm soát phần cứng với các trường bit khác nhau. Bằng cách thiết lập các giá trị trong các trường bit của thanh ghi này, chúng ta có thể kiểm soát các chức năng khác nhau của thanh ghi.

Bằng cách sử dụng Loại dữ liệu của Liên minh, Hoặc chúng tôi có thể sửa đổi toàn bộ nội dung của sổ đăng ký hoặc một trường bit cụ thể của thanh ghi.

Đối với Ex: Xem xét một kiểu dữ liệu công đoàn như sau,

/* Data1 Bit Defintion */ 
typedef union 
{ 
    struct STRUCT_REG_DATA 
    { 
     unsigned int u32_BitField1 : 3; 
     unsigned int u32_BitField2 : 2; 
     unsigned int u32_BitField3 : 1; 
     unsigned int u32_BitField4 : 2;     
    } st_RegData; 

    unsigned int u32_RegData; 

} UNION_REG_DATA; 

Để sửa đổi toàn bộ nội dung của thanh ghi,

UNION_REG_DATA un_RegData; 
un_RegData. u32_RegData = 0x77; 

Để chỉnh sửa nội dung trường bit duy nhất (Đối với Ex Bitfield3)

un_RegData.st_RegData.u32_BitField3 = 1; 

Cả hai đều phản ánh trong cùng một bộ nhớ. Sau đó, giá trị này có thể được ghi vào giá trị của thanh ghi điều khiển phần cứng.

+2

@ barati21 - Đó chính xác là điểm của câu hỏi - bạn ** không nên ** làm điều đó nếu bạn muốn bảo đảm tính di động. Bố cục cơ bản của các thành viên công đoàn trong bộ nhớ là ** không ** được định nghĩa trong tiêu chuẩn. Đó là ** thực hiện được xác định **. – ysap

Các vấn đề liên quan