#include <stdio.h>
int main() {
float a = 1234.5f;
printf("%d\n", a);
return 0;
}
Nó hiển thị một 0!! Làm thế nào là có thể? Lý luận là gì?
Tôi đã cố tình đặt một %dtrong các printftuyên bố nghiên cứu hành vi của printf.
Câu trả lời:
Đó là bởi vì %dmong đợi một intnhưng bạn đã cung cấp một phao.
Sử dụng %e/ %f/ %gđể in phao.
Tại sao số 0 được in: Số dấu phẩy động được chuyển đổi thành doubletrước khi gửi đến printf. Số 1234,5 trong biểu diễn kép ở số cuối nhỏ là
00 00 00 00 00 4A 93 40
A %dsử dụng một số nguyên 32 bit, vì vậy số 0 được in. (Như một bài kiểm tra, bạn có thể printf("%d, %d\n", 1234.5f);Bạn có thể nhận được đầu ra 0, 1083394560.)
Đối với lý do tại sao floatchuyển đổi thành double, như nguyên mẫu của printf int printf(const char*, ...), từ 6.5.2.2/7,
Kí hiệu dấu chấm lửng trong bộ khai báo nguyên mẫu hàm khiến việc chuyển đổi kiểu đối số dừng lại sau tham số được khai báo cuối cùng. Việc thăng hạng đối số mặc định được thực hiện trên các đối số theo sau.
và từ 6.5.2.2/6,
Nếu biểu thức biểu thị hàm được gọi có kiểu không bao gồm nguyên mẫu, thì việc thăng hạng số nguyên được thực hiện trên mỗi đối số và các đối số có kiểu
floatđược thăng cấpdouble. Chúng được gọi là quảng cáo đối số mặc định .
(Cảm ơn Alok đã tìm ra điều này.)
printflà một hàm biến thiên, và tiêu chuẩn nói rằng đối với các hàm biến thiên, a floatđược chuyển đổi thành doubletrước khi chuyển.
printf()gọi Hành vi không xác định .
Về mặt kỹ thuật không có sự printf , mỗi thư viện cụ riêng của mình, và do đó, phương pháp của bạn cố gắng nghiên cứu printfcủa hành vi bằng cách thực hiện những gì bạn đang làm sẽ không được sử dụng nhiều. Bạn có thể đang cố gắng nghiên cứu hoạt động của printfhệ thống của mình và nếu vậy, bạn nên đọc tài liệu và xem mã nguồn printfxem nó có sẵn cho thư viện của bạn hay không.
Ví dụ: trên Macbook của tôi, tôi nhận được đầu ra 1606416304bằng chương trình của bạn.
Phải nói rằng, khi bạn chuyển một hàm floatcho một hàm variadic, hàm floatđược chuyển dưới dạng a double. Vì vậy, chương trình của bạn tương đương với việc khai báo alà a double.
Để kiểm tra các byte của a double, bạn có thể xem câu trả lời này cho câu hỏi gần đây trên SO.
Hãy làm điều đó:
#include <stdio.h>
int main(void)
{
double a = 1234.5f;
unsigned char *p = (unsigned char *)&a;
size_t i;
printf("size of double: %zu, int: %zu\n", sizeof(double), sizeof(int));
for (i=0; i < sizeof a; ++i)
printf("%02x ", p[i]);
putchar('\n');
return 0;
}
Khi tôi chạy chương trình trên, tôi nhận được:
size of double: 8, int: 4
00 00 00 00 00 4a 93 40
Vì vậy, bốn byte đầu tiên doublehóa ra là 0, đó có thể là lý do tại sao bạn nhận được 0như là đầu ra của printfcuộc gọi của mình .
Để có kết quả thú vị hơn, chúng ta có thể thay đổi chương trình một chút:
#include <stdio.h>
int main(void)
{
double a = 1234.5f;
int b = 42;
printf("%d %d\n", a, b);
return 0;
}
Khi tôi chạy chương trình trên trên Macbook của mình, tôi nhận được:
42 1606416384
Với cùng một chương trình trên máy Linux, tôi nhận được:
0 1083394560
intđối số được truyền trong các thanh ghi khác với các doubleđối số. printfvới %dlấy intđối số là 42 và đối số thứ hai %dcó thể in rác vì không có intđối số thứ hai .
Bộ %dchỉ định cho biết printfmong đợi một số nguyên. Vì vậy, bốn (hoặc hai, tùy thuộc vào nền tảng) byte đầu tiên của float được intepreted như một số nguyên. Nếu chúng bằng 0, số 0 sẽ được in
Biểu diễn nhị phân của 1234.5 giống như
1.00110100101 * 2^10 (exponent is decimal ...)
Với trình biên dịch C đại diện floatthực sự là các giá trị kép IEEE754, các byte sẽ là (nếu tôi không mắc lỗi)
01000000 10010011 01001010 00000000 00000000 00000000 00000000 00000000
Trên hệ thống Intel (x86) có ít nội dung (tức là byte ít quan trọng nhất đến trước), chuỗi byte này được đảo ngược để bốn byte đầu tiên bằng 0. Đó là, những gì printfin ra ...
Xem bài viết này trên Wikipedia về biểu diễn dấu phẩy động theo IEEE754.
Đó là vì sự biểu diễn của một float trong hệ nhị phân. Việc chuyển đổi thành một số nguyên để lại nó bằng 0.
Bởi vì bạn đã gọi hành vi không xác định: bạn đã vi phạm hợp đồng của phương thức printf () bằng cách nói dối nó về các loại tham số của nó, vì vậy trình biên dịch có thể tự do làm bất cứ điều gì nó muốn. Nó có thể làm cho đầu ra chương trình "dksjalk is a ninnyhead !!!" và về mặt kỹ thuật thì nó vẫn đúng.
Lý do là đó printf()là một chức năng khá ngu ngốc. Nó không kiểm tra các loại ở tất cả. Nếu bạn nói đối số đầu tiên là một int(và đây là những gì bạn đang nói với %d), nó tin bạn và nó chỉ cần số byte cần thiết cho một int. Trong trường hợp này, khi máy tính của bạn sử dụng bốn byte intvà tám byte double( floatđược chuyển đổi thành doublebên trong printf()), bốn byte đầu tiên asẽ chỉ là số 0 và điều này sẽ được in.
%d là số thập phân
%f là phao
xem thêm những thứ này ở đây .
Bạn đang nhận được 0 vì số thực và số nguyên được biểu diễn khác nhau.
Bạn chỉ cần sử dụng mã định dạng thích hợp (% d,% f,% s, v.v.) với kiểu dữ liệu có liên quan (int, float, string, v.v.).
Nó không phải là một số nguyên. Hãy thử sử dụng %f.