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'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".
[...]
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.
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?
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:
-a-a f() + g();
f() and g() both do something, but nothing is done with their sum.
[...]
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-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
-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.
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
[...]
-a-a f() + g();
f() and g() both do something, but nothing is done with their sum.
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 ?
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.
"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?
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.
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).
-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;".
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.
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.
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).
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:
-a-a int a;
-a-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:
-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.)
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!
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.
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;".
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).
[...]
[...]
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).
[...]
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.
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. ;-)
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?
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:
[snip error list]
[snip opengroup-links]
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.
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.
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?)
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).
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.
[...]
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'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.
[...]
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.
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.
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.
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.
[...]--- Synchronet 3.22a-Linux NewsLink 1.2
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.
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 <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.
| Sysop: | Amessyroom |
|---|---|
| Location: | Fayetteville, NC |
| Users: | 70 |
| Nodes: | 6 (0 / 6) |
| Uptime: | 37:38:51 |
| Calls: | 948 |
| Calls today: | 2 |
| Files: | 1,325 |
| Messages: | 280,462 |