วิธีหนึ่งสามารถพิมพ์ตัวแปร size_t แบบพกพาโดยใช้ตระกูล printf ได้อย่างไร


403

ฉันมีตัวแปรของชนิดและฉันต้องการที่จะพิมพ์โดยใช้size_t printf()ฉันใช้ตัวระบุรูปแบบใดเพื่อพิมพ์แบบพกพา

ในเครื่อง 32- บิต%uดูเหมือนว่าถูกต้อง ฉันรวบรวมg++ -g -W -Wall -Werror -ansi -pedanticและไม่มีคำเตือน แต่เมื่อฉันรวบรวมรหัสนั้นในเครื่อง 64 บิตมันจะสร้างคำเตือน

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

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

%luคำเตือนออกไปตามที่คาดไว้ถ้าเปลี่ยนที่

คำถามคือฉันจะเขียนรหัสได้อย่างไรเพื่อรวบรวมคำเตือนฟรีทั้งบนเครื่อง 32- และ 64- บิต?

แก้ไข: ในฐานะที่เป็นวิธีแก้ปัญหาผมคิดว่าหนึ่งคำตอบที่อาจจะมีการ "โยน" ตัวแปรลงในจำนวนเต็มที่เป็นพอใหญ่พูดและพิมพ์โดยใช้unsigned long %luที่จะทำงานในทั้งสองกรณี ฉันกำลังมองหาว่ามีความคิดอื่น ๆ


4
การส่งไปunsigned longเป็นตัวเลือกที่ดีที่สุดหากการใช้งาน libc ของคุณไม่สนับสนุนzตัวปรับ มาตรฐาน C99 size_tไม่แนะนำให้มีการแปลงเป็นจำนวนเต็มมากกว่าlongดังนั้นคุณจึงปลอดภัยพอสมควร
Christoph

เป็นไปได้ซ้ำกันของตัวระบุรูปแบบอิสระ size_t รูปแบบ
maxschlepzig


1
บนแพลตฟอร์ม Windows size_t อาจมีขนาดใหญ่กว่าความยาว สำหรับเหตุผลด้านความเข้ากันได้นั้นมีความยาว 32- บิตเสมอ แต่ size_t สามารถเป็น 64- บิตได้ ดังนั้นการร่ายไปจนถึงความยาวที่ไม่ได้ลงชื่ออาจทำให้เสียครึ่งหนึ่งของบิต ขออภัย :-)
Bruce Dawson

คำตอบ:


481

ใช้โมดิzฟายเออร์:

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 นี่คือการเพิ่ม C99 หรือสิ่งนี้ใช้ได้กับ C ++ เช่นกัน (ฉันไม่มี C90 ที่มีประโยชน์)?
avakar

6
มันคือการเพิ่ม C99 และไม่มีคุณลักษณะในรายการprintf()ตัวดัดแปลงความยาวของร่าง C ++ 0x ตั้งแต่ 2009-11-09 (ตารางที่ 84 ในหน้า 672)
Christoph

3
@Christoph: ไม่เป็นในร่างล่าสุด n3035
GManNickG

11
@avakar @Adam Rosenfield @Christoph @GMan: อย่างไรก็ตามใน n3035 §1.2การอ้างอิงตามกฎเกณฑ์มีการอ้างอิงมาตรฐาน C99 เท่านั้นและ§17.6.1.2 / 3 ของรัฐเดียวกัน "สิ่งอำนวยความสะดวกของห้องสมุดมาตรฐาน C มีให้" ฉันจะตีความสิ่งนี้ให้หมายความว่าเว้นแต่จะระบุไว้เป็นอย่างอื่นทุกอย่างในไลบรารีมาตรฐาน C99 เป็นส่วนหนึ่งของไลบรารีมาตรฐาน C ++ 0x รวมถึงตัวระบุรูปแบบเพิ่มเติมใน C99
James McNellis

9
@ArunSaha: เป็นคุณลักษณะของ C99 เท่านั้นไม่ใช่ C ++ หากคุณต้องการให้คอมไพล์ด้วย-pedanticคุณจะต้องได้รับคอมไพเลอร์ที่สนับสนุน C ++ 1x draft (ไม่น่าเป็นไปได้สูง) หรือคุณต้องย้ายโค้ดของคุณไปยังไฟล์ที่คอมไพล์เป็น C99 มิฉะนั้นตัวเลือกเดียวของคุณคือการแปลงตัวแปรของคุณunsigned long longและใช้%lluเพื่อพกพาได้สูงสุด
Adam Rosenfield

88

ดูเหมือนว่ามันจะแตกต่างกันไปตามคอมไพเลอร์ที่คุณใช้ (blech):

... และแน่นอนถ้าคุณใช้ C ++ คุณสามารถใช้coutแทนตามที่ AraKแนะนำ


3
zได้รับการสนับสนุนโดย newlib (เช่น cygwin)
Christoph

7
%zdไม่ถูกต้องสำหรับsize_t; มันถูกต้องสำหรับประเภทที่เซ็นชื่อsize_tแล้ว แต่size_tตัวมันเองเป็นประเภทที่ไม่ได้ลงนาม
Keith Thompson

1
@ KeithThompson: ฉันก็พูดถึง%zuเช่นกัน (และ%zxในกรณีที่พวกเขาต้องการฐานสิบหก) จริงพอที่%zuน่าจะเป็นอันดับแรกในรายการ แก้ไขแล้ว.
TJ Crowder

10
@TJCrowder: ฉันไม่คิดว่า%zdควรจะอยู่ในรายการเลย ฉันไม่สามารถคิดเหตุผลที่จะใช้%zdแทนที่จะ%zuพิมพ์size_tค่า มันไม่ถูกต้องแม้ (มีพฤติกรรมที่ไม่ได้กำหนด) SIZE_MAX / 2ถ้าค่าเกิน (เพื่อความสมบูรณ์คุณอาจพูดถึง%zoเลขฐานแปด)
Keith Thompson

2
@FUZxxl: POSIX ไม่จำเป็นต้องให้ssize_tมีการลงนามประเภทที่สอดคล้องกับดังนั้นจึงไม่รับประกันว่าจะจับคู่size_t "%zd"( อาจเป็นการใช้งานส่วนใหญ่) pubs.opengroup.org/onlinepubs/9699919799/basedefs/ …
Keith Thompson

59

สำหรับ C89 ให้ใช้%luและแปลงค่าเป็นunsigned long:

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

สำหรับ C99 และใหม่กว่าใช้%zu:

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

7
พิจารณา 2013 แนะนำ "สำหรับ C99 เป็นต้นไป" และ "สำหรับ C99 ก่อน:" คำตอบที่ดีที่สุด
chux - Reinstate Monica

8
อย่าทำอย่างนี้. จะล้มเหลวใน Windows 64 บิตที่ size_t คือ 64 บิตและยาวเป็น 32 บิต
Yttrill

1
@Yttrill: คำตอบสำหรับ windows 64 บิตคืออะไร
John Bode

1
@JohnBode บางทีunsigned long long?
James Ko

2
หรือ: คุณสามารถส่งไปยัง a uint64_tแล้วใช้PRIu64แมโครจาก inttypes.h ซึ่งมีตัวระบุรูปแบบ
James Ko

9

ขยายคำตอบของ Adam Rosenfield สำหรับ Windows

ฉันทดสอบโค้ดนี้กับทั้งการดูตัวอย่าง VS2013 Update 4 และ 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 สร้างเอาต์พุตไบนารี:

1
1
2

ในขณะที่สร้างโดย VS2013 พูดว่า:

zu
zx
zd

หมายเหตุ: ssize_tเป็นส่วนขยาย POSIX และSSIZE_Tเป็นสิ่งที่คล้ายกันในWindows Data Typesดังนั้นฉันจึงเพิ่ม<BaseTsd.h>การอ้างอิง

นอกจากนี้ยกเว้นส่วนหัว C99 / C11 ที่ติดตามส่วนหัว C99 ทั้งหมดมีอยู่ในหน้าตัวอย่างของ VS2015:

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

นอกจากนี้ C11's <uchar.h>ยังรวมอยู่ในหน้าตัวอย่างล่าสุด

สำหรับรายละเอียดเพิ่มเติมให้ดูรายการเก่าและรายการใหม่สำหรับความสอดคล้องมาตรฐาน


VS2013 Update 5 ให้ผลลัพธ์เช่นเดียวกับ Update 4 ที่มอบให้คุณ
Nathan Kidd

6

สำหรับผู้ที่พูดถึงการทำเช่นนี้ใน C ++ ซึ่งไม่จำเป็นต้องสนับสนุนส่วนขยาย C99 ดังนั้นฉันขอแนะนำรูปแบบ boost :: สิ่งนี้ทำให้ moot ขนาดของคำถามขนาดที่สงสัย:

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

เนื่องจากคุณไม่ต้องการตัวระบุขนาดในรูปแบบ boost :: คุณสามารถกังวลเกี่ยวกับวิธีที่คุณต้องการแสดงค่า


4
อาจต้องการ%uแล้ว
GManNickG

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

8
ใช่ แต่ผู้ถามถามหาตัวprintfระบุเฉพาะ ฉันเดาว่าพวกเขามีข้อ จำกัด อื่น ๆ ที่ทำให้std::coutเกิดปัญหา
Donal Fellows

1
@ Donal ฉันสงสัยว่า C ++ สามารถสร้างสตรีมปัญหาในโครงการ C ++ ได้อย่างไร!
AraK

9
@AraK พวกมันช้ามากเหรอ? พวกเขาเพิ่มไบต์จำนวนมากด้วยเหตุผลไม่มาก อรุณซาฮาต้องการที่จะรู้ถึงความรู้ส่วนตัวของเขาหรือเธอ? การตั้งค่าส่วนตัว (ฉันชอบ stdio เพื่อส่งต่อตัวเอง) มีหลายเหตุผล
KitsuneYMG

1
@TKCrowder: คำขอเดิมบอกว่าต้องการโซลูชัน C (ผ่านการแท็ก) และมีเหตุผลที่ดีที่จะไม่ใช้สตรีมใน C ++ เช่นหากตัวอธิบายรูปแบบเอาต์พุตถูกดึงจากแค็ตตาล็อกข้อความ (คุณสามารถเขียนโปรแกรมแยกวิเคราะห์ข้อความและใช้สตรีมหากคุณต้องการ แต่นั่นเป็นงานจำนวนมากเมื่อคุณสามารถใช้รหัสที่มีอยู่ได้)
Donal Fellows

1
@Donal: แท็กคือ C และ C ++ ฉันไม่ได้สนับสนุนการสตรีมเนื้อหา I / O ของ C ++ (ฉันไม่ใช่แฟนของมัน) เพียงแค่ชี้ให้เห็นว่าคำถามไม่ได้เริ่มต้น * "... ขอข้อมูลจำเพาะสำหรับตัวprintfระบุ"
TJ Crowder


2

ดังที่ AraK กล่าวว่าอินเทอร์เฟซ c ++ สตรีมจะทำงานได้ทุกพอร์ต

std :: size_t s = 1024; std :: ศาล << s; // หรือสตรีมชนิดอื่น ๆ เช่นสตริงสตรีม!

หากคุณต้องการ C stdio ไม่มีคำตอบแบบพกพาสำหรับเรื่องนี้ในบางกรณีของ "พกพา" และมันก็น่าเกลียดตั้งแต่ที่คุณเห็นการเลือกรูปแบบที่ไม่ถูกต้องอาจทำให้เกิดคำเตือนของคอมไพเลอร์หรือให้ผลลัพธ์ที่ไม่ถูกต้อง

C99 พยายามแก้ไขปัญหานี้ด้วยรูปแบบ inttypes.h เช่น "%" PRIdMAX "\ n" แต่เช่นเดียวกับ "% zu" ทุกคนไม่สนับสนุน c99 (เช่น MSVS ก่อนปี 2013) มีไฟล์ "msinttypes.h" ลอยอยู่รอบ ๆ เพื่อจัดการกับสิ่งนี้

หากคุณส่งไปยังประเภทอื่นขึ้นอยู่กับแฟล็กคุณอาจได้รับคำเตือนคอมไพเลอร์สำหรับการตัดปลายหรือการเปลี่ยนเครื่องหมาย หากคุณไปเส้นทางนี้เลือกประเภทขนาดคงที่ที่เกี่ยวข้องที่มีขนาดใหญ่กว่า หนึ่งในความยาวที่ไม่ได้ลงชื่อและ "% llu" หรือยาวที่ไม่ได้ลงชื่อ "% lu" ควรใช้งานได้ แต่ llu อาจทำให้สิ่งต่าง ๆ ช้าลงในโลก 32 บิตซึ่งมีขนาดใหญ่เกินไป (แก้ไข - mac ของฉันออกคำเตือนใน 64 บิตสำหรับ% llu ไม่ตรงกับ size_t แม้ว่า% lu,% llu และ size_t จะมีขนาดเท่ากันทั้งหมดและ% lu และ% llu ไม่ได้มีขนาดเท่ากันใน MSVS2012 ของฉันดังนั้น คุณอาจต้องใช้ + ใช้รูปแบบที่ตรงกัน)

สำหรับเรื่องนั้นคุณสามารถไปกับประเภทขนาดคงที่เช่น int64_t แต่เดี๋ยวก่อน! ตอนนี้เรากลับไปที่ c99 / c ++ 11 และ MSVS รุ่นเก่าล้มเหลวอีกครั้ง นอกจากนี้คุณยังได้ปลดเปลื้อง (เช่น map.size () ไม่ใช่ประเภทขนาดคงที่)!

คุณสามารถใช้ส่วนหัวหรือห้องสมุดของบุคคลที่สามเช่นเพิ่ม หากคุณยังไม่ได้ใช้งานคุณอาจไม่ต้องการขยายโครงการของคุณแบบนั้น หากคุณยินดีที่จะเพิ่มเพียงแค่สำหรับปัญหานี้ทำไมไม่ใช้ c ++ สตรีมหรือการรวบรวมตามเงื่อนไข

ดังนั้นคุณจะอยู่ที่กระแส c ++ การรวบรวมแบบมีเงื่อนไขกรอบงานบุคคลที่สามหรืออุปกรณ์พกพาบางอย่างที่เหมาะกับคุณ


-1

มันจะเตือนคุณหรือไม่ถ้าคุณผ่านจำนวนเต็ม 32 บิตที่ไม่ได้ลงชื่อไปยังรูปแบบ% lu ควรจะดีเนื่องจากการแปลงมีการกำหนดไว้อย่างดีและจะไม่สูญเสียข้อมูลใด ๆ

ฉันได้ยินมาว่าบางแพลตฟอร์มกำหนดมาโคร<inttypes.h>ให้คุณสามารถแทรกลงในสตริงรูปแบบตามตัวอักษรได้ แต่ฉันไม่เห็นส่วนหัวนั้นในคอมไพเลอร์ Windows C ++ ของฉันซึ่งแปลว่ามันอาจไม่ข้ามแพลตฟอร์ม


1
คอมไพเลอร์ส่วนใหญ่จะไม่เตือนคุณหากคุณส่งบางสิ่งผิดขนาดไปยัง printf GCC เป็นข้อยกเว้น inttypes.h ถูกกำหนดใน C99 ดังนั้นคอมไพเลอร์ C ใด ๆ ที่เป็นไปตามมาตรฐาน C99 จะมีมันซึ่งควรจะเป็นของพวกเขาทั้งหมดในขณะนี้ ถึงกระนั้นคุณอาจต้องเปิด C99 ด้วยธงคอมไพเลอร์ ไม่ว่าในกรณีใด intttypes.h ไม่ได้กำหนดรูปแบบเฉพาะสำหรับ size_t หรือ ptrdiff_t เนื่องจากมีการตัดสินใจว่ามีความสำคัญพอที่จะรับตัวระบุขนาดของตนเองเป็น 'z' และ 't' ตามลำดับ
swestrup

ถ้าคุณใช้%luคุณควรโยนมูลค่าให้กับsize_t unsigned longไม่มีการแปลงโดยปริยาย (นอกเหนือจากโปรโมชั่น) printfสำหรับข้อโต้แย้งที่จะเป็น
Keith Thompson

-2

C99 กำหนด "% zd" และอื่น ๆ (ขอบคุณผู้แสดงความคิดเห็น) ไม่มีตัวระบุรูปแบบพกพาสำหรับสิ่งนั้นใน C ++ - คุณสามารถใช้%pซึ่งคำ woulkd ในสองสถานการณ์นี้ แต่ไม่ใช่ตัวเลือกแบบพกพาและให้ค่าเป็นเลขฐานสิบหก

หรือใช้สตรีมมิ่ง (เช่น stringstream) หรือการเปลี่ยน printf ปลอดภัยเช่นรูปแบบ Boost ฉันเข้าใจว่าคำแนะนำนี้ใช้ได้เฉพาะในจำนวน จำกัด (และต้องใช้ C ++) (เราใช้วิธีการที่คล้ายกันเหมาะสำหรับความต้องการของเราเมื่อใช้การสนับสนุน Unicode)

ปัญหาพื้นฐานสำหรับ C คือ printf ที่ใช้จุดไข่ปลานั้นไม่ปลอดภัยโดยการออกแบบ - มันต้องกำหนดขนาดของอาร์กิวเมนต์เพิ่มเติมจากอาร์กิวเมนต์ที่รู้จักดังนั้นจึงไม่สามารถแก้ไขได้เพื่อสนับสนุน "สิ่งที่คุณได้รับ" ดังนั้นหากคอมไพเลอร์ของคุณใช้ส่วนขยายที่เป็นกรรมสิทธิ์คุณก็ไม่โชคดี


2
zmodidfier ขนาดมาตรฐาน C แต่บางการใช้งาน libc จะติดอยู่ในปี 1990 ด้วยเหตุผลต่างๆ (เช่นไมโครซอฟท์ที่ถูกทิ้งร้างพื้น C ในความโปรดปรานของ C ++ และ - เมื่อเร็ว ๆ นี้ - C #)
คริสโต

3
C99 กำหนดตัวระบุขนาด 'z' เป็นขนาดของค่า size_t และ 't' เป็นขนาดของค่า ptrdiff_t
swestrup

2
%zd%zuถูกผิดก็ลงนามดังนั้นจึงควรจะ
David Conrad

-3

ในบางแพลตฟอร์มและสำหรับบางประเภทมีตัวระบุการแปลง printf เฉพาะ แต่บางครั้งก็ต้องหันไปใช้งานการพิมพ์ที่มีขนาดใหญ่กว่า

ฉันได้บันทึกปัญหาที่ยุ่งยากนี้ที่นี่ด้วยรหัสตัวอย่าง: http://www.pixelbeat.org/programming/gcc/int_types/ และอัปเดตเป็นระยะด้วยข้อมูลเกี่ยวกับแพลตฟอร์มและประเภทใหม่


1
โปรดทราบว่าคำตอบที่ลิงก์อย่างเดียวนั้นไม่ได้รับการสนับสนุนดังนั้นคำตอบนั้นควรเป็นจุดสิ้นสุดของการค้นหาวิธีแก้ไขปัญหา (เทียบกับอีกหนึ่งการหยุดพักระหว่างการอ้างอิงอื่น ๆ โปรดลองเพิ่มบทสรุปแบบสแตนด์อโลนที่นี่โดยคงลิงก์ไว้เป็นข้อมูลอ้างอิง
kleopatra

-5

หากคุณต้องการพิมพ์ค่าของ size_t เป็นสตริงคุณสามารถทำได้:

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);

ผลลัพธ์คือ:

หมายเลข: 2337200120702199116

ข้อความ: ให้เราไปตกปลาแทนที่จะนั่งบนเรา แต่ !!

แก้ไข: อ่านคำถามอีกครั้งเพราะคะแนนโหวตลงฉันสังเกตว่าปัญหาของเขาไม่ใช่% llu หรือ% I64d แต่ประเภท size_t บนเครื่องที่แตกต่างกันเห็นคำถามนี้https://stackoverflow.com/a/918909/1755797
http: // www cplusplus.com/reference/cstdio/printf/

size_t ไม่ได้ลงนาม int บนเครื่อง 32 บิตและ int long long ที่ไม่ได้ลงนามบน 64 บิต
แต่% ll จะคาดหวัง int long ที่ไม่ได้ลงนามเสมอ

size_t มีความยาวแตกต่างกันไปตามระบบปฏิบัติการที่แตกต่างกันในขณะที่% llu เหมือนกัน


4
นี่มันเรื่องไร้สาระอะไร!
Antti Haapala

กำลังส่งไบต์ char 8 ไบต์แรกไปยัง 64 บิตที่ไม่ได้ลงนามยาว ๆ ผ่านตัวชี้ size_t และพิมพ์เป็นตัวเลขด้วย printf% I64d ไม่น่าตื่นเต้นจริงๆฉันรู้แน่นอนว่าฉันไม่ได้อยู่ที่โค้ดเพื่อป้องกันการพิมพ์ล้น แต่ ไม่ได้อยู่ในขอบเขตของคำถาม
Andre
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.