• MIKE (unregistered)

    There's another bigger WTF. if CONFIG_STRING contains '%first' as an example, the program will probably crash or will print garbage, like 0.000000irst

  • Prime Mover (unregistered)

    TRWTF is C of course.

  • MaxiTB (unregistered) in reply to MIKE

    Most C compilers I used in the past just print the format string if there is no argument given (argc allows to check for the amount of it). But printf("This " FIRST " is bad %s", "boom"); would actually bad. In general, yeah, it's a bad idea to use printf because it's a legacy C method and especially if you just want to stream out text. puts was better legacy option or in c++ you just stream it to cout.

  • MaxiTB (unregistered) in reply to Prime Mover

    The is nothing wrong with C; at least it's not Oracle Java.

  • (nodebb) in reply to MIKE

    an embedded %s would be worse, unless the data picked up by the %f happens to correspond to something jolly like a signalling NaN.

  • (nodebb)

    Now, an interesting quirk of C's translation process is that, at least in most compilers, adjacent string literals get concatenated into a single string. So the first branch here makes sense, though you have to be used to C idioms. That COMPILER_XYZ doesn't support it is odd and suspicious, but hardly shocking.

    Actually, if XYZ doesn't support compile-time concatenation of adjacent string literals, it's not a C compiler. The rest of its input language might resemble C, but merging adjacent string literals like that is a required part of the language, and has been for more than thirty years. It was already required when the ANSI released their standard for C in 1989, and the first release of GCC was in 1987, so it would have been highly unlikely for the scenario depicted by the comments to ever happen unless the developers picked up a defective compiler that they thought would be a C compiler, but wasn't.

  • Sole Purpose Of Visit (unregistered) in reply to Steve_The_Cynic

    It's also not much of a quirk, is it? There's no semantic ambiguity. And on a Pythonic "no surprises" equivalency, you get what you expect. The only thing missing is a concatenation operator, and since C doesn't allow concatenation between a literal string and anything other than a literal string or a pointer -- what with not having objects and thus inheritance of ToString() -- it's hard to see what extra goodiness you would get out of a concatenation operator.

    It worries me when youngsters like Remy seem to ignore the fact that C has had a pretty iron-clad definition since, as you say, C89. If I found a "compiler" XYZ that did not, in fact, do something as simple and as clearly stipulated as this, I wouldn't write the alternative formatting. Instead, I would worry about what the hell else the "compiler" is going to get wrong, and complain to my manager and/or the vendor.

  • Sole Purpose Of Visit (unregistered) in reply to Sole Purpose Of Visit

    Correction. C does not allow concatenation between a literal string and a pointer. I'm getting rusty. (No pun intended, although possibly applicable.)

  • MaxiTB (unregistered) in reply to Steve_The_Cynic

    There were tons of C compilers that didn't not implement the standard fully in the pre-2000 days. In fact, a fully compliant compiler was an absolute rarity and I can't remember a single one I'd use (Turbo C, Borland C, MSC, Watcom ... all had special quirks to it).

  • Sauron (unregistered)

    Maybe they re-invented a C compiler? (but broken)

    That would be a meta-WTF.

  • dpm (unregistered) in reply to MaxiTB

    a fully compliant compiler was an absolute rarity

    The VAXC compiler from DEC!

    ha ha ha ha I kill myself. Seriously, there were days when I wanted to. At least the DECC compiler was pretty good, when it came out.

  • (nodebb)

    It might depend on what the actual CONFIG_STRING is.

    1 Does it evaluate to "some config"? 2 Does it evaluate to a bare __some_config_str? Where somewhere else is const char *__some_config_str = "some config";

    Concatenation doesn't work in the second case...

  • (nodebb) in reply to Pawel Kraszewski

    For double fun, the kind of definition of CONFIG_STRING might depend on what #defines are set...

  • Anonymous') OR 1=1; DROP TABLE wtf; -- (unregistered) in reply to Steve_The_Cynic

    Not only is it ISO C89, it's even specified behavior in pre-standard K&R C. 2nd edition of K&R mentions this on p.38 (I don't have a copy of the 1st edition handy, but it's highly unlikely it differs here). Every C compiler that's ever existed should support this behavior.

    The only rational explanation I have for why this code didn't work for non-GCC compilers is if somehow CONFIG_STRING weren't defined as a string literal for those compilers (but was a string literal with GCC).

  • (author) in reply to Sole Purpose Of Visit

    I've come across toolchains that aren't up to C89 yet in weirder corners of the embedded space. They're the exception, certainly, but if someone tells me they have a non-compliant C compiler, I won't argue with them.

  • (nodebb) in reply to MaxiTB

    True, but they all supported fusing adjacent string literals. Even the lightly crippled Microware C compiler that I had in 1985 or so, for OS9, supported that, and it couldn't compile structure assignment statements.

  • MaxiTB (unregistered) in reply to Steve_The_Cynic

    Well, if we are looking back here a goodie from my first C days: https://archive.org/details/AmigaCForAdvancedProgrammers/page/2/mode/2up Lattice was considered the most reliable standard conforming one, but oh boy, it was rough. You could nest macros only 16 times and then it simply stopped working for example :-) And edge cases like that was what I meant with quirks, random limitations from character limits, maximum amount of some sort of entities, maximum amount of nesting something etc.

  • (nodebb) in reply to MaxiTB

    Most C compilers I used in the past just print the format string if there is no argument given (argc allows to check for the amount of it).

    I've used gcc and clang. I'm currently using clang and aside from emitting a warning when compiling print("%d\n") (more % conversions than data arguments), it does the dumb thing. When I ran a short program containing just that statement, it just printed a randomish number followed by a carriage return.

    There's no argc with printf, it determines the number of arguments by the number of format specifiers and it's not a compiler thing either: printf is part of the C standard library and it wasn't necessarily compiled with the same C compiler. There's no way for it to tell it wasn't supplied with enough arguments in any particular case.

    In general, yeah, it's a bad idea to use printf because it's a legacy C method and especially if you just want to stream out text. puts was better legacy option or in c++ you just stream it to cout.

    No, this is just wrong. puts has no options for argument formatting. Admittedly, it would work for this particular use-case except that you've got to make sure the \n is appended. The correct printf is superior IMO i.e. printf("%s\n", CONFIG_STRING); Also, if you are programming in C, then by definition, cout is not available to you: C != C++.

    Even if you are programming in C++, if you have arguments in strings, you definitely want to be using some equivalent of C format strings, unless you don't care about translating your program into other languages. C format strings are easier to translate into languages where words might have to come in a different order than IO stream expressions*.

    • I haven't used C++ in many years, so maybe C++ output streams now can handle format strings.

    Addendum 2023-01-10 13:22: The last line is meant to be the footnote but the editor swallowed the asterisk.

    Also, referencing arguments out of order is POSIX functionality and it's not in the C standard.

  • Gearhead (unregistered)

    Dare I try to use a quote and a code block with no preview? Yes!

    There's no argc with printf, it determines the number of arguments by the number of format specifiers

    While you are technically correct, printf is defined variadically and you can absolutely get the number of arguments using va_start() and friends.

    $ grep printf /usr/include/stdio.h
    extern int printf (const char *__restrict __format, ...);
    
  • Alan (unregistered)

    It has occurred to me that printf format strings are a bit like leibnitzian notation in differential calculus: hard to justify theoretically but just too convenient in practice not to keep creeping back whenever some dogmatically purist authority tries to outlaw them.

  • MaxiTB (unregistered) in reply to Jeremy Pereira

    There's no argc with printf

    Oh yeah, va_start, va_end, va_arg, va_list - I completely mixed it up with main(). My mistake, need to code more old school C again for fun.

    No, this is just wrong.

    My reply refers to the issue as a formatting string in context with printf (see original post).

    The correct printf is superior IMO i.e. printf("%s\n", CONFIG_STRING);

    Now that is a ton of overhead for a literal string and the worst option of all. First you have the format string parsing, then it has to skip the optimization part for strings without format arguments and finally depending on the implementation it calls sprintf() internal or does two IO calls (stdout) depending on the implementation.

    In contract puts("configuration: " CONFIG_STRING "\n") in best case results in one string literal and one IO call for stdout.

  • a cow (not a robot) (unregistered)

    Now that is a ton of overhead

    Really? What speed difference have you measured?

  • nz (unregistered)
    1. puts adds \n itself.
    2. That means it's not one IO call unless optimized as a builtin
    3. My guess: on XYZ it did compile but did something funny due to that little % somewhere. Rewritting it in 3 calls "fixed" it by changing stack content. (the guess that CONFIG_STRING is not a literal on XYZ is more plausible, though)
  • some ron (unregistered)

    The real WTF is, that if you compile it neither on GCC nor on XYZ, it won't print anything.

    For whom it may concern: the first edition of "The C Programming Language" by K&R does not mention string constant concatenation at all (the book doesn't mention it being the first or any edition, which probably means, it is the first).

  • The real crime (unregistered)

    is using printf when a fputs would work better

  • (nodebb) in reply to MaxiTB

    Now that is a ton of overhead for a literal string

    Well, it's some overhead. Unless, you're printing it a few thousand times a second, it's probably not significant.

    In contract puts("configuration: " CONFIG_STRING "\n") in best case results in one string literal and one IO call for stdout.

    OK, that would be fine as long as you are not overly concerned about localisation (for "configuration"). However, almost certainly, both this and the printf options will result in only one IO system call.

  • (author)

    Assuming this is not performance-critical code (and if it is performance-critical, why are you using printf), TRWTF is even bothering with the compile-time concatenation branch at all. It's just extra code that is not fully portable across all the (non-compliant) compilers you're using, so just drop that branch and use the portable solution.

  • Foo AKA Fooo (unregistered) in reply to Jeremy Pereira
    Comment held for moderation.
  • Foo AKA Fooo (unregistered) in reply to Gearhead
    Comment held for moderation.
  • kolik (unregistered) in reply to MaxiTB
    Comment held for moderation.
  • Craig (unregistered) in reply to Jeremy Pereira
    Comment held for moderation.

Leave a comment on “String Formatting”

Log In or post as a guest

Replying to comment #:

« Return to Article