Path: csiph.com!eternal-september.org!feeder.eternal-september.org!nntp.eternal-september.org!.POSTED!not-for-mail From: Keith Thompson Newsgroups: comp.lang.c Subject: Re: printf and time_t Date: Tue, 03 Feb 2026 18:19:45 -0800 Organization: None to speak of Lines: 112 Message-ID: <87qzr1p7we.fsf@example.invalid> References: <10jfol6$2u6r8$1@news.xmission.com> <10jhkso$3c9r2$3@nntp.eternal-september.org> <20260106112938.00004446@yahoo.com> <10jj9st$3jbe4$2@dont-email.me> <20260106200522.000015ea@yahoo.com> <87h5sy2rlb.fsf@example.invalid> <87qzs1gliq.fsf@example.invalid> <20260108012620.000041a9@yahoo.com> <87bjj5gei4.fsf@example.invalid> <20260108023846.0000260c@yahoo.com> <10jpi8h$15aea$1@dont-email.me> <20260109141859.00004f22@yahoo.com> <10jv3rb$15aea$2@dont-email.me> <20260111132015.000026ad@yahoo.com> <87ikd82tks.fsf@example.invalid> <20260111153201.000075f9@yahoo.com> <87ecnv3gj2.fsf@example.invalid> <10k27e7$25sqv$1@dont-email.me> <87qzrvow2l.fsf@example.invalid> <86ikcdi9v5.fsf@linuxsc.com> MIME-Version: 1.0 Content-Type: text/plain Injection-Date: Wed, 04 Feb 2026 02:19:51 +0000 (UTC) Injection-Info: dont-email.me; posting-host="adc50be3bbe0dba199a858e52ca7a4e7"; logging-data="2108661"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX18xOoqeuEZUAMpJxCn5PLbX" User-Agent: Gnus/5.13 (Gnus v5.13) Cancel-Lock: sha1:O/vN6Qg1eoxKaKkhJdTYaipRNaQ= sha1:rydBoJQil9WoC1qvOvcm+wjYhlI= Xref: csiph.com comp.lang.c:396584 Tim Rentsch writes: > Keith Thompson writes: >> David Brown writes: >> [...] >> >>> C23 includes length specifiers with explicit bit counts, so "%w32u" is >>> for an unsigned integer argument of 32 bits: >>> >>> """ >>> wN Specifies that a following b, B, d, i, o, u, x, or X conversion >>> specifier applies to an integer argument with a specific width >>> where N is a positive decimal integer with no leading zeros >>> (the argument will have been promoted according to the integer >>> promotions, but its value shall be converted to the unpromoted >>> type); or that a following n conversion specifier applies to a >>> pointer to an integer type argument with a width of N bits. All >>> minimum-width integer types (7.22.1.2) and exact-width integer >>> types (7.22.1.1) defined in the header shall be >>> supported. Other supported values of N are implementation-defined. >>> """ >>> >>> That looks to me that it would be a correct specifier for uint32_t, >> >> Yes, so for example this: >> >> uint32_t n = 42; >> printf("n = %w32u\n", n); >> >> is correct, if I'm reading it correctly. It's also correct for >> uint_least32_t, which is expected to be the same type as uint32_t >> if the latter exists. There's also support for the [u]int_fastN_t >> types, using for example "%wf32u" in place of "%w32u". >> >>> and should also be fully defined behaviour for unsigned int and >>> unsigned long if these are 32 bits wide. >> >> No, I don't think C23 says that. > > Right, it doesn't. > >> If int and long happen to be the same >> width, they are still incompatible, and there is no printf format >> specifier that has defined behavior for both. >> >> That first sentence is a bit ambiguous >> >> wN Specifies that a following b, B, d, i, o, u, x, or X conversion >> specifier applies to an integer argument with a specific width ... >> >> but I don't think it means that it must accept *any* integer type >> of the specified width. > > As I read the standard there is no ambiguity. The first sentence > says what the length modifier means. The second sentence says > which types (if any) correspond to the description in the first > sentence. The descriptions for all the other length modifiers name the types to which they apply in the first sentence. "hh" applies to signed char or unsigned char, "l" applies to long int or unsigned long int, "z" applies to size_t, and so forth. The first sentence of the description for "wN" says it "applies to an integer argument with a specific width". The intent is that "%w32d" applies to an argument of type int_least32_t or int32_t (if the latter exists, it must be the same type as the former). Suppose, hypothetically, that it had been the intent that "%w32d" applies to *any* signed integer type with a width of 32 bits (e.g., both int and long if both are 32 bits wide). I think that the current wording could express that intent. The second sentence could taken as a clarification rather than a restriction. (An irrelevant aside: That might actually be a nice feature.) Assume an implementation with 32-bit int, 32-bit long, and 64-bit long long, where int32_t and int64_t are int and long long, respectively (e.g., "gcc -m32" with glibc on 64-bit Ubuntu), so none of the intN_t types are defined as long. Then this: printf("%w32d\n", 0L); has undefined behavior if we assume (as I do) that "%w32d" applies only to the type defined as int32_t (and int_least32_t). But the 0L argument *is* "an integer argument with a specific width", and the following sentence "All minimum-width integer types (7.22.1.2) and exact-width integer types (7.22.1.1) defined in the header shall be supported." does not contradict that. I think the phrase "an integer argument with a specific width" was an attempt to describe a specific set of types, but it was worded in a way that applies a larger set of types. I think the following sentence is not sufficiently clear in its attempt to restrict the list of applicable types. I understand the intent. Adding a format string that can apply to distinct incompatible types would be a major change that would surely be discussed in greater detail. But the current wording does not clearly express that intent, and one or more people here have, quite understandably, interpreted the wording in a way that's inconsistent with the presumed intent. The description of wfN is a bit clearer, but could also use some clarification that "a fastest minimum-width integer argument with a specific width" refers specifically to the [u]int_fastN_t types. I merely suggest a clarification, either a change in wording or a footnote. -- Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com void Void(void) { Void(); } /* The recursive call of the void */