#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 %d
trong các printf
tuyên bố nghiên cứu hành vi của printf
.
Câu trả lời:
Đó là bởi vì %d
mong đợi một int
như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 double
trướ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 %d
sử 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 float
chuyể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.)
printf
là 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 double
trướ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 printf
củ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 printf
hệ 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 printf
xem 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 1606416304
bằng chương trình của bạn.
Phải nói rằng, khi bạn chuyển một hàm float
cho 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 a
là 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 double
hóa ra là 0, đó có thể là lý do tại sao bạn nhận được 0
như là đầu ra của printf
cuộ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ố. printf
với %d
lấy int
đối số là 42 và đối số thứ hai %d
có thể in rác vì không có int
đối số thứ hai .
Bộ %d
chỉ định cho biết printf
mong đợ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 float
thự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ì printf
in 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 int
và tám byte double
( float
được chuyển đổi thành double
bên trong printf()
), bốn byte đầu tiên a
sẽ 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
.