Làm thế nào một người có thể in một biến size_t có thể di chuyển bằng cách sử dụng họ printf?


405

Tôi có một loại biến size_t, và tôi muốn in nó bằng cách sử dụng printf(). Tôi sử dụng định dạng định dạng nào để in nó một cách hợp lý?

Trong máy 32 bit, %ucó vẻ đúng. Tôi biên dịch với g++ -g -W -Wall -Werror -ansi -pedantic, và không có cảnh báo. Nhưng khi tôi biên dịch mã đó trong máy 64 bit, nó sẽ tạo ra cảnh báo.

size_t x = <something>;
printf("size = %u\n", x);

warning: format '%u' expects type 'unsigned int', 
    but argument 2 has type 'long unsigned int'

Cảnh báo sẽ biến mất, như mong đợi, nếu tôi thay đổi điều đó thành %lu.

Câu hỏi là, làm thế nào tôi có thể viết mã, để nó biên dịch cảnh báo miễn phí trên cả máy 32 và 64 bit?

Chỉnh sửa: Như một cách giải quyết, tôi đoán một câu trả lời có thể là "đúc" biến thành một số nguyên đủ lớn, giả sử unsigned longvà in bằng cách sử dụng %lu. Điều đó sẽ làm việc trong cả hai trường hợp. Tôi đang tìm kiếm nếu có bất kỳ ý tưởng khác.


4
truyền tới unsigned longlà tùy chọn tốt nhất nếu việc triển khai libc của bạn không hỗ trợ công cụ zsửa đổi; tiêu chuẩn C99 khuyến nghị size_tkhông nên có thứ hạng chuyển đổi số nguyên lớn hơn long, vì vậy bạn an toàn một cách hợp lý
Christoph



1
Trên nền tảng Windows size_t có thể lớn hơn dài. Vì lý do tương thích, dài luôn là 32 bit nhưng size_t có thể là 64 bit. Vì vậy, truyền tới dấu dài không dấu có thể mất một nửa số bit. Xin lỗi :-)
Bruce Dawson

Câu trả lời:


483

Sử dụng công cụ zsửa đổi:

size_t x = ...;
ssize_t y = ...;
printf("%zu\n", x);  // prints as unsigned decimal
printf("%zx\n", x);  // prints as hex
printf("%zd\n", y);  // prints as signed decimal

7
+1. Đây có phải là một bổ sung C99 hay điều này cũng áp dụng cho C ++ (Tôi không có C90 tiện dụng)?
avakar

6
đó là một bổ sung C99 và không có trong danh sách các printf()sửa đổi độ dài của bản nháp C ++ 0x từ 2009-11-09 (bảng 84 trên trang 672)
Christoph

3
@Christoph: Cũng không có trong dự thảo mới nhất, n3035.
GManNickG

11
@avakar @Adam Rosenfield @Christoph @GMan: Tuy nhiên, trong n3035 §1.2 Tham chiếu tiêu chuẩn, chỉ có tiêu chuẩn C99 được tham chiếu và §17.6.1.2 / 3 của cùng một tiểu bang "Các cơ sở của thư viện chuẩn C được cung cấp." Tôi sẽ giải thích điều này có nghĩa là, trừ khi có quy định khác, mọi thứ trong thư viện tiêu chuẩn C99 là một phần của thư viện chuẩn C ++ 0x, bao gồm các công cụ định dạng bổ sung trong C99.
James McNellis

9
@ArunSaha: Đây là một tính năng chỉ có C99, không phải C ++. Nếu bạn muốn biên dịch nó -pedantic, bạn sẽ cần có một trình biên dịch hỗ trợ bản nháp C ++ 1x (rất khó xảy ra) hoặc bạn sẽ cần chuyển mã của mình vào một tệp được biên dịch thành C99. Mặt khác, tùy chọn duy nhất của bạn là truyền các biến của bạn đến unsigned long longvà sử dụng %lluđể có thể di động tối đa.
Adam Rosenfield

88

Có vẻ như nó thay đổi tùy thuộc vào trình biên dịch bạn đang sử dụng (blech):

... Và tất nhiên, nếu bạn đang sử dụng C ++, bạn có thể sử dụng coutthay thế theo đề xuất của AraK .


3
zcũng được hỗ trợ bởi newlib (tức là cygwin)
Christoph

7
%zdlà không chính xác cho size_t; Nó đúng cho loại đã ký tương ứng size_t, nhưng size_tbản thân nó là loại không dấu.
Keith Thompson

1
@KeithThndry: Tôi cũng đã đề cập đến %zu(và %zxtrong trường hợp họ muốn hex). Đúng như %zuvậy có lẽ nên có đầu tiên trong danh sách. Đã sửa.
TJ Crowder

10
@TJCrowder: Tôi không nghĩ %zdnên có trong danh sách. Tôi không thể nghĩ ra bất kỳ lý do nào để sử dụng %zdhơn là %zuin một size_tgiá trị. Nó thậm chí không hợp lệ (có hành vi không xác định) nếu giá trị vượt quá SIZE_MAX / 2. (Để đầy đủ, bạn có thể đề cập đến %zobát phân.)
Keith Thompson

2
@FUZxxl: POSIX không yêu cầu đó ssize_tlà loại đã ký tương ứng size_t, vì vậy nó không được đảm bảo khớp "%zd". (Nó có lẽ là trên hầu hết các triển khai.) Pubs.opengroup.org/onlinepubs/9699919799/basedefs/...
Keith Thompson

59

Đối với C89, sử dụng %luvà chuyển giá trị thành unsigned long:

size_t foo;
...
printf("foo = %lu\n", (unsigned long) foo);

Đối với C99 trở lên, sử dụng %zu:

size_t foo;
...
printf("foo = %zu\n", foo);

7
Xem xét năm 2013, đề xuất "Dành cho C99 trở đi" & "Đối với C99 trước:". Câu trả lời tốt nhất.
chux - Phục hồi lại

8
Đừng làm điều này. Nó sẽ thất bại trên Windows 64 bit trong đó size_t là 64 bit và dài là 32 bit.
Yttrill

1
@Yttrill: Câu trả lời cho các cửa sổ 64 bit là gì?
John Bode

1
@JohnBode Có lẽ unsigned long long?
James Ko

2
Hoặc: bạn có thể truyền tới a uint64_tvà sau đó sử dụng PRIu64macro từ inttypes.h, có chứa định dạng định dạng.
James Ko

9

Mở rộng câu trả lời của Adam Rosenfield cho Windows.

Tôi đã thử nghiệm mã này với trên cả bản xem trước VS2013 Update 4 và VS2015:

// test.c

#include <stdio.h>
#include <BaseTsd.h> // see the note below

int main()
{
    size_t x = 1;
    SSIZE_T y = 2;
    printf("%zu\n", x);  // prints as unsigned decimal
    printf("%zx\n", x);  // prints as hex
    printf("%zd\n", y);  // prints as signed decimal
    return 0;
}

VS2015 tạo đầu ra nhị phân:

1
1
2

trong khi cái được tạo bởi VS2013 nói:

zu
zx
zd

Lưu ý: ssize_tlà tiện ích mở rộng POSIX và SSIZE_Ttương tự trong Kiểu dữ liệu Windows , do đó tôi đã thêm <BaseTsd.h>tham chiếu.

Ngoài ra, ngoại trừ các tiêu đề C99 / C11 theo sau, tất cả các tiêu đề C99 đều có sẵn trong bản xem trước VS2015:

C11 - <stdalign.h>
C11 - <stdatomic.h>
C11 - <stdnoreturn.h>
C99 - <tgmath.h>
C11 - <threads.h>

Ngoài ra, C11 <uchar.h>hiện được bao gồm trong bản xem trước mới nhất.

Để biết thêm chi tiết, xem danh sách mới này để biết sự phù hợp tiêu chuẩn.


Bản cập nhật 5 của VS2013 tạo ra kết quả giống như Bản cập nhật 4 đã cho bạn.
Nathan Kidd

6

Đối với những người nói về việc làm điều này trong C ++ không nhất thiết phải hỗ trợ các tiện ích mở rộng C99, thì tôi thực sự khuyên bạn nên sử dụng định dạng boost ::. Điều này làm cho câu hỏi kích thước loại size_t moot:

std::cout << boost::format("Sizeof(Var) is %d\n") % sizeof(Var);

Vì bạn không cần chỉ định kích thước ở định dạng boost ::, bạn chỉ có thể lo lắng về cách bạn muốn hiển thị giá trị.


4
Có lẽ muốn %urồi.
GManNickG

5
std::size_t s = 1024;
std::cout << s; // or any other kind of stream like stringstream!

8
Vâng, nhưng người hỏi yêu cầu cụ thể cho một printfspecifier. Tôi đoán rằng họ có một số ràng buộc không có căn cứ khác khiến việc sử dụng std::coutmột vấn đề.
Donal Fellows

1
@Donal Tôi tự hỏi loại luồng C ++ nào có thể tạo ra trong một dự án C ++!
AraK

9
@AraK. Họ rất chậm? Họ thêm rất nhiều byte vì lý do không nhiều. ArunSaha chỉ muốn biết kiến ​​thức cá nhân của riêng mình? Sở thích cá nhân (tôi thích stdio để fux mình). Có nhiều lý do.
KitsuneYMG

1
@TKCrowder: Vâng, yêu cầu ban đầu đã nói rằng cần có giải pháp C (thông qua gắn thẻ) và có lý do chính đáng để không sử dụng luồng trong C ++, ví dụ: nếu bộ mô tả định dạng đầu ra được lấy từ danh mục tin nhắn. (Bạn có thể viết một trình phân tích cú pháp cho các tin nhắn và sử dụng các luồng nếu bạn muốn, nhưng đó là rất nhiều công việc khi bạn có thể tận dụng mã hiện có.)
Donal Fellows

1
@Donal: Các thẻ là C và C ++. Tôi không ủng hộ công cụ truyền phát I / O của C ++ (tôi không phải là người hâm mộ của nó), chỉ nêu ra rằng câu hỏi ban đầu không * "... hỏi đặc tả cho một công printfcụ xác định."
TJ Crowder


2

Như AraK đã nói, giao diện luồng c ++ sẽ luôn hoạt động tốt.

std :: size_t s = 1024; std :: cout << s; // hoặc bất kỳ loại luồng nào khác như chuỗi dòng!

Nếu bạn muốn C stdio, không có câu trả lời di động nào cho điều này đối với một số trường hợp "di động". Và nó trở nên xấu xí như bạn đã thấy, chọn các cờ định dạng sai có thể đưa ra cảnh báo trình biên dịch hoặc đưa ra đầu ra không chính xác.

C99 đã cố gắng giải quyết vấn đề này bằng các định dạng inttypes.h như "%" PRIdMAX "\ n". Nhưng cũng giống như "% zu", không phải ai cũng hỗ trợ c99 (như MSVS trước năm 2013). Có các tệp "msinttypes.h" nổi xung quanh để giải quyết vấn đề này.

Nếu bạn chuyển sang một loại khác, tùy thuộc vào cờ, bạn có thể nhận được cảnh báo của trình biên dịch để cắt bớt hoặc thay đổi dấu hiệu. Nếu bạn đi tuyến đường này chọn một loại kích thước cố định có liên quan lớn hơn. Một trong những ký tự dài không dấu và "% llu" hoặc dài không dấu "% lu" sẽ hoạt động, nhưng llu cũng có thể làm mọi thứ chậm lại trong một thế giới 32 bit quá lớn. (Chỉnh sửa - mac của tôi đưa ra cảnh báo trong 64 bit cho% llu không khớp với size_t, mặc dù% lu,% llu và size_t đều có cùng kích thước. Và% lu và% llu không cùng kích thước trên MSVS2012 của tôi. bạn có thể cần truyền + sử dụng định dạng phù hợp.)

Đối với vấn đề đó, bạn có thể đi với các loại kích thước cố định, chẳng hạn như int64_t. Nhưng chờ đã! Bây giờ chúng tôi trở lại c99 / c ++ 11 và MSVS cũ lại thất bại. Ngoài ra, bạn cũng có các phôi (ví dụ map.size () không phải là loại kích thước cố định)!

Bạn có thể sử dụng tiêu đề hoặc thư viện của bên thứ 3 như boost. Nếu bạn chưa sử dụng một cái, bạn có thể không muốn thổi phồng dự án của mình theo cách đó. Nếu bạn sẵn sàng thêm một chỉ cho vấn đề này, tại sao không sử dụng các luồng c ++ hoặc biên dịch có điều kiện?

Vì vậy, bạn đang xuống các luồng c ++, biên dịch có điều kiện, khung bên thứ 3 hoặc một loại di động nào đó phù hợp với bạn.


-1

Nó sẽ cảnh báo bạn nếu bạn chuyển một số nguyên không dấu 32 bit sang định dạng% lu? Sẽ ổn vì chuyển đổi được xác định rõ và không mất bất kỳ thông tin nào.

Tôi đã nghe nói rằng một số nền tảng xác định các macro trong <inttypes.h>đó bạn có thể chèn vào chuỗi định dạng bằng chữ nhưng tôi không thấy tiêu đề đó trên trình biên dịch Windows C ++ của mình, điều đó có nghĩa là nó có thể không phải là đa nền tảng.


1
Hầu hết các trình biên dịch sẽ không cảnh báo bạn nếu bạn chuyển một cái gì đó có kích thước sai vào printf. GCC là một ngoại lệ. inttypes.h đã được định nghĩa trong C99, vì vậy bất kỳ trình biên dịch C nào tuân thủ C99 sẽ có nó, mà bây giờ tất cả chúng phải là tất cả. Tuy nhiên, bạn có thể phải bật C99 bằng cờ biên dịch. Trong mọi trường hợp, intttypes.h không xác định định dạng cụ thể cho size_t hoặc ptrdiff_t, vì chúng được quyết định là đủ quan trọng để có các chỉ định kích thước riêng của chúng tương ứng là 'z' và 't'.
swestrup

Nếu bạn sử dụng %lu, bạn nên bỏ size_tgiá trị unsigned long. Không có chuyển đổi ngầm định (ngoài các chương trình khuyến mãi) cho các đối số printf.
Keith Thompson

-2

C99 định nghĩa "% zd", v.v. (cảm ơn những người bình luận) Không có trình xác định định dạng di động nào cho C ++ - bạn có thể sử dụng %ptừ woulkd trong hai trường hợp này, nhưng cũng không phải là lựa chọn di động và đưa ra giá trị ở dạng hex.

Ngoài ra, sử dụng một số luồng (ví dụ: chuỗi dòng) hoặc thay thế printf an toàn, chẳng hạn như Định dạng Boost . Tôi hiểu rằng lời khuyên này chỉ được sử dụng hạn chế (và không yêu cầu C ++). (Chúng tôi đã sử dụng một cách tiếp cận tương tự phù hợp với nhu cầu của chúng tôi khi triển khai hỗ trợ unicode.)

Vấn đề cơ bản đối với C là printf sử dụng dấu chấm lửng không an toàn theo thiết kế - nó cần xác định kích thước của đối số bổ sung từ các đối số đã biết, do đó không thể sửa lỗi để hỗ trợ "bất cứ điều gì bạn có". Vì vậy, trừ khi trình biên dịch của bạn thực hiện một số tiện ích mở rộng độc quyền, bạn sẽ không gặp may.


2
các zkích thước modidfier là tiêu chuẩn C, nhưng một số hiện thực libc đang bị mắc kẹt vào năm 1990 vì những lý do khác nhau (ví dụ như Microsoft về cơ bản bị bỏ rơi C ủng hộ của C ++ và - gần đây - C #)
Christoph

3
C99 đã xác định bộ xác định kích thước 'z' là kích thước của giá trị size_t và 't' là kích thước của giá trị ptrdiff_t.
swestrup

2
%zdlà sai, nó là không dấu nên nó phải như vậy %zu.
David Conrad

-3

Trên một số nền tảng và đối với một số loại, có sẵn các công cụ chuyển đổi printf cụ thể, nhưng đôi khi người ta phải dùng đến các kiểu lớn hơn.

Tôi đã ghi lại vấn đề khó khăn này ở đây, với mã ví dụ: http://www.pixelbeat.org/programming/gcc/int_types/ và cập nhật định kỳ với thông tin về các nền tảng và loại mới.


1
Lưu ý rằng các câu trả lời chỉ liên kết không được khuyến khích, các câu trả lời SO phải là điểm cuối của tìm kiếm giải pháp (so với một điểm dừng khác của tài liệu tham khảo, có xu hướng bị cũ theo thời gian). Vui lòng xem xét việc thêm một bản tóm tắt độc lập ở đây, giữ liên kết làm tài liệu tham khảo.
kleopatra

-5

nếu bạn muốn in giá trị của size_t dưới dạng chuỗi, bạn có thể thực hiện việc này:

char text[] = "Lets go fishing in stead of sitting on our but !!";
size_t line = 2337200120702199116;

/* on windows I64x or I64d others %lld or %llx if it works %zd or %zx */
printf("number: %I64d\n",*(size_t*)&text);
printf("text: %s\n",*(char(*)[])&line);

kết quả là:

số: 2337200120702199116

văn bản: Hãy đi câu cá thay vì ngồi trên chúng tôi nhưng !!

Chỉnh sửa: đọc lại câu hỏi vì số phiếu giảm tôi lưu ý vấn đề của anh ấy không phải là% llu hay% I64d mà là loại size_t trên các máy khác nhau xem câu hỏi này https://stackoverflow.com/a/918909/1755797
http: // www. cplusplus.com/reference/cstdio/printf/

size_t là unsign int trên máy 32 bit và int dài không dấu trên 64 bit
nhưng% ll luôn mong đợi một int dài không dấu.

size_t thay đổi độ dài trên các hệ điều hành khác nhau trong khi% llu là như nhau


4
Cái gì vớ vẩn thế này?!
Antti Haapala

chuyển 8 byte đầu tiên của mảng char thành 64 bit dài không dấu thông qua con trỏ size_t và in chúng dưới dạng số với printf% I64d không thực sự ngoạn mục, tôi biết, tất nhiên tôi đã không mã hóa để ngăn chặn kiểu tràn nhưng đó không phải là trong phạm vi của câu hỏi.
Andre
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.