On Sat, 3 May 2025 21:42:37 -0400
Richard Damon <richard@damon-family.org> wrote:
Bigger than that, and you likely want to pass the object by address,
not by value, passing just a pointer to it.
That sort of thinking is an example of Knutian premature optimization.
Here's a version of the same code that corrects the above distracting
issues
#include <stdio.h>
struct S { int a[10]; };
int main()
{
struct S a, b = { 0 };
int *pa, *pb, *pc;
pa = &a.a[5],
pb = &b.a[5],
pc = &(a = b).a[5],
printf("%p %p %p\n", (void *) pa, (void *) pb, (void *) pc);
}
This version has no UB.
It's only not UB in the nazal demons sense.
It's UB in a sense that we can't predict values of expressions
like (pa==pc) and (pb==pc). I.e. pc is completely useless. In my book
it is form of UB.
Andrey Tarasevich <noone@noone.net> writes:
[...]
#include <stdio.h>
struct S { int a[10]; };
int main()
{
struct S a, b = { 0 };
int *pa, *pb, *pc;
pa = &a.a[5],
pb = &b.a[5],
pc = &(a = b).a[5],
printf("%p %p %p\n", (void *) pa, (void *) pb, (void *) pc);
}
This version has no UB.
I believe it does. pc points to an element of an object with
temporary lifetime. The value of pc is then used after the object
it points to has reached the end of its lifetime. At that point,
pc has an indeterminate value.
N3096 6.2.4p2: "If a pointer value is used in an evaluation after
the object the pointer points to (or just past) reaches the end of
its lifetime, the behavior is undefined. The representation of a
pointer object becomes indeterminate when the object the pointer
points to (or just past) reaches the end of its lifetime."
N3096 6.2.4p2: "If a pointer value is used in an evaluation after
the object the pointer points to (or just past) reaches the end of
its lifetime, the behavior is undefined. The representation of a
pointer object becomes indeterminate when the object the pointer
points to (or just past) reaches the end of its lifetime."
It seems clear to me that "pc" has an indeterminate value after the expression assigning, since it points to an object with temporary lifetime.
And attempting to use the value of an object with automatic storage
while it has an indeterminate value is undefined behaviour.
As far as I can see, simply reading the value in "pc" to print it out is
UB according to the C standards. It is clearly going to be a harmless operation on most hardware, but there are processors where pointer
registers are more complicated than simple linear addresses - they can
track some kind of segment structure describing the range of a data
block, or permissions for access to the data, and such structures could
have been deactivated or deallocated when the temporary lifetime object died. Even attempting to read the value of the pointer, without dereferencing it, would then cause some kind of fault or trap.
Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
Andrey Tarasevich <noone@noone.net> writes:
[...]
#include <stdio.h>
struct S { int a[10]; };
int main()
{
struct S a, b = { 0 };
int *pa, *pb, *pc;
pa = &a.a[5],
pb = &b.a[5],
pc = &(a = b).a[5],
printf("%p %p %p\n", (void *) pa, (void *) pb, (void *) pc);
}
This version has no UB.
I believe it does. pc points to an element of an object with
temporary lifetime. The value of pc is then used after the object
it points to has reached the end of its lifetime. At that point,
pc has an indeterminate value.
N3096 6.2.4p2: "If a pointer value is used in an evaluation after
the object the pointer points to (or just past) reaches the end of
its lifetime, the behavior is undefined. The representation of a
pointer object becomes indeterminate when the object the pointer
points to (or just past) reaches the end of its lifetime."
Note commas above. Assignment to pc and call to printf are parts
of a single expression, so use of pc is within lifetime of the
temporary object.
What C90 constraint does it violate? Both gcc and clang reject it
with "-std=c90 -pedantic-errors", with an error message "ISO C90
forbids subscripting non-lvalue array", but I don't see a relevant
constraint in the C90 standard.
That would get an immediate downcheck during review for exactly
that reason.
Of course. In fact, if someone presented such code for review (and
assuming I noticed the commas!) I'd have to consider whether it was done maliciously, intentionally deceptively, due to incompetence, or smart-
arse coding. In all my C coding experience, I can't recall ever coming across a single situation when I thought the use of the comma operator
was appropriate in the kind of code I work with.
On Wed 5/7/2025 12:37 AM, David Brown wrote:
That would get an immediate downcheck during review for exactly
that reason.
Of course. In fact, if someone presented such code for review (and
assuming I noticed the commas!) I'd have to consider whether it was
done maliciously, intentionally deceptively, due to incompetence, or
smart- arse coding. In all my C coding experience, I can't recall
ever coming across a single situation when I thought the use of the
comma operator was appropriate in the kind of code I work with.
Wow! That's catastrophically bad.
As it has been stated many times before, both C and C++ are programming languages that embrace both statement-level and expression-level
programming. Expression-level programming (e.g. where `?:` is used for branching and `,` for sequencing) is a very valuable and massively
important programming paradigm in these languages. The fact that
elaborate expression-level programming is not in nay way abandoned or
shunned today is pretty obvious in C++, since C++ took major steps
lately to develop its expression-level capabilities. But it has always
been and will always remain important in C as well.
The proclivity to stick exclusively to statement-level programming in C
and, God forbid, impose it in others through so called "code reviews"...
that would be a trait specific to "sweatshop" development outfits, which strive to replace quality with quantity. I'd agree that in a revolving
door employment environment relying on a large number of low-competence developers such code might be seen as "too confusing". But I don't see
why we should set our standards that low here, in `comp.lang.c`.
On Tue 5/6/2025 10:36 AM, Waldek Hebisch wrote:
Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
Andrey Tarasevich <noone@noone.net> writes:
[...]
#include <stdio.h>
struct S { int a[10]; };
int main()
{
struct S a, b = { 0 };
int *pa, *pb, *pc;
pa = &a.a[5],
pb = &b.a[5],
pc = &(a = b).a[5],
printf("%p %p %p\n", (void *) pa, (void *) pb, (void *) pc);
}
This version has no UB.
I believe it does. pc points to an element of an object with
temporary lifetime. The value of pc is then used after the object
it points to has reached the end of its lifetime. At that point,
pc has an indeterminate value.
N3096 6.2.4p2: "If a pointer value is used in an evaluation after
the object the pointer points to (or just past) reaches the end of
its lifetime, the behavior is undefined. The representation of a
pointer object becomes indeterminate when the object the pointer
points to (or just past) reaches the end of its lifetime."
Note commas above. Assignment to pc and call to printf are parts
of a single expression, so use of pc is within lifetime of the
temporary object.
Exactly. I thought the nature of the corrections I made (i.e. the
deliberate usage of comma operator) would be strikingly obvious to the participants of the thread. But alas...
Here's a version of the same code that corrects the above distracting
issues
#include <stdio.h>
struct S { int a[10]; };
int main()
{
struct S a, b = { 0 };
int *pa, *pb, *pc;
pa = &a.a[5],
pb = &b.a[5],
pc = &(a = b).a[5],
printf("%p %p %p\n", (void *) pa, (void *) pb, (void *) pc);
}
This version has no UB.
It's only not UB in the nazal demons sense.
It's UB in a sense that we can't predict values of expressions
like (pa==pc) and (pb==pc). ...
... I.e. pc is completely useless. In my book
it is form of UB.
On Tue 5/6/2025 2:35 AM, David Brown wrote:
N3096 6.2.4p2: "If a pointer value is used in an evaluation after
the object the pointer points to (or just past) reaches the end of
its lifetime, the behavior is undefined. The representation of a
pointer object becomes indeterminate when the object the pointer
points to (or just past) reaches the end of its lifetime."
It seems clear to me that "pc" has an indeterminate value after the
expression assigning, since it points to an object with temporary
lifetime.
And attempting to use the value of an object with automatic storage
while it has an indeterminate value is undefined behaviour.
Again, this makes no sense. Please, pay attention to the code and the corrections made after the initial version (e.g. usage of comma
operator). No, the value of `pc` is not indeterminate, and no, there's
no undefined behavior in the above version of the code.
As far as I can see, simply reading the value in "pc" to print it out
is UB according to the C standards. It is clearly going to be a
harmless operation on most hardware, but there are processors where
pointer registers are more complicated than simple linear addresses -
they can track some kind of segment structure describing the range of
a data block, or permissions for access to the data, and such
structures could have been deactivated or deallocated when the
temporary lifetime object died. Even attempting to read the value of
the pointer, without dereferencing it, would then cause some kind of
fault or trap.
Again, irrelevant. In the above code the temporary object does not die during the entire period when pointer `pc` is used in any way.
On Wed 5/7/2025 12:37 AM, David Brown wrote:
That would get an immediate downcheck during review for exactly
that reason.
Of course. In fact, if someone presented such code for review (and
assuming I noticed the commas!) I'd have to consider whether it was
done maliciously, intentionally deceptively, due to incompetence, or
smart- arse coding. In all my C coding experience, I can't recall
ever coming across a single situation when I thought the use of the
comma operator was appropriate in the kind of code I work with.
Wow! That's catastrophically bad.
As it has been stated many times before, both C and C++ are programming languages that embrace both statement-level and expression-level programming. Expression-level programming (e.g. where `?:` is used for branching and `,` for sequencing) is a very valuable and massively
important programming paradigm in these languages. The fact that
elaborate expression-level programming is not in nay way abandoned or shunned today is pretty obvious in C++, since C++ took major steps
lately to develop its expression-level capabilities. But it has always
been and will always remain important in C as well.
The proclivity to stick exclusively to statement-level programming in C
and, God forbid, impose it in others through so called "code reviews"... that would be a trait specific to "sweatshop" development outfits, which strive to replace quality with quantity. I'd agree that in a revolving
door employment environment relying on a large number of low-competence developers such code might be seen as "too confusing". But I don't see
why we should set our standards that low here, in `comp.lang.c`.
On Mon 5/5/2025 1:27 PM, Keith Thompson wrote:
Andrey Tarasevich <noone@noone.net> writes:
[...]
#include <stdio.h>I believe it does. pc points to an element of an object with
struct S { int a[10]; };
int main()
{
struct S a, b = { 0 };
int *pa, *pb, *pc;
pa = &a.a[5],
pb = &b.a[5],
pc = &(a = b).a[5],
printf("%p %p %p\n", (void *) pa, (void *) pb, (void *) pc);
}
This version has no UB.
temporary lifetime. The value of pc is then used after the object
it points to has reached the end of its lifetime. At that point,
pc has an indeterminate value.
Nope. Nowhere in this code the value of `pc` is used beyond the
lifetime of the object with temporary lifetime.
Pay attention to the fact that the last 4 lines in above code is a
single expression joined by a comma operator, which is the whole point
of the corrections that differentiate it from the original version.
On 29/05/2025 14:49, Andrey Tarasevich wrote:
On Wed 5/7/2025 12:37 AM, David Brown wrote:
That would get an immediate downcheck during review for exactly
that reason.
Of course. In fact, if someone presented such code for review (and
assuming I noticed the commas!) I'd have to consider whether it was
done maliciously, intentionally deceptively, due to incompetence, or
smart- arse coding. In all my C coding experience, I can't recall
ever coming across a single situation when I thought the use of the
comma operator was appropriate in the kind of code I work with.
Wow! That's catastrophically bad.
As it has been stated many times before, both C and C++ are programming
languages that embrace both statement-level and expression-level
programming. Expression-level programming (e.g. where `?:` is used for
branching and `,` for sequencing) is a very valuable and massively
important programming paradigm in these languages. The fact that
elaborate expression-level programming is not in nay way abandoned or
shunned today is pretty obvious in C++, since C++ took major steps
lately to develop its expression-level capabilities. But it has always
been and will always remain important in C as well.
No, expression-level programming has always been and will likely always >remain a very minor part of C programming. Yes, some people make use of
the comma operator. Some people do so extensively - and they are often,
but not necessarily, considered "smart-arse" programmers rather than
"smart" programmers. If the comma operator were removed from the C >language, I guess some 95% of programmers would barely notice - at
worst, they would have to add an extra line inside an occasional "for"
loop. (The tertiary operator is used much more.)
On Mon 5/5/2025 1:43 PM, Keith Thompson wrote:
What C90 constraint does it violate? Both gcc and clang reject it
with "-std=c90 -pedantic-errors", with an error message "ISO C90
forbids subscripting non-lvalue array", but I don't see a relevant
constraint in the C90 standard.
The "constraint" in C89/90 is simply the fact that C89/90 _requires_
an lvalue (of array type) in order to apply array to pointer
conversion. Here's is the original wording:
Except when it is the operand of the sizeof operator or the unary &
operator, or is a character string literal used to initialize an
array of character type, or is a wide string literal used to
initialize an array with element type compatible with wchar-t, an
*lvalue* that has type “array of type” is converted to an expression
that has type “pointer to rype” that points to the initial element
of the array object and is not an lvalue.
The presence of that "*lvalue*" requirement is what prevented up from
using `[]` operator on non-lvalue arrays in C89/90, because `[]`
critically relies on that conversion.
In C11 the wording has changed:
Except when it is the operand of the sizeof operator, the _Alignof
operator, or the unary & operator, or is a string literal used to
initialize an array, an expression that has type ‘‘array of type’’
is converted to an expression with type ‘‘pointer to type’’ that
points to the initial element of the array object and is not an
lvalue. If the array object has register storage class, the behavior
is undefined.
Note that the "lvalue" requirement has disappeared from this
wording. That is exactly why since C99 we can apply `[]` to non-lvalue arrays.
And sometimes, excessive use of the comma operator causes
compiler failures.
cfront generated the comma operator extensively, and expression trees
would grow to very large sizes. There was a bug in PCC (for the
88100) where it would run out of temporary registers while generating
code for some cfront generated comma expressions (which were -far- from
human readable). I had to fix the temporary register allocation
code in PCC to spill registers when the sethi-ullman number for an
expression exceeded the number of registers.
That was circa 1990, and I've generally not found any arguments
favoring their general use persuasive in the years since, including
Andrey's and Kaz's responses recently posted here.
The simple fact that experienced programmers that read this usenet
newsgroup missed the comma operators in the original example speaks
volumes.
David Brown <david.brown@hesbynett.no> writes:
On 29/05/2025 14:49, Andrey Tarasevich wrote:
On Wed 5/7/2025 12:37 AM, David Brown wrote:
That would get an immediate downcheck during review for exactly
that reason.
Of course. In fact, if someone presented such code for review (and
assuming I noticed the commas!) I'd have to consider whether it was
done maliciously, intentionally deceptively, due to incompetence, or
smart- arse coding. In all my C coding experience, I can't recall
ever coming across a single situation when I thought the use of the
comma operator was appropriate in the kind of code I work with.
Wow! That's catastrophically bad.
As it has been stated many times before, both C and C++ are programming
languages that embrace both statement-level and expression-level
programming. Expression-level programming (e.g. where `?:` is used for
branching and `,` for sequencing) is a very valuable and massively
important programming paradigm in these languages. The fact that
elaborate expression-level programming is not in nay way abandoned or
shunned today is pretty obvious in C++, since C++ took major steps
lately to develop its expression-level capabilities. But it has always
been and will always remain important in C as well.
No, expression-level programming has always been and will likely always
remain a very minor part of C programming. Yes, some people make use of
the comma operator. Some people do so extensively - and they are often,
but not necessarily, considered "smart-arse" programmers rather than
"smart" programmers. If the comma operator were removed from the C
language, I guess some 95% of programmers would barely notice - at
worst, they would have to add an extra line inside an occasional "for"
loop. (The tertiary operator is used much more.)
And sometimes, excessive use of the comma operator causes
compiler failures.
cfront generated the comma operator extensively, and expression trees
would grow to very large sizes. There was a bug in PCC (for the
88100) where it would run out of temporary registers while generating
code for some cfront generated comma expressions (which were -far- from
human readable). I had to fix the temporary register allocation
code in PCC to spill registers when the sethi-ullman number for an
expression exceeded the number of registers.
That was circa 1990, and I've generally not found any arguments
favoring their general use persuasive in the years since, including
Andrey's and Kaz's responses recently posted here.
The simple fact that experienced programmers that read this usenet
newsgroup missed the comma operators in the original example speaks
volumes.
scott@slp53.sl.home (Scott Lurndal) writes:
[...]
And sometimes, excessive use of the comma operator causes
compiler failures.
cfront generated the comma operator extensively, and expression trees
would grow to very large sizes. There was a bug in PCC (for the
88100) where it would run out of temporary registers while generating
code for some cfront generated comma expressions (which were -far- from
human readable). I had to fix the temporary register allocation
code in PCC to spill registers when the sethi-ullman number for an
expression exceeded the number of registers.
That was circa 1990, and I've generally not found any arguments
favoring their general use persuasive in the years since, including
Andrey's and Kaz's responses recently posted here.
So a compiler you used circa 1990 had problems with comma expressions.
That's hardly an argument against using comma operators today.
The simple fact that experienced programmers that read this usenet
newsgroup missed the comma operators in the original example speaks
volumes.
*That's* a valid argument.
On Tue 5/6/2025 10:36 AM, Waldek Hebisch wrote:
Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
Andrey Tarasevich <noone@noone.net> writes:
[...]
#include <stdio.h>
struct S { int a[10]; };
int main()
{
struct S a, b = { 0 };
int *pa, *pb, *pc;
pa = &a.a[5],
pb = &b.a[5],
pc = &(a = b).a[5],
printf("%p %p %p\n", (void *) pa, (void *) pb, (void *) pc);
}
This version has no UB.
I believe it does. pc points to an element of an object with
temporary lifetime. The value of pc is then used after the object
it points to has reached the end of its lifetime. At that point,
pc has an indeterminate value.
N3096 6.2.4p2: "If a pointer value is used in an evaluation after
the object the pointer points to (or just past) reaches the end of
its lifetime, the behavior is undefined. The representation of a
pointer object becomes indeterminate when the object the pointer
points to (or just past) reaches the end of its lifetime."
Note commas above. Assignment to pc and call to printf are parts
of a single expression, so use of pc is within lifetime of the
temporary object.
Exactly. I thought the nature of the corrections I made (i.e. the
deliberate usage of comma operator) would be strikingly obvious to the participants of the thread. But alas...
Sysop: | Tetrazocine |
---|---|
Location: | Melbourne, VIC, Australia |
Users: | 8 |
Nodes: | 8 (0 / 8) |
Uptime: | 102:24:11 |
Calls: | 161 |
Files: | 21,502 |
Messages: | 78,562 |