2009-09-06 33 views
10

Lấy sau:C: In số lớn

#include <stdio.h> 

main() { 
    unsigned long long verybig = 285212672; 

    printf("Without variable : %llu\n", 285212672); 
    printf("With variable : %llu", verybig); 
} 

Đây là sản phẩm của chương trình trên:

Without variable : 18035667472744448 
With variable : 285212672 

Như bạn có thể nhìn thấy từ trên, khi printf được truyền số là một hằng số, nó in một số số không chính xác lớn, nhưng khi giá trị được lưu trữ lần đầu tiên trong một biến, printf in số chính xác.

Lý do đằng sau điều này là gì?

Trả lời

25

Hãy thử 285212672ULL; nếu bạn viết nó mà không có hậu tố, bạn sẽ thấy trình biên dịch xử lý nó như một số nguyên thông thường. Lý do nó làm việc trong một biến là bởi vì số nguyên đang được đúc lên đến một unsigned long long trong nhiệm vụ, để giá trị được chuyển đến printf() là đúng loại.

Và trước khi bạn hỏi, không, trình biên dịch có thể không phải là đủ thông minh để hình dung nó ra từ "%llu "trong chuỗi định dạng printf(). Đó là một mức độ trừu tượng khác nhau. Trình biên dịch chịu trách nhiệm về cú pháp ngôn ngữ , printf() ngữ nghĩa không phải là một phần của cú pháp, đó là một chức năng thư viện runtime (không khác gì thực sự từ chức năng của riêng bạn ngoại trừ việc nó được bao gồm trong tiêu chuẩn).

Xét đoạn mã sau cho một int 32-bit và 64 -bit hệ thống dài không có ký hiệu:

#include <stdio.h> 

int main (void) { 
    printf ("%llu\n",1,2); 
    printf ("%llu\n",1ULL,2); 
    return 0; 
} 

mà kết quả đầu ra:

8589934593 
1 

Trong trường hợp đầu tiên, hai 32-bit số nguyên 1 và 2 được đẩy vào stack và printf() giải thích rằng khi một giá trị duy nhất 64-bit ULL, 2 x 2 + 1. Đối số 2 đang được vô tình bao gồm trong giá trị ULL.

Trong giây, bạn thực sự đẩy giá trị 1 bit 64 bit và số nguyên 32 bit thừa 2, bị bỏ qua.

Lưu ý rằng điều này "thoát khỏi bước" giữa chuỗi định dạng của bạn và đối số thực tế của bạn là một ý tưởng tồi. Một cái gì đó như:

printf ("%llu %s %d\n", 0, "hello", 0); 

có thể sụp đổ vì 32-bit "hello" con trỏ sẽ được tiêu thụ bởi các %llu%s sẽ cố gắng bỏ tham khảo trận chung kết 0 tranh cãi. "Hình ảnh" sau đây minh họa điều này (giả sử rằng các ô là 32 bit và chuỗi "hello" được lưu trữ ở 0xbf000000.

What you pass  Stack frames  What printf() uses 
       +------------+ 
0    | 0   | \ 
       +------------+ > 64-bit value for %llu. 
"hello"   | 0xbf000000 |/
       +------------+ 
0    | 0   | value for %s (likely core dump here). 
       +------------+ 
       | ?   | value for %d (could be anything). 
       +------------+ 
+1

nhưng tôi nghĩ rằng trình biên dịch là đủ thông minh để tìm ra% u trong printf định dạng spec, hãy thử printf ("% d% u", ~ 0, ~ 0) .. cả hai sẽ in các giá trị như mong đợi .. – sud03r

+0

Không - những kiểu dữ liệu có cùng kích thước - đó là printf() tìm ra - thử % d với 'a'. – paxdiablo

+0

Pax: Điều đó cũng tốt, các ký tự chữ cái là hằng số nguyên. – caf

3

285212672 là giá trị int. printf mong đợi một số unsigned long long và bạn đang chuyển qua số int. Do đó, nó sẽ mất nhiều byte hơn ngăn xếp hơn là bạn đã chuyển một giá trị thực và in ra rác. Khi bạn đặt nó vào biến số unsigned long long trước khi chuyển nó đến hàm, nó sẽ được thăng lên unsigned long long trong dòng gán và bạn chuyển giá trị đó đến printf hoạt động chính xác.

0

Kiểu dữ liệu chỉ đơn giản là cách diễn giải nội dung của vị trí bộ nhớ.
trong trường hợp đầu tiên giá trị không đổi được lưu trữ ở vị trí bộ nhớ chỉ đọc dưới dạng int, printf cố gắng giải thích địa chỉ này là 8 byte vì nó được chỉ thị rằng giá trị được lưu trữ dài trong quá trình in giá trị rác.
Trong trường hợp thứ hai printf cố gắng giải thích một giá trị dài dài là 8 byte và nó in những gì được mong đợi.

5

Đó là giá trị chỉ ra rằng một số trình biên dịch đưa ra một cảnh báo hữu ích cho trường hợp này - ví dụ, đây là những gì GCC nói về mã của bạn:

x.c: In function ‘main’: 
x.c:6: warning: format ‘%llu’ expects type ‘long long unsigned int’, but argument 2 has type ‘int’ 
+1

Tuy nhiên, một lý do khác không bỏ qua việc biên soạn * cảnh báo *. – reuben

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