• Re: Expression statements (was Re: Meaning of "expression")

    From Janis Papanagnou@3:633/10 to All on Sun Jun 28 02:49:30 2026
    On 2026-06-12 02:41, Keith Thompson wrote:
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:

    Oh, actually I indeed thought that printing a constant string would not
    create any error that would then be indicated by printf's return value.

    Linux has a device called "/dev/full". It acts like it has no data
    on input, and like it's full on output. You can redirect a program's
    stdout to /dev/full. It's useful for testing, and much easier than
    finding a writable filesystem with no remaining space. (/dev/null
    accepts and discards as much intput as you send to it.)

    I've never stumbled across /dev/full before. Thanks for that hint.

    [...]

    I'd indeed also expected that, say, printing a string value with a '%d'
    specifier would produce an error, but I saw that it doesn't; while the
    compiler creates just a warning, execution provides some random output
    and a _non-negative_ string-length value as printf's return value. Not
    exactly what I'd expect from a language.

    Calling printf with a mismatch between the format string and
    an argument has undefined behavior. Some compilers will warn
    about this in most cases, but in general the format string is not
    necessarily known at compile time.

    Well, yes. But therefore I imagined that at runtime an rc<0 could have indicated such a mismatch.

    (BTW, only after my post I noticed that my example scenario ("printing
    a string value with a '%d'") a string is actually passed as a pointer
    value, so is a type similar to an integer, which might make it easier
    to spot at compile time than at runtime? Anyway; this is something I'd
    like to be detected.)

    No diagnostic or other error indication is required.

    Well.

    [...]

    Obviously (because of that?) I've never seen anyone test such a call
    by, say,

    int rc = printf("Hello, world\n");
    if (rc < 0) {
    /* umm.. */
    }

    Quick-and-dirty programs like the classic "hello, world" often don't
    bother to check. The above could print an error message to stderr and
    call exit(EXIT_FAILURE). Even if stdout and stderr both produce errors,
    the caller should be able to detect the error status. (I've configured
    my shell to print a message when a program dies with an error status.)

    But most production programs don't just blindly print stuff to stdout.
    [...]

    Are you - plural, all CLC audience - writing such code with 'printf()',
    honestly? - Same question with 'int rc = fclose (...);' - what can one
    do about that, then? (Write a logfile entry, maybe? - and then?)

    Write the error message to stderr, optionally log it somewhere,
    and exit with an error code.

    Just note that generally a terminal might not be connected. As I wrote,
    logging was what we've done, so I'm with you here. An exit, OTOH, was in
    our server applications not an option.

    Janis


    --- PyGate Linux v1.5.18
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Sun Jun 28 03:16:15 2026
    On 2026-06-12 02:41, James Kuyper wrote:
    On 2026-06-11 18:37, Janis Papanagnou wrote:
    [...]
    I'd indeed also expected that, say, printing a string value with a '%d'
    specifier would produce an error, but I saw that it doesn't; while the
    compiler creates just a warning, execution provides some random output
    and a _non-negative_ string-length value as printf's return value. Not
    exactly what I'd expect from a language.

    On some systems I've used, it would try to interpret the pointer to the string as an int, and print the result.

    Right. That occurred to me only after I had sent my post.

    On others, it would expect the
    int to be stored in one register, whereas the pointer was stored in a different register, and as a result it would print whatever value was
    last stored in the first register. These were natural outcomes for those implementations; had the C standard imposed any conflicting requirements
    on the behavior, it would have complicated those implementations.

    [...]

    Obviously (because of that?) I've never seen anyone test such a call
    by, say,

    int rc = printf("Hello, world\n");
    if (rc < 0) {
    /* umm.. */
    }

    Are you - plural, all CLC audience - writing such code with 'printf()',
    honestly? - Same question with 'int rc = fclose (...);' - what can one
    do about that, then? (Write a logfile entry, maybe? - and then?)

    For most of the programs I ever wrote, a single check for ferror(file)
    at the end of the program, resulting in exit(EXIT_FAILURE) being called, would be acceptable.

    Hmm.. - I don't recall to have ever used ferror().

    Personally it seems to me that continuing I/O once an error gets
    flagged is not something I'd have done with an easy conscience.
    So I'd not have dared to interrogate that state only once at the
    end of the program. But then, instead of regularly calling ferror,
    checking the RC should suffice?

    (I think I mentioned already that usually we inspected the RC at
    the place of generation for all functions where we felt it matters
    and where we can act on such events.)

    That approach relies on the fact that the error
    flag is sticky. Because I made a habit of such checks, we caught a
    problem when a disk overflowed before we'd wasted hours "writing" data
    to nowhere. If I had sent a message to a log file, it would have been
    blocked by the same problem, which is why I used the exit status to
    report the problem.

    Okay. In our cases we had no problems with disk overflows; our main
    operational concern was communication of data, less storage of huge
    amounts of temporaries or payload data. For logfiles we had our own
    framework with IIRC a "tandem approach" (alternating between two or
    more logfiles, each with a fixed maximum number of log-entries, and
    reused when it's its turn); so disk space was well under control.

    Janis

    [...]

    --- PyGate Linux v1.5.18
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Keith Thompson@3:633/10 to All on Sat Jun 27 21:00:39 2026
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    On 2026-06-12 02:41, Keith Thompson wrote:
    [...]
    Calling printf with a mismatch between the format string and
    an argument has undefined behavior. Some compilers will warn
    about this in most cases, but in general the format string is not
    necessarily known at compile time.

    Well, yes. But therefore I imagined that at runtime an rc<0 could have indicated such a mismatch.

    That would be nice, but it's just one of the infinitely many possible
    results of undefined behavior.

    For example, this program:

    #include <stdio.h>
    int main(void) {
    const int result = printf("%ld\n", 0.3);
    printf("printf returned %d\n", result);
    }

    on my system prints:

    140732048673560
    printf returned 16

    gcc and clang warn about the format string. tcc doesn't.

    The (first) printf call was apparently successful because printf
    has no way to know that the argument was of an incorrect type
    (types don't really exist at run time).

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */

    --- PyGate Linux v1.5.18
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Tim Rentsch@3:633/10 to All on Sun Jun 28 09:52:36 2026
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:

    On 2026-06-12 02:41, Keith Thompson wrote:

    [...]

    Calling printf with a mismatch between the format string and an
    argument has undefined behavior. Some compilers will warn about
    this in most cases, but in general the format string is not
    necessarily known at compile time.

    Well, yes. But therefore I imagined that at runtime an rc<0
    could have indicated such a mismatch.

    That would be nice, but it's just one of the infinitely many
    possible results of undefined behavior.

    For example, this program:

    #include <stdio.h>
    int main(void) {
    const int result = printf("%ld\n", 0.3);
    printf("printf returned %d\n", result);
    }

    on my system prints:

    140732048673560
    printf returned 16

    gcc and clang warn about the format string. tcc doesn't.

    The (first) printf call was apparently successful because printf
    has no way to know that the argument was of an incorrect type
    (types don't really exist at run time).

    printf() could know if an argument were of an incorrect type, if
    an implementation chose to do so.

    --- PyGate Linux v1.5.18
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Keith Thompson@3:633/10 to All on Sun Jun 28 18:28:46 2026
    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    [...]
    For example, this program:

    #include <stdio.h>
    int main(void) {
    const int result = printf("%ld\n", 0.3);
    printf("printf returned %d\n", result);
    }

    on my system prints:

    140732048673560
    printf returned 16

    gcc and clang warn about the format string. tcc doesn't.

    The (first) printf call was apparently successful because printf
    has no way to know that the argument was of an incorrect type
    (types don't really exist at run time).

    printf() could know if an argument were of an incorrect type, if
    an implementation chose to do so.

    Sure. I did write "on my system", where printf has no way to know
    that the argument was of an incorrect type.

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */

    --- PyGate Linux v1.5.18
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)