• Meaning of "expression"

    From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Mon Jun 8 14:05:06 2026
    From Newsgroup: comp.lang.c

    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    [...]
    The actual text of the standard implies that 42 is not an expression.
    I rely on the obvious intent to conclude that it is.

    I made the above statement to demonstrate that just following the exact
    wording of the standard, without thinking about the (sometimes unclear)
    intent behind it, can lead to absurd results.

    I've discussed this particular glitch before, but it's been a while.

    N3220 6.5.1 says:

    An *expression* is a sequence of operators and operands that
    specifies computation of a value, or that designates an object
    or a function, or that generates side effects, or that performs
    a combination thereof.

    I believe the wording is unchanged from C90 up to the latest C202y
    draft. Since the word "expression" is in italics, this is the
    standard's definition of the word.

    This is a flawed definition. The terms "operator" and "operand"
    are defined in 6.4.6:

    *punctuator: one of
    [ ] ( )
    [snip]

    A punctuator is a symbol that has independent syntactic and semantic
    significance. Depending on context, it may specify an operation to
    be performed (which in turn may yield a value or a function
    designator, produce a side effect, or some combination thereof) in
    which case it is known as an *operator* (other forms of operator also
    exist in some contexts). An *operand* is an entity on which an
    operator acts.

    Consider this expression statement:

    42;

    Is `42` an expression? Clearly it's intended to be, but there is no
    operator, and therefore there is no operand, so it doesn't meet the
    standard's definition of the word "expression".

    For that matter, consider:

    (void)0;

    It's "obvious" that `(void)0` is an expression. It consists of one
    operator `(void)` and one operand `0` (I'll ignore the fact that
    the definition uses plurals for both), but it does not specify
    computation of a value, or designate an object or a function,
    or generates side effects, or perform a combination thereof.

    The fact that the standard's definition of "expression" is flawed is
    not much of a problem in practice. Virtually everyone, implementers
    and programmers, assumes the obvious intent. Nobody believes that
    `42` isn't an expression. But it is my strongly held opinion that
    the wording should be improved in a future edition of the standard.

    I think it should say something to the effect that the meaning
    of the term "expression" is defined by the grammar. The current
    wording that claims to be the definition of the term could, with
    a few tweaks, still be turned into a valid normative statement
    *about* expressions.

    I have a similar issue with the standard's definition of "value":
    "precise meaning of the contents of an object when interpreted as
    having a specific type". It's obvious that the result of evaluating
    a non-void expression (such as the infamous `42`) is a "value",
    but the definition implies that a "value" can only be the meaning
    of the contents of an object. Nobody is actually misled by the
    current definition, but it should be improved.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Tue Jun 9 15:17:29 2026
    From Newsgroup: comp.lang.c

    On 2026-06-08 23:05, Keith Thompson wrote:
    [...]
    I've discussed this particular glitch before, but it's been a while.

    N3220 6.5.1 says:

    An *expression* is a sequence of operators and operands that
    specifies computation of a value, or that designates an object
    or a function, or that generates side effects, or that performs
    a combination thereof.

    I believe the wording is unchanged from C90 up to the latest C202y
    draft. Since the word "expression" is in italics, this is the
    standard's definition of the word.

    This is a flawed definition. The terms "operator" and "operand"
    are defined in 6.4.6:

    *punctuator: one of
    [ ] ( )
    [snip]

    A punctuator is a symbol that has independent syntactic and semantic
    significance. Depending on context, it may specify an operation to
    be performed (which in turn may yield a value or a function
    designator, produce a side effect, or some combination thereof) in
    which case it is known as an *operator* (other forms of operator also
    exist in some contexts). An *operand* is an entity on which an
    operator acts.

    Consider this expression statement:

    42;

    Is `42` an expression? Clearly it's intended to be, but there is no operator, and therefore there is no operand, so it doesn't meet the standard's definition of the word "expression".

    Above you used the term "expression statement", and then compare the
    "42" to an "expression".

    I know from my earlier C-days that '42;' is a valid statement, and so
    the term "expression statement" makes sense to me.

    I know from various languages' syntax definitions that a number like
    '42' is a sensible form for an expression (and no operators required).
    It's also depending on the context. Where expressions may be written
    (and where not) depends on the concrete language; syntactically and
    also semantically.

    Usually I'd expect above "expression-statement" to serve some purpose, semantically. I don't recall that in "C" such an expression-statement
    would serve any purpose. (Or that they'd show any observable behavior,
    if that term fits the C-parlance better?)

    Or do these stand-alone values (the "expression-statement") have some practically useful semantics?

    In other languages such stand-alone values serve a purpose; e.g. they
    may determine the result value of a block that can then be used in an
    outer context; but in "C" such constructs are obviously not possible.

    What purpose serve such stand-alone numbers in places where statements
    are expected?

    [...]

    The fact that the standard's definition of "expression" is flawed is
    not much of a problem in practice. Virtually everyone, implementers
    and programmers, assumes the obvious intent. Nobody believes that
    `42` isn't an expression. But it is my strongly held opinion that
    the wording should be improved in a future edition of the standard.

    I think it should say something to the effect that the meaning
    of the term "expression" is defined by the grammar. The current
    wording that claims to be the definition of the term could, with
    a few tweaks, still be turned into a valid normative statement
    *about* expressions.

    I have a similar issue with the standard's definition of "value":
    "precise meaning of the contents of an object when interpreted as
    having a specific type". It's obvious that the result of evaluating
    a non-void expression (such as the infamous `42`) is a "value",
    but the definition implies that a "value" can only be the meaning
    of the contents of an object. Nobody is actually misled by the
    current definition, but it should be improved.

    Janis

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Tue Jun 9 14:53:35 2026
    From Newsgroup: comp.lang.c

    On 09/06/2026 14:17, Janis Papanagnou wrote:
    On 2026-06-08 23:05, Keith Thompson wrote:
    [...]
    I've discussed this particular glitch before, but it's been a while.

    N3220 6.5.1 says:

    -a-a-a-a An *expression* is a sequence of operators and operands that
    -a-a-a-a specifies computation of a value, or that designates an object
    -a-a-a-a or a function, or that generates side effects, or that performs
    -a-a-a-a a combination thereof.

    I believe the wording is unchanged from C90 up to the latest C202y
    draft.-a Since the word "expression" is in italics, this is the
    standard's definition of the word.

    This is a flawed definition.-a The terms "operator" and "operand"
    are defined in 6.4.6:

    -a-a-a-a *punctuator: one of
    -a-a-a-a-a-a-a-a [ ] ( )
    -a-a-a-a [snip]
    -a-a-a-a A punctuator is a symbol that has independent syntactic and semantic
    -a-a-a-a significance. Depending on context, it may specify an operation to >> -a-a-a-a be performed (which in turn may yield a value or a function
    -a-a-a-a designator, produce a side effect, or some combination thereof) in >> -a-a-a-a which case it is known as an *operator* (other forms of operator >> also
    -a-a-a-a exist in some contexts). An *operand* is an entity on which an
    -a-a-a-a operator acts.

    Consider this expression statement:

    -a-a-a-a 42;

    Is `42` an expression?-a Clearly it's intended to be, but there is no
    operator, and therefore there is no operand, so it doesn't meet the
    standard's definition of the word "expression".

    Above you used the term "expression statement", and then compare the
    "42" to an "expression".

    I know from my earlier C-days that '42;' is a valid statement, and so
    the term "expression statement" makes sense to me.

    I know from various languages' syntax definitions that a number like
    '42' is a sensible form for an expression (and no operators required).
    It's also depending on the context. Where expressions may be written
    (and where not) depends on the concrete language; syntactically and
    also semantically.

    Usually I'd expect above "expression-statement" to serve some purpose, semantically. I don't recall that in "C" such an expression-statement
    would serve any purpose. (Or that they'd show any observable behavior,
    if that term fits the C-parlance better?)

    Or do these stand-alone values (the "expression-statement") have some practically useful semantics?

    In other languages such stand-alone values serve a purpose; e.g. they
    may determine the result value of a block that can then be used in an
    outer context; but in "C" such constructs are obviously not possible.

    What purpose serve such stand-alone numbers in places where statements
    are expected?

    I think it is just difficult for the syntax to ban certain expressons
    and not others. How would you express that in the grammar?

    If you ramp up the warnings, then you'll get messages like 'statement
    with no effect' or 'computed value not used', since sometimes there are side-effects that are needed:

    f() + g();

    f() and g() both do something, but nothing is done with their sum.

    In my projects, such standalone expressions are always a hard error. The
    main exceptions include (using C syntax):

    f();
    ++a;
    a = b;

    These are expressions that can return values, but that can sensibly be
    used standalone too. (I don't support value-returning compound assignments.)

    (I first introduced this check because in the past, if I'd been writing
    some C, I might write 'a = b' instead of 'a := b'. The first does
    nothing (compares then discards result), but it is not what I'd intended.)

    Anyway, I don't have it as a syntax violation either.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Tue Jun 9 16:30:23 2026
    From Newsgroup: comp.lang.c

    On 2026-06-09 15:53, Bart wrote:
    On 09/06/2026 14:17, Janis Papanagnou wrote:
    On 2026-06-08 23:05, Keith Thompson wrote:
    [...]
    I've discussed this particular glitch before, but it's been a while.

    N3220 6.5.1 says:

    -a-a-a-a An *expression* is a sequence of operators and operands that
    -a-a-a-a specifies computation of a value, or that designates an object
    -a-a-a-a or a function, or that generates side effects, or that performs >>> -a-a-a-a a combination thereof.

    I believe the wording is unchanged from C90 up to the latest C202y
    draft.-a Since the word "expression" is in italics, this is the
    standard's definition of the word.

    This is a flawed definition.-a The terms "operator" and "operand"
    are defined in 6.4.6:

    -a-a-a-a *punctuator: one of
    -a-a-a-a-a-a-a-a [ ] ( )
    -a-a-a-a [snip]
    -a-a-a-a A punctuator is a symbol that has independent syntactic and
    semantic
    -a-a-a-a significance. Depending on context, it may specify an operation to >>> -a-a-a-a be performed (which in turn may yield a value or a function
    -a-a-a-a designator, produce a side effect, or some combination thereof) in >>> -a-a-a-a which case it is known as an *operator* (other forms of operator >>> also
    -a-a-a-a exist in some contexts). An *operand* is an entity on which an
    -a-a-a-a operator acts.

    Consider this expression statement:

    -a-a-a-a 42;

    Is `42` an expression?-a Clearly it's intended to be, but there is no
    operator, and therefore there is no operand, so it doesn't meet the
    standard's definition of the word "expression".

    Above you used the term "expression statement", and then compare the
    "42" to an "expression".

    I know from my earlier C-days that '42;' is a valid statement, and so
    the term "expression statement" makes sense to me.

    I know from various languages' syntax definitions that a number like
    '42' is a sensible form for an expression (and no operators required).
    It's also depending on the context. Where expressions may be written
    (and where not) depends on the concrete language; syntactically and
    also semantically.

    Usually I'd expect above "expression-statement" to serve some purpose,
    semantically. I don't recall that in "C" such an expression-statement
    would serve any purpose. (Or that they'd show any observable behavior,
    if that term fits the C-parlance better?)

    Or do these stand-alone values (the "expression-statement") have some
    practically useful semantics?

    In other languages such stand-alone values serve a purpose; e.g. they
    may determine the result value of a block that can then be used in an
    outer context; but in "C" such constructs are obviously not possible.

    What purpose serve such stand-alone numbers in places where statements
    are expected?

    I think it is just difficult for the syntax to ban certain expressons
    and not others. How would you express that in the grammar?

    Well, I'd do that as it's done in other languages.

    Define _statements_ and define _expressions_. And defined expressions
    in contexts where a sensible operational semantics can be defined (as
    in mathematical formulas, actual function parameter lists, etc.), but
    not in places where statements are expected.


    If you ramp up the warnings, then you'll get messages like 'statement
    with no effect' or 'computed value not used', since sometimes there are side-effects that are needed:

    -a-a f() + g();

    f() and g() both do something, but nothing is done with their sum.

    Right. And I wouldn't allow a mathematical formula where the results
    are calculated but not used, here an expression, as a statement.

    But your example may indeed lead to the actual answer to my question;
    when writing just

    f();

    There's no distinction of procedures and functions in "C". One cannot
    tell whether that f() is a "procedure" (i.e. a function with no return
    value, or one with return value but the call just relying on the side
    effects). In "C" any value of f() just gets discarded in this context.

    That of course doesn't mean that it could be handled by the compilers
    and sensibly defined by the language, depending on how f() is actually
    defined. After all, 'f();' is not the same case as '42;'.

    But okay, we're talking about "C" here - so own design preferences are
    anyway irrelevant here.

    Janis

    [...]

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Tue Jun 9 17:13:10 2026
    From Newsgroup: comp.lang.c

    On 09/06/2026 16:30, Janis Papanagnou wrote:
    On 2026-06-09 15:53, Bart wrote:
    On 09/06/2026 14:17, Janis Papanagnou wrote:
    On 2026-06-08 23:05, Keith Thompson wrote:
    [...]
    I've discussed this particular glitch before, but it's been a while.

    N3220 6.5.1 says:

    -a-a-a-a An *expression* is a sequence of operators and operands that
    -a-a-a-a specifies computation of a value, or that designates an object >>>> -a-a-a-a or a function, or that generates side effects, or that performs >>>> -a-a-a-a a combination thereof.

    I believe the wording is unchanged from C90 up to the latest C202y
    draft.-a Since the word "expression" is in italics, this is the
    standard's definition of the word.

    This is a flawed definition.-a The terms "operator" and "operand"
    are defined in 6.4.6:

    -a-a-a-a *punctuator: one of
    -a-a-a-a-a-a-a-a [ ] ( )
    -a-a-a-a [snip]
    -a-a-a-a A punctuator is a symbol that has independent syntactic and
    semantic
    -a-a-a-a significance. Depending on context, it may specify an operation to
    -a-a-a-a be performed (which in turn may yield a value or a function
    -a-a-a-a designator, produce a side effect, or some combination thereof) in
    -a-a-a-a which case it is known as an *operator* (other forms of
    operator also
    -a-a-a-a exist in some contexts). An *operand* is an entity on which an >>>> -a-a-a-a operator acts.

    Consider this expression statement:

    -a-a-a-a 42;

    Is `42` an expression?-a Clearly it's intended to be, but there is no
    operator, and therefore there is no operand, so it doesn't meet the
    standard's definition of the word "expression".

    Above you used the term "expression statement", and then compare the
    "42" to an "expression".

    I know from my earlier C-days that '42;' is a valid statement, and so
    the term "expression statement" makes sense to me.

    I know from various languages' syntax definitions that a number like
    '42' is a sensible form for an expression (and no operators required).
    It's also depending on the context. Where expressions may be written
    (and where not) depends on the concrete language; syntactically and
    also semantically.

    Usually I'd expect above "expression-statement" to serve some purpose,
    semantically. I don't recall that in "C" such an expression-statement
    would serve any purpose. (Or that they'd show any observable behavior,
    if that term fits the C-parlance better?)


    I don't see why you would expect that. Statements do not have to have observable behaviour - indeed, I don't think any statements in C have observable behaviour in themselves. A "statement" in C is basically
    something that does not produce a value - "return", "if ...", "for...",
    or it is an "expression statement". Expression statements are the most
    common type of statement, I would guess (without having calculated statistics.)

    Expressions do not have to have observable behaviour. "x = y + z;" is a perfectly good expression statement, but has no observable behaviour
    (unless x, y or z are volatile). Most statements, and most expressions,
    do not have observable behaviour. (Again, I have no statistics, but I
    think this would be the solid majority of statements and expressions.)

    Of course most statements and expressions /contribute/ to later
    observable behaviour - such as printing out the result of a calculation.
    Otherwise they are not much use (and compilers can eliminate or reduce
    them, if the compiler is sure that there is no effect on observable behaviour).

    Or do these stand-alone values (the "expression-statement") have some
    practically useful semantics?

    In other languages such stand-alone values serve a purpose; e.g. they
    may determine the result value of a block that can then be used in an
    outer context; but in "C" such constructs are obviously not possible.

    What purpose serve such stand-alone numbers in places where statements
    are expected?

    I think it is just difficult for the syntax to ban certain expressons
    and not others. How would you express that in the grammar?

    Agreed.

    "42" is an expression of type "int", and so is 'printf("Hello\n")'. How
    (and why) would a language distinguish between them and allow one but
    not the other?


    Well, I'd do that as it's done in other languages.

    Define _statements_ and define _expressions_.

    C defines statements and expressions. One type of statement is the "expression statement", consisting of an expression followed by a
    semi-colon. The expression is optional - if it is missing, you have a
    null statement.

    And defined expressions
    in contexts where a sensible operational semantics can be defined (as
    in mathematical formulas, actual function parameter lists, etc.), but
    not in places where statements are expected.


    So where would "printf" fit in this picture? A printf call gives a
    result - it is an expression. It also has side-effects and observable behaviour. "while (false) ;" is a valid statement, with no
    side-effects. The distinction you want to make does not exist in C.
    (And I don't think C is special in that regard.)


    If you ramp up the warnings, then you'll get messages like 'statement
    with no effect' or 'computed value not used', since sometimes there
    are side-effects that are needed:

    -a-a-a f() + g();

    f() and g() both do something, but nothing is done with their sum.

    Right. And I wouldn't allow a mathematical formula where the results
    are calculated but not used, here an expression, as a statement.

    If the definitions of "f" and "g" are not visible to the compiler at the
    time, how could the compiler know that they have no side-effects? Lots
    of operators have side-effects - if you want to allow "x = y;" but
    disallow "x + y;" you are going to have to have a lot of special cases
    and extra grammar, syntax or constraint rules. It is better to do as C
    does, and allow expression statements in the language and let compilers
    and other tools help developers spot their mistakes.


    But your example may indeed lead to the actual answer to my question;
    when writing just

    -a f();

    There's no distinction of procedures and functions in "C". One cannot
    tell whether that f() is a "procedure" (i.e. a function with no return
    value, or one with return value but the call just relying on the side effects). In "C" any value of f() just gets discarded in this context.


    Yes.

    It is certainly possible for a language to distinguish between "pure functions" and functions/procedures with side-effects. (C actually lets
    you do that, with the [[reproducible]] and [[unsequenced]] attributes in
    C23, or compiler extensions before C23.) These can aid compiler static
    error checking and optimisation, but do not affect the grammar of the language.

    That of course doesn't mean that it could be handled by the compilers
    and sensibly defined by the language, depending on how f() is actually defined. After all, 'f();' is not the same case as '42;'.

    But okay, we're talking about "C" here - so own design preferences are
    anyway irrelevant here.

    Janis

    [...]


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From tTh@tth@none.invalid to comp.lang.c on Tue Jun 9 19:27:50 2026
    From Newsgroup: comp.lang.c

    On 6/9/26 15:53, Bart wrote:

    -a-a f() + g();

    f() and g() both do something, but nothing is done with their sum.

    I've just one question : why did you waste your life time
    with a lot of non-sense questions ?
    --
    ** **
    * tTh des Bourtoulots *
    * http://maison.tth.netlib.re/ *
    ** **
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Tue Jun 9 19:19:07 2026
    From Newsgroup: comp.lang.c

    On 09/06/2026 18:27, tTh wrote:
    On 6/9/26 15:53, Bart wrote:

    -a-a-a f() + g();

    f() and g() both do something, but nothing is done with their sum.

    -a I've just one question : why did you waste your life time
    -a with a lot of non-sense questions ?


    I didn't ask any question.

    You, on the other hand, did.

    I take it that you don't understand what is being discussed, and why. In
    that case you're wasting /your/ time posting.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Tue Jun 9 15:22:06 2026
    From Newsgroup: comp.lang.c

    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    [...]
    Above you used the term "expression statement", and then compare the
    "42" to an "expression".

    I know from my earlier C-days that '42;' is a valid statement, and so
    the term "expression statement" makes sense to me.

    Sorry, I thought that would be clear enough.

    Syntactically, an expression-statement is an optional statement
    followed by a semicolon (N3220 6.8.4, glossing over an irrelevant
    detail). I merely used it as an easy way to establish a context in
    which 42 is obviously a full expression (defined as "an expression
    that is not part of another expression, nor part of a declarator
    or abstract declarator").

    An expression-statement where the expression has no side effects
    is not useful, but it's permitted. C tends not to ban things just
    because they're not useful. `42;` is useful only to illustrate
    the point I was making about expressions.

    Since a function call is an expression, this is an expression-statement:

    printf("hello, world\n");

    [...]

    To be clear, I have zero doubt that 42 is an expression. My concern
    is that the C standard's English definition of "expression" doesn't
    quite say so. I advocate improving the wording so it expresses
    the obvious and universally agreed intent.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Tue Jun 9 15:34:06 2026
    From Newsgroup: comp.lang.c

    David Brown <david.brown@hesbynett.no> writes:
    [...]
    "42" is an expression of type "int", and so is 'printf("Hello\n")'.
    How (and why) would a language distinguish between them and allow one
    but not the other?
    [...]

    Ada, Pascal, and similar languages do exactly this, for what many
    people consider to be good reasons.

    In both languages, functions and procedures are distinct. Functions
    return values; procedures do not. An expression cannot be turned
    into a statement just by adding a semicolon. A function call is
    an expression. A procedure call is a statement, not an expression.
    An assignment is a statement, not an expression.

    For I/O, the equivalent of printf is a procedure. In C,
    printf("Hello, world\n") returns a negative result to denote an
    error (and that value is often ignored). In Ada, an error in the
    equivalent Put_Line("Hello, world") raises an exception, which
    can't easily be ignored.

    Both approaches are valid.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Wed Jun 10 09:04:26 2026
    From Newsgroup: comp.lang.c

    On 10/06/2026 00:34, Keith Thompson wrote:
    David Brown <david.brown@hesbynett.no> writes:
    [...]
    "42" is an expression of type "int", and so is 'printf("Hello\n")'.
    How (and why) would a language distinguish between them and allow one
    but not the other?
    [...]

    Ada, Pascal, and similar languages do exactly this, for what many
    people consider to be good reasons.


    I don't know enough about Ada to be sure, but Pascal does not do this -
    see below.

    In both languages, functions and procedures are distinct. Functions
    return values; procedures do not. An expression cannot be turned
    into a statement just by adding a semicolon. A function call is
    an expression. A procedure call is a statement, not an expression.
    An assignment is a statement, not an expression.

    Sure. But the key factor there is that "printf", or its equivalent
    (such as "writeln", if I remember my Pascal correctly - it's been a
    while) are /procedures/. A "print" function in Pascal that returned the number of characters printed would be a function, used in an expression,
    not a procedure used in a statement.

    The rough equivalent of the distinction between Pascal procedures and functions is that procedures are like C functions that have "void"
    return type. It's fine (and not at all a bad idea) for a language to distinguish between void and non-void like this. What cannot easily be
    done in a clear and consistent way is to distinguish between two
    expressions of type "int" (or any other general non-void type).

    In C, an expression statement "expr;" causes the expression to be
    evaluated as a void expression for its side effects (-o6.8.4p2). You
    can, arguably, say that C also requires all statements to be of "void"
    type, just like Pascal - but the cast-to-void is done implicitly to
    treat "expr;" as "(void) expr;".


    For I/O, the equivalent of printf is a procedure. In C,
    printf("Hello, world\n") returns a negative result to denote an
    error (and that value is often ignored). In Ada, an error in the
    equivalent Put_Line("Hello, world") raises an exception, which
    can't easily be ignored.

    Both approaches are valid.


    Indeed they are.

    It is also fine for a language to distinguish between "pure" functions
    and functions/procedures with side-effects and/or functions/procedures
    with observable behaviour. (A "pure procedure" would not do anything.)
    As far as I remember, Pascal does not make that distinction.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Wed Jun 10 11:10:29 2026
    From Newsgroup: comp.lang.c

    On 10/06/2026 08:04, David Brown wrote:
    On 10/06/2026 00:34, Keith Thompson wrote:
    David Brown <david.brown@hesbynett.no> writes:
    [...]
    "42" is an expression of type "int", and so is 'printf("Hello\n")'.
    How (and why) would a language distinguish between them and allow one
    but not the other?
    [...]

    Ada, Pascal, and similar languages do exactly this, for what many
    people consider to be good reasons.


    I don't know enough about Ada to be sure, but Pascal does not do this -
    see below.

    In both languages, functions and procedures are distinct.-a Functions
    return values; procedures do not.-a An expression cannot be turned
    into a statement just by adding a semicolon.-a A function call is
    an expression.-a A procedure call is a statement, not an expression.
    An assignment is a statement, not an expression.

    Sure.-a But the key factor there is that "printf", or its equivalent
    (such as "writeln", if I remember my Pascal correctly - it's been a
    while) are /procedures/.-a A "print" function in Pascal that returned the number of characters printed would be a function, used in an expression,
    not a procedure used in a statement.

    The rough equivalent of the distinction between Pascal procedures and functions is that procedures are like C functions that have "void"
    return type.-a It's fine (and not at all a bad idea) for a language to distinguish between void and non-void like this.-a What cannot easily be done in a clear and consistent way is to distinguish between two
    expressions of type "int" (or any other general non-void type).

    In C, an expression statement "expr;" causes the expression to be
    evaluated as a void expression for its side effects (-o6.8.4p2).

    In C201x draft. 6.8.4p2 is about selection statements.

    -a You
    can, arguably, say that C also requires all statements to be of "void"
    type, just like Pascal - but the cast-to-void is done implicitly to
    treat "expr;" as "(void) expr;".

    That's not quite the same thing. If I write:

    int a;
    a;

    then gcc -Wall will report a warning. But write it as (void)a, then it doesn't.

    While this is awkward to express in a language's grammar, it can choose
    to list the kinds of expressions that /are/ allowed to be statements,
    rather than leave it to the whim of an implemenation. (The ones that
    aren't allowed would be a much bigger, unlimited set.)

    For example:

    E(...); // function call
    ++E; // increment
    E = E; // assigment (and compound assignment)

    E is any expression term. Here, the call/increment/assignment is the
    top-level AST mode.

    (I do this in my stuff, and there I can override the restriction using
    'eval': eval a + b, which turns it into an allowed form.

    Mainly this is for convenience of testing, but it was also used to
    ensure an expression ended up in the primary register for subsequent
    inline assembly.)


    For I/O, the equivalent of printf is a procedure.-a In C,
    printf("Hello, world\n") returns a negative result to denote an
    error (and that value is often ignored).-a In Ada, an error in the
    equivalent Put_Line("Hello, world") raises an exception, which
    can't easily be ignored.

    Both approaches are valid.


    Indeed they are.

    Distinguishing between function and procedure is incredibly rare in
    modern languages. There the preoccupation seems to be to unify
    everything: everything is a function, even if-statements and loops.
    Every function is a closure, etc. I do not consider that useful.

    It is also fine for a language to distinguish between "pure" functions
    and functions/procedures with side-effects and/or functions/procedures
    with observable behaviour.-a (A "pure procedure" would not do anything.)
    As far as I remember, Pascal does not make that distinction.


    This goes the other way and is a better idea!

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Wed Jun 10 03:17:41 2026
    From Newsgroup: comp.lang.c

    David Brown <david.brown@hesbynett.no> writes:
    On 10/06/2026 00:34, Keith Thompson wrote:
    David Brown <david.brown@hesbynett.no> writes:
    [...]
    "42" is an expression of type "int", and so is 'printf("Hello\n")'.
    How (and why) would a language distinguish between them and allow one
    but not the other?
    [...]
    Ada, Pascal, and similar languages do exactly this, for what many
    people consider to be good reasons.

    I don't know enough about Ada to be sure, but Pascal does not do this
    - see below.

    You seem to disagree with me, but then you describe most of what
    I wrote. I'm not sure where you disagree, or where our signals
    got crossed.

    Ada and Pascal don't have expression statements. The Pascal
    (writeln(...)) and Ada (Put_Line(...)) constructs most similar
    to C's printf("Hello\n") are procedure calls. 42 can't made into
    a statement by adding a semicolon. Neither can any function call.
    But a procedure call can. That's how and why Pascal and Ada allow
    one but not the other. (And both languages deliberately make it
    awkward to ignore the value returned by a function.)

    In both languages, functions and procedures are distinct. Functions
    return values; procedures do not. An expression cannot be turned
    into a statement just by adding a semicolon. A function call is
    an expression. A procedure call is a statement, not an expression.
    An assignment is a statement, not an expression.

    Sure. But the key factor there is that "printf", or its equivalent
    (such as "writeln", if I remember my Pascal correctly - it's been a
    while) are /procedures/. A "print" function in Pascal that returned
    the number of characters printed would be a function, used in an
    expression, not a procedure used in a statement.

    Right, and a Pascal function that prints its argument and returns an
    integer value could not be used by itself as a statement.

    The rough equivalent of the distinction between Pascal procedures and functions is that procedures are like C functions that have "void"
    return type. It's fine (and not at all a bad idea) for a language to distinguish between void and non-void like this. What cannot easily
    be done in a clear and consistent way is to distinguish between two expressions of type "int" (or any other general non-void type).

    Right. Which is why the I/O and similar subroutines that you'd want to
    use as statements are procedures, not functions.

    [...]
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Wed Jun 10 13:29:13 2026
    From Newsgroup: comp.lang.c

    On 10/06/2026 12:10, Bart wrote:
    On 10/06/2026 08:04, David Brown wrote:
    On 10/06/2026 00:34, Keith Thompson wrote:
    David Brown <david.brown@hesbynett.no> writes:
    [...]
    "42" is an expression of type "int", and so is 'printf("Hello\n")'.
    How (and why) would a language distinguish between them and allow one
    but not the other?
    [...]

    Ada, Pascal, and similar languages do exactly this, for what many
    people consider to be good reasons.


    I don't know enough about Ada to be sure, but Pascal does not do this
    - see below.

    In both languages, functions and procedures are distinct.-a Functions
    return values; procedures do not.-a An expression cannot be turned
    into a statement just by adding a semicolon.-a A function call is
    an expression.-a A procedure call is a statement, not an expression.
    An assignment is a statement, not an expression.

    Sure.-a But the key factor there is that "printf", or its equivalent
    (such as "writeln", if I remember my Pascal correctly - it's been a
    while) are /procedures/.-a A "print" function in Pascal that returned
    the number of characters printed would be a function, used in an
    expression, not a procedure used in a statement.

    The rough equivalent of the distinction between Pascal procedures and
    functions is that procedures are like C functions that have "void"
    return type.-a It's fine (and not at all a bad idea) for a language to
    distinguish between void and non-void like this.-a What cannot easily
    be done in a clear and consistent way is to distinguish between two
    expressions of type "int" (or any other general non-void type).

    In C, an expression statement "expr;" causes the expression to be
    evaluated as a void expression for its side effects (-o6.8.4p2).

    In C201x draft. 6.8.4p2 is about selection statements.


    C23 is the latest C standard, so that was what I was using (n3220.pdf).
    It is unfortunate that C23 has slightly different numbers for some
    sections - the standards authors have previously managed a higher
    consistency between versions. Section 6.8.3p2 is the number for C11 (as
    you have probably found already).

    -a You can, arguably, say that C also requires all statements to be of
    "void" type, just like Pascal - but the cast-to-void is done
    implicitly to treat "expr;" as "(void) expr;".

    That's not quite the same thing. If I write:

    -a-a int a;

    (Just to be clear that we agree - "int a;" is a declaration, not a
    statement, expression, or expression statement.)

    -a-a a;

    then gcc -Wall will report a warning. But write it as (void)a, then it doesn't.

    Yes. But that's a matter of warnings and conventional idioms, not the C language. "a;" and "(void) a;" both mean the same thing in the C
    language. gcc, like many compilers, has warnings on unused variables
    and parameters, and set-but-unused variables, as these are often the
    result of mistakes in the code. And tools that have such warnings have
    ways to mark intentionally unused variables and parameters - such as __attribute__(("unused")) or C23's "[[maybe_unused]]". A common idiom
    is that casting an expression or variable to void tells the compiler
    that you know the variable or parameter is unused, and only evaluated
    for its side-effects (if any).


    While this is awkward to express in a language's grammar, it can choose
    to list the kinds of expressions that /are/ allowed to be statements,
    rather than leave it to the whim of an implemenation. (The ones that
    aren't allowed would be a much bigger, unlimited set.)


    Yes, a language could do that. In C, the language chooses to allow expressions of any type - that's the simplest to express!

    For example:

    -a-a E(...);-a-a-a-a-a // function call
    -a-a ++E;-a-a-a-a-a-a-a-a // increment
    -a-a E = E;-a-a-a-a-a-a // assigment (and compound assignment)

    E is any expression term. Here, the call/increment/assignment is the top-level AST mode.

    (I do this in my stuff, and there I can override the restriction using 'eval': eval a + b, which turns it into an allowed form.

    Mainly this is for convenience of testing, but it was also used to
    ensure an expression ended up in the primary register for subsequent
    inline assembly.)

    A better choice for a language that wanted to restrict the kinds of expressions that can be used as statements would be to do as Pascal does
    - allow only what C would consider "void" expressions as statements, and
    make things like assignment void expressions. Saying that "x = 1" is an expression of type "int" that can be used as a statement while "x + 1"
    is an expression of type "int" that cannot be used as a statement would
    likely require significant complication in the language rules to work
    well. Saying that "x = 1" is a void expression and can therefore be
    used as a statement, while "x + 1" is a non-void expression and can
    therefore not be used as a statement, is simple and clear. The cost -
    or the benefit, depending on your viewpoint and preferences - is that it
    is no longer possible to write "x = y = 1" or "while (x = read())...".




    For I/O, the equivalent of printf is a procedure.-a In C,
    printf("Hello, world\n") returns a negative result to denote an
    error (and that value is often ignored).-a In Ada, an error in the
    equivalent Put_Line("Hello, world") raises an exception, which
    can't easily be ignored.

    Both approaches are valid.


    Indeed they are.

    Distinguishing between function and procedure is incredibly rare in
    modern languages. There the preoccupation seems to be to unify
    everything: everything is a function, even if-statements and loops.
    Every function is a closure, etc. I do not consider that useful.


    Fair enough. There are pros and cons to any such choices.

    It is also fine for a language to distinguish between "pure" functions
    and functions/procedures with side-effects and/or functions/procedures
    with observable behaviour.-a (A "pure procedure" would not do
    anything.) As far as I remember, Pascal does not make that distinction.


    This goes the other way and is a better idea!


    I personally think the "purity" of a function/procedure is a more
    important distinction than whether or not it evaluates to a non-void.
    But it is hard to see how it would work well in a compiled imperative
    language - an emphasis on pure functions is more the domain of function programming languages. But a discussion on that would be more for comp.lang.misc than comp.lang.c


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Wed Jun 10 13:43:01 2026
    From Newsgroup: comp.lang.c

    On 10/06/2026 12:17, Keith Thompson wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 10/06/2026 00:34, Keith Thompson wrote:
    David Brown <david.brown@hesbynett.no> writes:
    [...]
    "42" is an expression of type "int", and so is 'printf("Hello\n")'.
    How (and why) would a language distinguish between them and allow one
    but not the other?
    [...]
    Ada, Pascal, and similar languages do exactly this, for what many
    people consider to be good reasons.

    I don't know enough about Ada to be sure, but Pascal does not do this
    - see below.

    You seem to disagree with me, but then you describe most of what
    I wrote. I'm not sure where you disagree, or where our signals
    got crossed.


    It was most likely a misunderstanding or misinterpretation of what you
    wrote - or what I wrote in the earlier post. We agree on how Pascal
    (and, AFAIUI, Ada) work, and we can let that stand as a clarification
    rather than risk yet another endless thread on the details of exactly
    what words were used.

    Ada and Pascal don't have expression statements. The Pascal
    (writeln(...)) and Ada (Put_Line(...)) constructs most similar
    to C's printf("Hello\n") are procedure calls. 42 can't made into
    a statement by adding a semicolon. Neither can any function call.
    But a procedure call can. That's how and why Pascal and Ada allow
    one but not the other. (And both languages deliberately make it
    awkward to ignore the value returned by a function.)

    Agreed.


    In both languages, functions and procedures are distinct. Functions
    return values; procedures do not. An expression cannot be turned
    into a statement just by adding a semicolon. A function call is
    an expression. A procedure call is a statement, not an expression.
    An assignment is a statement, not an expression.

    Sure. But the key factor there is that "printf", or its equivalent
    (such as "writeln", if I remember my Pascal correctly - it's been a
    while) are /procedures/. A "print" function in Pascal that returned
    the number of characters printed would be a function, used in an
    expression, not a procedure used in a statement.

    Right, and a Pascal function that prints its argument and returns an
    integer value could not be used by itself as a statement.

    Agreed.


    The rough equivalent of the distinction between Pascal procedures and
    functions is that procedures are like C functions that have "void"
    return type. It's fine (and not at all a bad idea) for a language to
    distinguish between void and non-void like this. What cannot easily
    be done in a clear and consistent way is to distinguish between two
    expressions of type "int" (or any other general non-void type).

    Right. Which is why the I/O and similar subroutines that you'd want to
    use as statements are procedures, not functions.


    That is often the case, but is certainly not required by the language.
    Even standard functions can have side-effects (like "random"), though idiomatic Pascal typically uses procedures where the results are
    obtained by passing result variables by reference.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Wed Jun 10 14:08:52 2026
    From Newsgroup: comp.lang.c

    David Brown <david.brown@hesbynett.no> writes:
    [...]
    In C, an expression statement "expr;" causes the expression to be
    evaluated as a void expression for its side effects (-o6.8.4p2). You
    can, arguably, say that C also requires all statements to be of "void"
    type, just like Pascal - but the cast-to-void is done implicitly to
    treat "expr;" as "(void) expr;".
    [...]

    In an expression statement, the expression is "evaluated as a void
    expression for its side effects". I think that's equivalent to
    convert (not casting!) it to void, but the standard doesn't describe
    it that way.

    6.3.2.2: "If an expression of any other type [other than void]
    is evaluated as a void expression, its value or designator is
    discarded."

    But statements have no type.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Thu Jun 11 20:12:32 2026
    From Newsgroup: comp.lang.c

    On 2026-06-10 00:34, Keith Thompson wrote:
    David Brown <david.brown@hesbynett.no> writes:
    [...]
    "42" is an expression of type "int", and so is 'printf("Hello\n")'.
    How (and why) would a language distinguish between them and allow one
    but not the other?
    [...]

    Ada, Pascal, and similar languages do exactly this, for what many
    people consider to be good reasons.

    Right.

    What I'm not sure about is the predominance of "these" or "those"
    languages. - Is that clear distinction of procedures and function
    the typical case, or are the "C-derived" languages predominant and
    languages with a clear distinction (meanwhile?) just outliers?

    There's of course also other languages that distinguish procedures
    from functions "only" by the 'void' "return type", but are anyway
    able to diagnose the appropriate context and emit error messages
    when inappropriately used.


    In both languages, functions and procedures are distinct. Functions
    return values; procedures do not. An expression cannot be turned
    into a statement just by adding a semicolon. A function call is
    an expression. A procedure call is a statement, not an expression.
    An assignment is a statement, not an expression.

    For I/O, the equivalent of printf is a procedure. In C,
    printf("Hello, world\n") returns a negative result to denote an
    error (and that value is often ignored).

    Erm, I hope that above printf() call does not create an error, but
    returns the number of characters in the printed text. ;-)

    Janis

    [...]

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Thu Jun 11 20:29:16 2026
    From Newsgroup: comp.lang.c

    On 2026-06-10 09:04, David Brown wrote:
    [...]

    The rough equivalent of the distinction between Pascal procedures and functions is that procedures are like C functions that have "void"
    return type.-a It's fine (and not at all a bad idea) for a language to distinguish between void and non-void like this.-a What cannot easily be done in a clear and consistent way is to distinguish between two
    expressions of type "int" (or any other general non-void type).

    Here I cannot follow you. - The C-compiler can analyze code to do
    optimizations and even (as so often stated) "assume" things about
    the intent concerning UB and optimization but cannot value facts
    about types and context? - If so, then it sounds rather arbitrary.

    [...]

    It is also fine for a language to distinguish between "pure" functions
    and functions/procedures with side-effects and/or functions/procedures
    with observable behaviour.-a (A "pure procedure" would not do anything.)

    By "would not do anything" you probably mean that it would not have side-effects on/with relatively global entities in the program?

    As far as I remember, Pascal does not make that distinction.

    Pascal functions and procedures can affect and be affected by global
    entities. Predefined functions and procedures can have side effects
    also unrelated to global entities in the program (e.g. print effect).
    A procedure/function not affecting the global (or surrounding stack) environment could likely be identified. But here we're anyway talking
    about the (clean!) return-interface of functions (as opposed to the procedures).

    Janis

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Thu Jun 11 15:13:09 2026
    From Newsgroup: comp.lang.c

    On 2026-06-11 14:12, Janis Papanagnou wrote:
    On 2026-06-10 00:34, Keith Thompson wrote:
    ...
    For I/O, the equivalent of printf is a procedure. In C,
    printf("Hello, world\n") returns a negative result to denote an
    error (and that value is often ignored).

    Erm, I hope that above printf() call does not create an error, but
    returns the number of characters in the printed text. ;-)

    Hope is nice. I hope, in particular, that you're aware that there are
    not guarantees on that matter?
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Fri Jun 12 00:37:03 2026
    From Newsgroup: comp.lang.c

    On 2026-06-11 21:13, James Kuyper wrote:
    On 2026-06-11 14:12, Janis Papanagnou wrote:
    On 2026-06-10 00:34, Keith Thompson wrote:
    ...
    For I/O, the equivalent of printf is a procedure. In C,
    printf("Hello, world\n") returns a negative result to denote an
    error (and that value is often ignored).

    Erm, I hope that above printf() call does not create an error, but
    returns the number of characters in the printed text. ;-)

    Hope is nice. I hope, in particular, that you're aware that there are
    not guarantees on that matter?

    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.

    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.

    Concerning the "guarantees" that you're asking for I sadly have to say
    that I meanwhile expect nothing sensible at all any more from "C". ;-)

    But to be more serious again...

    The man-page is very unspecific on that; 'man 3 printf' says:
    "If an output error is encountered, a negative value is returned."

    Now of course an error can occur with that simple 'printf' above, for
    example, by issuing an 'fclose (stdout);' before the 'printf (...);'
    But what can I as a C-programmer derive from that; how would one act
    on that. (That's just rhetorical.)

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

    But yes, I'm aware of negative OS function or library function output.

    Our rules (back in my C/C++ days) suggested to catch any sensible and
    possible error indications to quickly localize any potential issues.

    Janis

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Thu Jun 11 23:05:17 2026
    From Newsgroup: comp.lang.c

    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    On 2026-06-11 21:13, James Kuyper wrote:
    On 2026-06-11 14:12, Janis Papanagnou wrote:
    On 2026-06-10 00:34, Keith Thompson wrote:
    ...
    For I/O, the equivalent of printf is a procedure. In C,
    printf("Hello, world\n") returns a negative result to denote an
    error (and that value is often ignored).

    Erm, I hope that above printf() call does not create an error, but
    returns the number of characters in the printed text. ;-)

    Hope is nice. I hope, in particular, that you're aware that there are
    not guarantees on that matter?

    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.

    The manual page also notes for the cases where printf returns -1:

    For the conditions under which [CX] [Option Start] dprintf(), [Option End] fprintf(),
    and printf() fail and may fail, refer to fputc() or fputwc().

    In addition, all forms of fprintf() shall fail if:

    [EILSEQ]
    [CX] [Option Start] A wide-character code that does not correspond to a valid character has been detected. [Option End]
    [EOVERFLOW]
    [CX] [Option Start] The value to be returned is greater than {INT_MAX}. [Option End]

    [CX] [Option Start] The asprintf() function shall fail if:

    [ENOMEM]
    Insufficient storage space is available.

    The dprintf() function may fail if:

    [EBADF]
    The fildes argument is not a valid file descriptor.

    [Option End]

    The [CX] [Option Start] dprintf(), [Option End] fprintf(), and printf() functions may fail if:

    [ENOMEM]
    [CX] [Option Start] Insufficient storage space is available. [Option End]

    The fputc(3) errors:

    ERRORS

    The fputc() function shall fail if either the stream is unbuffered or the stream's buffer needs to be flushed, and:

    [EAGAIN]
    [CX] [Option Start] The O_NONBLOCK flag is set for the file descriptor underlying stream and the thread would be delayed in the write operation. [Option End]
    [EBADF]
    [CX] [Option Start] The file descriptor underlying stream is not a valid file descriptor open for writing. [Option End]
    [EFBIG]
    [CX] [Option Start] An attempt was made to write to a file that exceeds the maximum file size. [Option End]
    [EFBIG]
    [CX] [Option Start] An attempt was made to write to a file that exceeds the file size limit of the process.
    [Option End] [XSI] [Option Start] A SIGXFSZ signal shall also be generated for the thread. [Option End]
    [EFBIG]
    [CX] [Option Start] The file is a regular file and an attempt was made to write at or beyond the offset maximum. [Option End]
    [EINTR]
    [CX] [Option Start] The write operation was terminated due to the receipt of a signal, and no data was transferred. [Option End]
    [EIO]
    [CX] [Option Start] A physical I/O error has occurred, or the process is a member of a background process group attempting to write to its controlling terminal, TOSTOP is set, the calling thread is not blocking SIGTTOU, the process is not ignoring SIGTTOU, and the process group of the process is orphaned. This error may also be returned under implementation-defined conditions. [Option End]
    [ENOSPC]
    [CX] [Option Start] There was no free space remaining on the device containing the file. [Option End]
    [EPIPE]
    [CX] [Option Start] An attempt is made to write to a pipe or FIFO that is not open for reading by any process. A SIGPIPE signal shall also be sent to the thread. [Option End]


    The fputc() function may fail if:

    [ENOMEM]
    [CX] [Option Start] Insufficient storage space is available. [Option End]
    [ENXIO]
    [CX] [Option Start] A request was made of a nonexistent device, or the request was outside the capabilities of the device. [Option End]


    The '[Option start]' '[Option end]' tags describe behavior aligned with the C standard.

    https://pubs.opengroup.org/onlinepubs/9799919799/functions/fputc.html https://pubs.opengroup.org/onlinepubs/9799919799/functions/printf.html
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Fri Jun 12 01:18:17 2026
    From Newsgroup: comp.lang.c

    On 2026-06-12 01:05, Scott Lurndal wrote:

    The manual page also notes for the cases where printf returns -1:

    The man page on my Linux doesn't. :-(

    [snip error list]

    Thanks for the error list.

    [snip opengroup-links]

    Yeah, and these links; always useful to look up these resources.

    Janis

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Thu Jun 11 17:41:38 2026
    From Newsgroup: comp.lang.c

    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    On 2026-06-11 21:13, James Kuyper wrote:
    On 2026-06-11 14:12, Janis Papanagnou wrote:
    On 2026-06-10 00:34, Keith Thompson wrote:
    ...
    For I/O, the equivalent of printf is a procedure. In C,
    printf("Hello, world\n") returns a negative result to denote an
    error (and that value is often ignored).

    Erm, I hope that above printf() call does not create an error, but
    returns the number of characters in the printed text. ;-)

    Hope is nice. I hope, in particular, that you're aware that there are
    not guarantees on that matter?

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

    On my system, a small write to /dev/full will typically succeed, since
    the output is buffered rather than being immediately sent to the
    file. It fails with ENOSPC after about 4 kbytes.

    If I use fopen() to open /dev/full, then write to it, then fclose()
    it, the fclose() fails. Since files are implicitly closed when
    main() finishes, this is likely to go undetected.

    A common pattern is "some_program > some_file", which redirects
    stdout to a file but leaves stderr going to the default (typically
    the tty).

    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. No diagnostic or other error
    indication is required.

    Concerning the "guarantees" that you're asking for I sadly have to say
    that I meanwhile expect nothing sensible at all any more from "C". ;-)

    But to be more serious again...

    The man-page is very unspecific on that; 'man 3 printf' says:
    "If an output error is encountered, a negative value is returned."

    Now of course an error can occur with that simple 'printf' above, for example, by issuing an 'fclose (stdout);' before the 'printf (...);'
    But what can I as a C-programmer derive from that; how would one act
    on that. (That's just rhetorical.)

    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.

    For example, GNU coreutils "cat" and "echo" both print "write error:
    No space left on device" on stderr and exit with a status of 1 when
    output is redirected to /dev/full -- if the output is big enough.
    I haven't checked the source, but they must be explicitly checking
    the result of both whatever output routine(s) they use and the
    fclose(), or perhaps doing some fancy system-specific stuff that
    has the same effect.

    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.

    But yes, I'm aware of negative OS function or library function output.

    Our rules (back in my C/C++ days) suggested to catch any sensible and possible error indications to quickly localize any potential issues.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Thu Jun 11 20:41:49 2026
    From Newsgroup: comp.lang.c

    On 2026-06-11 18:37, Janis Papanagnou wrote:
    On 2026-06-11 21:13, James Kuyper wrote:
    On 2026-06-11 14:12, Janis Papanagnou wrote:
    On 2026-06-10 00:34, Keith Thompson wrote:
    ...
    For I/O, the equivalent of printf is a procedure. In C,
    printf("Hello, world\n") returns a negative result to denote an
    error (and that value is often ignored).

    Erm, I hope that above printf() call does not create an error, but
    returns the number of characters in the printed text. ;-)

    Hope is nice. I hope, in particular, that you're aware that there are
    not guarantees on that matter?

    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.

    Every I/O function has a way of reporting failure, because every one is
    capable of failing. That's because, if nothing else, hardware problems
    could prevent I/O from happening. How much attention you need to pay to
    that possibility depends upon the context.

    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. 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.

    ...
    Now of course an error can occur with that simple 'printf' above, for example, by issuing an 'fclose (stdout);' before the 'printf (...);'
    But what can I as a C-programmer derive from that; how would one act
    on that. (That's just rhetorical.)

    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. 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.
    But I was never involved in writing interactive programs, where I
    suspect that would not be acceptable.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Fri Jun 12 12:55:36 2026
    From Newsgroup: comp.lang.c

    On 11/06/2026 20:29, Janis Papanagnou wrote:
    On 2026-06-10 09:04, David Brown wrote:
    [...]

    The rough equivalent of the distinction between Pascal procedures and
    functions is that procedures are like C functions that have "void"
    return type.-a It's fine (and not at all a bad idea) for a language to
    distinguish between void and non-void like this.-a What cannot easily
    be done in a clear and consistent way is to distinguish between two
    expressions of type "int" (or any other general non-void type).

    Here I cannot follow you. - The C-compiler can analyze code to do optimizations and even (as so often stated) "assume" things about
    the intent concerning UB and optimization but cannot value facts
    about types and context? - If so, then it sounds rather arbitrary.


    I think this thread is getting difficult to follow - there is a lot of wandering and vagueness (mostly from me, I must admit). So I am not
    sure if it is worth pursuing further.

    However, what I am trying to say is that it is easy for a programming
    language design to make a distinction between "things that result in a
    value of a type like int" and "things that do not result in a value" -
    and then the language could decide that the former are "expressions"
    that cannot stand alone, and the later are "statements". It is much
    harder for a language description to say that /some/ "things that result
    in a value of a type like int" can be used as "statements", while others
    can be used only as "expressions" and not stand-alone statements.

    [...]

    It is also fine for a language to distinguish between "pure" functions
    and functions/procedures with side-effects and/or functions/procedures
    with observable behaviour.-a (A "pure procedure" would not do anything.)

    By "would not do anything" you probably mean that it would not have side-effects on/with relatively global entities in the program?

    Yes.

    A "pure" function is one whose output depends entirely on its input parameters, and has no side-effects. (Some details of the definition
    may be varied, such as the ability to read global data that never
    changes after the first call. Perhaps memoizing might also be allowed.)
    If you don't use the value of a call to a pure function - or if the
    pure function does not return a value - then it can't do anything useful.


    As far as I remember, Pascal does not make that distinction.

    Pascal functions and procedures can affect and be affected by global entities. Predefined functions and procedures can have side effects
    also unrelated to global entities in the program (e.g. print effect).
    A procedure/function not affecting the global (or surrounding stack) environment could likely be identified. But here we're anyway talking
    about the (clean!) return-interface of functions (as opposed to the procedures).


    That all agrees with what I thought.



    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Sat Jun 13 15:01:38 2026
    From Newsgroup: comp.lang.c

    On 2026-06-12 12:55, David Brown wrote:
    On 11/06/2026 20:29, Janis Papanagnou wrote:
    [...]

    I think this thread is getting difficult to follow - there is a lot of wandering and vagueness (mostly from me, I must admit).-a So I am not
    sure if it is worth pursuing further.

    I agree, and I appreciate your post to clarify some things. - Thanks.

    Janis

    [...]

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Sun Jun 28 02:49:30 2026
    From Newsgroup: comp.lang.c

    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

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Sun Jun 28 03:16:15 2026
    From Newsgroup: comp.lang.c

    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

    [...]
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Sat Jun 27 21:00:39 2026
    From Newsgroup: comp.lang.c

    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 */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Sun Jun 28 09:52:36 2026
    From Newsgroup: comp.lang.c

    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.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Sun Jun 28 18:28:46 2026
    From Newsgroup: comp.lang.c

    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 */
    --- Synchronet 3.22a-Linux NewsLink 1.2