2011-11-12 36 views
6

Hôm nay tôi có một câu hỏi lạ.C++ union để đại diện cho bộ nhớ dữ liệu và loại biến vô hướng C

Bộ luật (C++)

#include <iostream> 

union name 
{ 
    int num; 
    float num2; 

}oblong; 


int main(void) 
{ 
    oblong.num2 = 27.881; 

    std::cout << oblong.num << std::endl; 

    return 0; 
} 

Bộ luật (C)

#include <stdio.h> 

int main(void) 
{ 
    float num = 27.881; 

    printf("%d\n" , num); 

    return 0; 
} 

Các Câu hỏi

  1. Như chúng ta đã biết, C++ đoàn thể hol d nhiều hơn một loại phần tử dữ liệu nhưng chỉ có một loại tại một thời điểm. Vì vậy, về cơ bản, name oblong sẽ chỉ dành một phần bộ nhớ 32 bit (vì loại lớn nhất trong công đoàn là 32-bit, int và float) và phần này có thể giữ nguyên hoặc nổi.

  2. Vì vậy, tôi chỉ gán giá trị 27.881 vào oblong.num2 (như bạn có thể thấy trên mã ở trên). Nhưng vì tò mò, tôi truy cập vào bộ nhớ bằng cách sử dụng oblong.num trỏ đến cùng một vị trí bộ nhớ. Như dự kiến, nó đã cho tôi một giá trị không phải là 27 vì cách trôi nổi và số nguyên đại diện trong bộ nhớ khác nhau, đó là lý do tại sao khi tôi sử dụng oblong.num để truy cập phần bộ nhớ, nó sẽ xử lý phần giá trị bộ nhớ đó dưới dạng số nguyên và diễn giải nó bằng cách sử dụng cách trình bày số nguyên.

  3. Tôi biết hiện tượng này cũng sẽ xảy ra trong C, đó là lý do tại sao tôi khởi tạo biến kiểu float với giá trị và sau đó đọc nó bằng cách sử dụng %d. xem ở trên. Nhưng khi tôi chạy nó, một cái gì đó kỳ lạ xảy ra, đó là giá trị của một trong tôi nhận được trong C là khác nhau từ C + +.

  4. Tại sao điều này lại xảy ra? Từ những gì tôi biết hai giá trị tôi nhận được từ hai mã cuối cùng không phải là giá trị rác, nhưng tại sao tôi nhận được các giá trị khác nhau? Tôi cũng sử dụng sizeof để xác minh cả số nguyên C và C++ và kích thước float và cả hai đều là 32-bit. Vì vậy, kích thước bộ nhớ không phải là một nguyên nhân gây ra điều này xảy ra, vì vậy những gì nhắc sự khác biệt này trong các giá trị?

+1

Trường hợp tương tự: http://stackoverflow.com/questions/2377733/how-does-this-program-work và điều này nữa: http://stackoverflow.com/questions/2398791/how-is-conversion- của-float-double-to-int-handling-in-printf –

+0

Sự khác biệt không phải là C so với C++. Bạn có thể biên dịch ví dụ thứ hai trong C++, và nhận được kết quả tương tự từ C và C++. Ví dụ đầu tiên với 'union' sẽ cần một thay đổi nhỏ để sử dụng' printf', thay vì 'std :: cout <<', nhưng sau đó bạn sẽ nhận được kết quả tương tự từ C và C++ một lần nữa. – MSalters

Trả lời

9

Trước hết, sai chuỗi định dạng printf() là hành vi không xác định. Bây giờ mà nói, đây là những gì đang thực sự xảy ra trong trường hợp của bạn:

Trong chức năng vararg như printf(), số nguyên nhỏ hơn int được thăng int và nổi nhỏ hơn double được thăng double.

Kết quả là 27.881 của bạn đang được chuyển đổi thành 8 byte kép khi được chuyển vào printf(). Do đó, biểu diễn nhị phân không còn giống như một float.

Chuỗi định dạng %d mong đợi số nguyên 4 byte. Vì vậy, có hiệu lực, bạn sẽ in 4 byte thấp hơn của biểu diễn chính xác kép của 27.881.(giả định ít người cuối)

* Thực tế (giả định nghiêm ngặt-FP), bạn đang thấy 4 byte dưới cùng của 27.881 sau khi được truyền tới float và sau đó được thăng cấp lên double.

2

Trong cả hai trường hợp, bạn đang gặp phải hành vi không xác định. Việc triển khai của bạn chỉ xảy ra để làm điều gì đó kỳ lạ.

+0

Cả hai trường hợp? Tôi nghĩ chỉ printf thứ hai là không xác định. Đầu tiên là một bãi chứa với một bitpattern cũng được xác định bắt đầu từ phao. (Đối với những gì nó có ý nghĩa là một câu hỏi khác của khóa học) – Bort

+0

Trường hợp đầu tiên là UB vì bạn đang truy cập đối tượng 'float (27.881)' thông qua một biểu thức của kiểu 'int'. Các đối tượng phải được truy cập thông qua một biểu thức của kiểu riêng của chúng, hoặc thông qua (unsigned) 'char []'. Quy tắc thứ hai là bạn có thể 'memcpy' objects (trong C; C++ hạn chế' memcpy' thành Plain Old Data). – MSalters

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