2011-12-14 32 views
14

Tôi muốn diễn giải lại dữ liệu của một loại dưới dạng một loại khác theo cách di động (C99). Tôi đang không phải nói về truyền, tôi muốn một sự giải thích của một số dữ liệu nhất định. Ngoài ra, bởi xách tay Tôi có nghĩa là nó không vi phạm quy tắc C99 - Tôi làm không có nghĩa là giá trị được giải thích bằng nhau trên tất cả các hệ thống.Tái phân tích dữ liệu di động

Tôi biết 3 cách khác nhau để diễn giải dữ liệu, nhưng chỉ có hai trong số này là xách tay:

  1. Đây không phải là cầm tay - nó phá vỡ các quy tắc nghiêm ngặt răng cưa.

    /* #1 Type Punning */ 
    
    float float_value = 3.14; 
    int *int_pointer = (int *)&float_value; 
    int int_value = *int_pointer; 
    
  2. này phụ thuộc nền tảng, bởi vì nó đọc một giá trị int từ sự kết hợp sau khi viết một float vào nó. Nhưng nó không phá vỡ bất kỳ quy tắc C99, vì vậy mà nên làm việc (nếu sizeof(int) == sizeof(float)).

    /* #2 Union Punning */ 
    
    union data { 
        float float_value; 
        int int_value; 
    }; 
    
    union data data_value; 
    data_value.float_value = 3.14; 
    int int_value = data_value.int_value; 
    
  3. nên không sao đâu, miễn là sizeof(int) == sizeof(float)

    /* #3 Copying */ 
    
    float float_value = 3.14; 
    int int_value = 0; 
    memcpy(&int_value, &float_value, sizeof(int_value)); 
    

Câu hỏi của tôi:

  1. là đúng này?
  2. Bạn có biết các cách khác để diễn giải lại dữ liệu theo cách di động không?
+0

Các $ float_value nên & float_value? – wildplasser

+0

@wildplasser đã được sửa! thanks – Johannes

+0

Giải thích lại dữ liệu cho kết quả phụ thuộc vào nền tảng. Điều này có thể hoạt động như thế nào? Ví dụ, các nền tảng khác nhau có thể đại diện cho 'float' khác nhau trong bộ nhớ. –

Trả lời

17

Giải pháp 2 di động - loại thông qua công đoàn luôn hợp pháp trong C99 và được làm rõ bằng TC3, đã thêm chú thích sau vào phần 6.5.2.3:

Nếu thành viên sử dụng để truy cập vào các nội dung của một đối tượng công đoàn không phải là giống như thành viên cuối cùng dùng để lưu trữ một giá trị trong đối tượng, phần thích hợp của đại diện đối tượng của giá trị là giải thích lại như một biểu diễn đối tượng trong kiểu mới như được mô tả trong 6.2.6 (một quá trình đôi khi được gọi là "loại punning"). Đây có thể là biểu diễn bẫy .

Phụ lục J vẫn liệt kê nó như là hành vi unspecfied, đó là một khiếm khuyết nổi tiếng và đã được sửa chữa với C11, mà thay đổi

Giá trị của một thành viên đoàn khác hơn là người cuối cùng được lưu trữ vào [là không xác định]

để

các giá trị của byte tương ứng với đoàn viên khác so với cái được lưu trữ cuối cùng vào [là không xác định]

Nó không phải là một thỏa thuận lớn như phụ lục chỉ là thông tin, chứ không phải bản quy phạm.

Hãy ghi nhớ rằng bạn vẫn có thể kết thúc với hành vi không xác định, ví dụ như

  • bằng cách tạo ra một cái bẫy đại diện
  • do vi phạm quy tắc răng cưa trong trường hợp của các thành viên với kiểu con trỏ (mà không cần phải chuyển đổi qua type-punning anyway vì không cần đại diện con trỏ thống nhất)
  • nếu các thành viên công đoàn có kích thước khác nhau - chỉ byte của thành viên được sử dụng lần cuối trong cửa hàng có giá trị được chỉ định; nói riêng, lưu trữ các giá trị trong một viên nhỏ hơn cũng có thể làm mất hiệu lực byte trailing của một thành viên lớn hơn
  • nếu một thành viên chứa byte đệm, mà luôn luôn có giá trị không xác định
0

để an toàn, tôi muốn đi với mảng byte (unsigned char) thay vì 'int' để giữ giá trị.

+0

Sau đó, một câu hỏi khác nảy sinh: Làm cách nào để diễn giải lại 'int' (hoặc một kiểu khác) như một' unsigned char [] '? – delnan

+0

@Khi một dấu gạch ngang sẽ giải thích dữ liệu dưới dạng chuỗi byte. những gì tôi cần là một giải thích lại – Johannes

+0

Để được an toàn từ những gì? – zvrba

0

loại dữ liệu int là ví dụ về loại không di động vì tính cuối cùng có thể thay đổi thứ tự byte giữa các nền tảng.

nếu bạn muốn di động, bạn cần phải xác định các loại của riêng mình, sau đó triển khai chúng trên từng nền tảng mà bạn muốn chuyển đến. Sau đó, xác định các phương thức chuyển đổi cho các loại dữ liệu của bạn. Đó là như xa như tôi biết cách duy nhất để có quyền kiểm soát đầy đủ các lệnh byte, vv

+0

cảm ơn câu trả lời của bạn - tôi nghĩ rằng tôi không rõ rằng tất cả những gì tôi cần là ansi c99 và giá trị xác định – Johannes

2
  1. Các giải pháp công đoàn được quy định như memcpy một trong C (AFAIK, nó là UB trong C++), xem DR283

  2. có thể đúc một con trỏ đến một con trỏ đến (ký/unsigned /) char, vì vậy

    unsigned char *ptr = (unsigned char*)&floatVar; 
    

    và sau đó truy cập vào ptr [0] để ptr [sizeof (floatVar) -1] là hợp pháp.

+0

đó là sự thật, nhưng đó sẽ là sự tái diễn giải như là một bytearray - và tôi cần một int – Johannes

+1

Đó là UB trong C++ ngoại trừ trường hợp đặc biệt của cấu trúc POD 'Nếu một tổ hợp POD chứa một số cấu trúc POD có chung chuỗi ban đầu (9.2) , và nếu một đối tượng của loại hiệp hội POD này có chứa một trong các cấu trúc POD, nó được phép kiểm tra chuỗi ban đầu chung của bất kỳ thành viên POD-struct' –

+0

@Dave, vâng, tôi đã không đề cập đến bản hack X bởi vì nó không cung cấp một cách để làm kiểu phạt. – AProgrammer

0

Nếu bạn muốn tránh sự cai trị răng cưa chặt chẽ, đầu tiên bạn cần đúc để một con trỏ char:

float float_value = 3.14; 
int *int_pointer = (int *)(char *)&float_value; 
int int_value = *int_pointer; 

Lưu ý tuy nhiên, bạn có thể có sizeof(int) > sizeof(float), trong trường hợp bạn vẫn nhận được không xác định hành vi

+3

theo như tôi biết, truyền * từ * char * cũng phá vỡ quy tắc – Johannes

+1

đây vẫn là UB - loại hiệu quả là thuộc tính của đối tượng (nghĩa là khối bộ nhớ), không phải con trỏ được sử dụng để truy cập – Christoph

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