Như những người khác đã lưu ý, hãy đưa con trỏ đến loại không phải char để trỏ đến một loại không phải char khác và sau đó dereferencing là hành vi không xác định.
printf("%08lx\n", *(unsigned long *)&fValue)
gọi hành vi không xác định này không nhất thiết có nghĩa là chạy chương trình cố gắng thực hiện một sự cố như vậy sẽ dẫn đến xóa ổ cứng hoặc làm cho các mũi mũi phun ra khỏi mũi (hai dấu hiệu của hành vi không xác định). Trên máy tính trong đó sizeof(unsigned long)==sizeof(float)
và trên cả hai loại có cùng yêu cầu căn chỉnh, printf
hầu như chắc chắn sẽ làm những gì người ta mong đợi, đó là in biểu diễn hex của giá trị dấu chấm động đang được đề cập.
Điều này không đáng ngạc nhiên. Tiêu chuẩn C công khai mời các triển khai để mở rộng ngôn ngữ. Nhiều người trong số các phần mở rộng là trong các lĩnh vực được, nói đúng, hành vi không xác định. Ví dụ, hàm POSIX dlsym trả về một void*
, nhưng hàm này thường được sử dụng để tìm địa chỉ của một hàm thay vì một biến toàn cầu. Điều này có nghĩa là con trỏ void được trả về bởi dlsym
cần phải được truyền đến một con trỏ hàm và sau đó dereferenced để gọi hàm. Đây rõ ràng là hành vi không xác định, nhưng nó vẫn hoạt động trên bất kỳ nền tảng tuân thủ POSIX nào. Điều này sẽ không hoạt động trên một máy kiến trúc Harvard mà trên đó các con trỏ tới các hàm có các kích thước khác với các con trỏ tới dữ liệu.
Tương tự, hãy đưa con trỏ đến số float
đến con trỏ đến số nguyên chưa dấu và sau đó dereferencing sẽ hoạt động trên hầu hết mọi máy tính. a float
.
Điều đó nói rằng, sử dụng unsigned long
cũng có thể khiến bạn gặp rắc rối. Trên máy tính của tôi, một unsigned long
dài 64 bit và có yêu cầu căn chỉnh 64 bit. Điều này không tương thích với phao. Sẽ tốt hơn nếu sử dụng uint32_t
- trên máy tính của tôi.
Các công đoàn Hack là một trong những con đường xung quanh đống lộn xộn này:
typedef struct {
float fval;
uint32_t ival;
} float_uint32_t;
Gán một float_uint32_t.fval
và truy cập từ một `` float_uint32_t.ival` từng là hành vi không xác định. Đó không còn là trường hợp trong C. Không có trình biên dịch mà tôi biết thổi mũi quỷ cho công đoàn hack. Đây không phải là UB trong C++. Nó là bất hợp pháp. Cho đến C++ 11, một trình biên dịch C++ tuân thủ phải phàn nàn là tuân thủ.
Bất kỳ cách nào tốt hơn xung quanh đống lộn xộn này là sử dụng các định dạng %a
, mà đã là một phần của chuẩn C từ năm 1999:
printf ("%a\n", fValue);
Đây là đơn giản, dễ dàng, di động, và không có cơ hội của hành vi không xác định. Điều này in đại diện hệ thập lục phân/nhị phân của giá trị điểm nổi chính xác kép trong câu hỏi. Vì printf
là một chức năng cổ, tất cả các đối số float
được chuyển đổi thành double
trước cuộc gọi đến printf
. Chuyển đổi này phải chính xác theo phiên bản 1999 của tiêu chuẩn C. Người ta có thể nhận giá trị chính xác đó thông qua một cuộc gọi đến scanf
hoặc các chị em của nó.
Đây là hành vi không xác định. Đó là điều mà mọi người đã làm trước khi C được chuẩn hóa vào năm 1989 và một vài người không theo kịp thời điểm –