Tuesday, October 26, 2021

Digest for comp.lang.c++@googlegroups.com - 24 updates in 2 topics

Manfred <noname@add.invalid>: Oct 26 05:15PM +0200

On 10/25/2021 8:11 PM, Keith Thompson wrote:
 
> I don't see how the omission of "upon use of a nonportable or erroneous
> program construct or of erroneous data" in the C++ standard makes any
> real difference.
 
It depends on the reader: whether it is some sort of text processing
machine, a language lawyer or a human being.
 
 
> In all cases, "undefined behavior" is determined either by an explicit
> statement or by the omission of any definition of the behavior (or, in
> C, by violation of a "shall" outside a constraint).
 
For a human being, the additional sentence makes a difference, simply
because it is there: it means that the writer had a reason to write it,
and such writer's intent is part of the message that matters to a human
reader.
 
More specifically:
 
In C, the additional sentence actually poses a distinctive
characterization of the code that qualifies for undefined behavior, i.e.
"nonportable or erroneus" code, which is something else than saying
"anything that is not explicitly defined here". Now, the problem is that
the C definition actually redirects to a definition of "nonportable" and
(more relevantly) "erroneous", so a machine reader might trigger an
"undefined reference" error and stop parsing.
A language lawyer might interpret the wording as implying that anything
that is not explicitly defined is considered "nonportable" or
"erroneous", but that's an interpretation which is still to be proven to
hold in court, since the other party's lawyer might interpret the same
wording as UB being <<anything that is "nonportable" or "erroneous"
/and/ is not covered by this standard's requirements>>
 
Schematically, one might think of the entire set of possible source code
to be validated against the standard, and divide it into the subsets:
1) Explicitly defined valid code (e.g. syntax of declarations)
2) Explicitly defined invalid code (e.g. constraint violations)
3) Code which is not explicitly defined by 1) and 2)
 
Ideally, for a perfect standard, subset 3) would be empty - i.e. to make
a machine reader happy. In practice, standards are (luckily) written by
humans, so there may be something left in subset 3). To me, the
additional sentence is intended to give a rationale to discriminate
which is which in this area.
That said, lawyers may still complain that a non-empty subset 3) leads
to ambiguity, and this may be a serious problem in court.
 
Pre-C++11 C++ apparently tried to address this potential ambiguity by
adding "such as", thus suggesting that the category of "nonportable" or
"erroneous" code is meant to be an example of code for which the
standard poses "no requirements".
The wording may appear to suggest that subset 3) is meant to be included
in subset 2), but the authors didn't feel brave enough to say this
explicitly, and left the categories of "nonportable" and "erroneous" in.
The fact is that, obviously, placing subset 3) into subset 2) poses a
heavy burden on the standard itself.
 
C++11-and-later C++ was actually brave enough to assert that anything
that is not explicitly defined in the standard is "undefined behavior",
which would just be a self-identity assertion if it weren't for the note
that clarifies that UB is a very Bad Thing™, unless such behavior is
actually defined by the implementation.
 
The result is that the latest C++ definition of UB is so strong that it
suddenly raised the bar of the Standard's quality requirement by several
levels, thus opening the Pandora's box of countless examples of UB that
affect nowadays' C++, to the point of invalidating even sample code in
Bjarne's TC++PL that has been valid since the beginning of time, and led
to cryptic additions to the standard itself (std::launder, anyone?)
This might be seen as a process of improvement of the language, but so
far this seems controversial.
RacingRabbit@watershipdown.co.uk: Oct 26 03:21PM

On Tue, 26 Oct 2021 10:43:09 -0400
>> return 0;
>> }
 
>You shouldn't get a bus error this time. Do you understand why?
 
Noooo! Really??
 
Have you actually read anything I wrote or are you just jumping on the
bandwagon of what others have said in order to try and sound clever?
James Kuyper <jameskuyper@alumni.caltech.edu>: Oct 26 11:22AM -0400

>> with read-only memory. Within the scope of an identifier that identifies
 
> No I'm not. The pointer will be pointing to a string literal in the program
> static text area which is usually non modifiable.
 
Yes, I discussed that fact, which is the real reason for the bus error
you saw. It was not because you used * rather than [] in your
declaration of str.
 
>> Exception 1: it's not permitted to declare functions that take arrays as
>> arguments,
 
> Since when?
 
Since K&R C. As explained below, the following is NOT a counter example.
 
 
> void func(int a[2][3])
> {
> printf("%d\n",a[1][2]);
 
"A declaration of a parameter as "array of type" shall be adjusted to
"qualified pointer to type", where the type qualifiers (if any) are
those specified within the [ and ] of the array type derivation."
(6.7.63.p6).
There's an easy way you can test this. Declare func a second time, as
follows:
 
void func(int (*a)[3]);
 
Such redeclaration is permitted only if the new declaration is
compatible with the previous one. The "adjustment" described above makes
your declaration identical to the second one.
 
 
> int a[2][3];
> a[1][2] = 123;
> func(a);
 
Appearances to the contrary notwithstanding, that code does NOT pass the
entire array a to func(). That's because:
 
"Except when it is the operand of the sizeof 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." (6.3.2p3).
 
In the expression func(a), "a" is not the operand of the sizeof operator
or the unary & operator, and it is certainly not a string literal.
Therefore, a gets converted to &a[0]. Try it, add the following line to
your program:
 
func(&a[0]);
 
It won't be diagnosed as an error, because &a[0] is a pointer to an
array of 3 ints, which precisely what the first argument of func() has
been declared to be (after the adjustments described above). It will
simply result in a second printing of "123".
 
 
> }
> fenris$ cc t.c; a.out
> 123
 
Neither of these features are new, they both date back to K&R C.
RacingRabbit@watershipdown.co.uk: Oct 26 03:26PM

On Tue, 26 Oct 2021 15:55:38 +0100
 
>I actually listed the 7 compilers I tried it on, just at the point where
>you must have stopped reading.
 
>Oh, you mean you only tried it on one implementation?
 
Sorry, I have limited tolerance for smart asses so yes, I stopped reading.
They're all toy compilers apart from VC and I specifically was talking about
*nix and yes, these days that means gcc or clang.
James Kuyper <jameskuyper@alumni.caltech.edu>: Oct 26 11:27AM -0400


>> This shows ABC the first time it's executed. The second time it shows
>> ZBC; the code has changed the string literal! Where the same literal iS
 
> I suggest you actually try running that code and see what happens.
 
The behavior of the code shown is undefined, and therefore very well
might be exactly as he described - you would need to know precisely
which compiler he used, on which platform, with which compiler options.
That is in fact common behavior for such code. He claims that he did
test it, and I know of no reason to disbelieve him.
RacingRabbit@watershipdown.co.uk: Oct 26 03:30PM

On Tue, 26 Oct 2021 11:22:18 -0400
 
>Yes, I discussed that fact, which is the real reason for the bus error
>you saw. It was not because you used * rather than [] in your
>declaration of str.
 
Hello, we speak English on this group. Do. You. Understand. It?
 
>>> arguments,
 
>> Since when?
 
>Since K&R C. As explained below, the following is NOT a counter example.
 
Thats exactly what it is. Too bad it made you look stupid.
 
>There's an easy way you can test this. Declare func a second time, as
>follows:
 
>void func(int (*a)[3]);
 
[hopeless effort at self justification]
 
You said arrays couldn't be declared as parameters. I showed you they could,
end of. What the compiler does with it under the hood is irrelevant.
 
tl;dr
James Kuyper <jameskuyper@alumni.caltech.edu>: Oct 26 11:52AM -0400

> On Tue, 26 Oct 2021 10:42:24 -0400
> James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
...
>> modifiable, but that's not just because of the "*", it's because str has
>> been initialized to point at the first character of a string literal. It
 
> So you disagree with what I said then say exactly the same thing yourself.
 
You said that "[] means modifyable, * means read only in every
C implementation I've ever used."
 
That is false.
 
What I said corresponds to the following examples:
 
char array1[] = "modifiable";
const char array2[] = "optionally read only";
char *pointer1 = array1;
const char *pointer2 = array1;
 
array1[0] = 'u'; // permitted
array2[0] = 'u'; // constraint violation
*pointer1 = 'u'; // permitted
*pointer2 = 'u'; // constraint violation
 
pointer1 = "optionally read only"; // permitted
*pointer1 = 'u'; // undefined behavior
pointer2 = array2;
*pointer2 = 'u'; // Still a constraint violation.
 
Despite both of them being declared with [], and therefore according to
you both being modifiable, array1 is modifiable, while array2 may be
placed in read-only memory - but it doesn't have to be.
 
Despite being declared with *, and therefore according to you being
read-only, pointer1 points at modifiable memory the first time it is
dereferenced, and points at memory that could be read-only the second
time it is dereferenced.
Despite being declared with *, pointer2 differs from pointer 1 in that
it is always a constraint violation to write through it, regardless of
whether or not it points at read-only memory.
Despite being both declared with *, and therefore according to you being
read-only, pointer1 and pointer2 are themselves modifiable, as shown by
the fact that I changed both of their values.
James Kuyper <jameskuyper@alumni.caltech.edu>: Oct 26 12:23PM -0400

> On Tue, 26 Oct 2021 11:22:18 -0400
> James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
...
>> There's an easy way you can test this. Declare func a second time, as
>> follows:
 
>> void func(int (*a)[3]);
 
Did you try inserting such a line? What were the results?
 
> [hopeless effort at self justification]
 
> You said arrays couldn't be declared as parameters.
 
No, I said "it's not permitted to declare functions that take arrays as
arguments, but it is permitted to declare a function parameter as if it
were an array."
 
Such a declaration does NOT declare the parameter to be an array, it
declares it to be a pointer. You've retained my citation of the part of
the standard that says so above. Here's a complete compilable program
demonstrating that rule:
 
#include <stdio.h>
 
static void func(int a[2][3])
{
printf("%s\n",
_Generic(a, int[2][3]: "array", int(*)[3]: "pointer"));
}
 
int main(void)
{
int a[2][3];
func(a);
}
 
When I run that program, it says "pointer". What do you get when you run it?
Keith Thompson <Keith.S.Thompson+u@gmail.com>: Oct 26 09:49AM -0700

> On 26/10/2021 14:29, Ben Bacarisse wrote:
>> Bart <bc@freeuk.com> writes:
[...]
>> permitted in C++.
 
> I tried it in C++ before posting (as I'd thought that "ABC" would have
> type const char*) but it seemed to work. (Using -Wall -std=c++14.)
 
And you didn't bother to mention the diagnostic? I get
warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]
And of course with "-pedantic-errors" it becomes a fatal error.
 
Did you not get a diagnostic? It's not all that interesting to see what
you can get away with by ignoring warnings.
 
> I'm writing about what is typically observed.
 
What I typically observe is that skilled programmers pay attention to
warnings and do not attempt to modify string literals.
 
> got round to it yet.
 
> It is surprising that a big compiler like MSVC doesn't do so either,
> but apparently that's only done when optimising; rather odd.)
 
My quick experiment with MSVC 2017 does not confirm that.
char *s = "ABC";
gives a fatal error in C++. It compiles in C (as expected), but
attempting to modify the literal causes a run-time crash.
 
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for Philips
void Void(void) { Void(); } /* The recursive call of the void */
James Kuyper <jameskuyper@alumni.caltech.edu>: Oct 26 12:51PM -0400

On 10/26/21 12:23 PM, James Kuyper wrote:
...
> {
> printf("%s\n",
> _Generic(a, int[2][3]: "array", int(*)[3]: "pointer"));]]][
 
Here's another test you can perform. If that declaration declares a to
be an array, the following assignment statement, inserted in the body of
func(), should be a constraint violation:
 
int b[4][3];
a = b;
 
The left side of an assignment cannot have array type. On my system,
those lines compiled without generating any diagnostics.
 
Keith Thompson <Keith.S.Thompson+u@gmail.com>: Oct 26 10:22AM -0700

>>abysmal understanding of C, while believing you understand it better
>>than others.
 
> Says the preening fool.
 
James is not a "preening fool". He's right.
 
You have joined a forum many of whose participants are experts on the C
programming language. You have made a number of incorrect statements
about C, and you have shown an inappropriately condescending attitude
while doing so.
 
This is a great place to learn about C, and I sincerely hope you'll take
advantage of the opportunity. My advice is to express less certainty
about the statements you make, engage in discussion, and stop insulting
people.
 
>>modifiable, but that's not just because of the "*", it's because str has
>>been initialized to point at the first character of a string literal. It
 
> So you disagree with what I said then say exactly the same thing yourself.
 
No, that's not what he did. The array is read-only because it's a
string literal. You didn't say that.
 
 
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for Philips
void Void(void) { Void(); } /* The recursive call of the void */
Keith Thompson <Keith.S.Thompson+u@gmail.com>: Oct 26 10:27AM -0700

Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
[...]
> advantage of the opportunity. My advice is to express less certainty
> about the statements you make, engage in discussion, and stop insulting
> people.
 
Sorry, I didn't notice which newsgroup I was in. C++, not C. (The
rest of what I wrote stands.)
 
[...]
 
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for Philips
void Void(void) { Void(); } /* The recursive call of the void */
Bart <bc@freeuk.com>: Oct 26 06:38PM +0100

On 26/10/2021 17:49, Keith Thompson wrote:
> And of course with "-pedantic-errors" it becomes a fatal error.
 
> Did you not get a diagnostic? It's not all that interesting to see what
> you can get away with by ignoring warnings.
 
I used rextester.com, which uses the default options I used. There were
no diagnostics.
 
Maybe I could have tested half a dozen more C++ compilers, but they're
thin on the ground on my machine.
 
 
>> I'm writing about what is typically observed.
 
> What I typically observe is that skilled programmers pay attention to
> warnings and do not attempt to modify string literals.
 
In general a compiler is not able to warn about the latter, and a
programmer may not know that a pointer passed via a function for example
points into readonly memory. Or out-of-bounds memory. Or contains NULL
or other invalid memory address.
 
It is however useful to know what might TYPICALLY happen if you do try
and write into a string literal.
James Kuyper <jameskuyper@alumni.caltech.edu>: Oct 26 02:06PM -0400

On 10/26/21 1:27 PM, Keith Thompson wrote:
>> people.
 
> Sorry, I didn't notice which newsgroup I was in. C++, not C. (The
> rest of what I wrote stands.)
 
Even though this is comp.std.c++, and the original message was about
C++, this sub-thread has turned into a discussion about C. However,
everything we're saying about C is true of C++ as well (with some minor
subtle differences), and everything he's saying incorrectly about C is
incorrect for C++, too. The biggest difference in C++ would be that
_Generic() is not supported, but typeinfo() is, which would actually be
a more convenient of proving the truth of what we're saying.
Keith Thompson <Keith.S.Thompson+u@gmail.com>: Oct 26 11:20AM -0700

>> you can get away with by ignoring warnings.
 
> I used rextester.com, which uses the default options I used. There
> were no diagnostics.
 
Yes, there were. rextester.com didn't show them to you because you
didn't enable the "Show compiler warnings" checkbox.
 
> Maybe I could have tested half a dozen more C++ compilers, but they're
> thin on the ground on my machine.
 
>>> I'm writing about what is typically observed.
 
Any C++ compiler that does not issue a diagnostic for
 
char* s = "ABC";
 
is non-conforming. I'm skeptical that there are very many C++ compilers
out there that fail to do this when invoked properly. (C does not
require a diagnostic, which makes it important to remember to add
"const".)
 
> programmer may not know that a pointer passed via a function for
> example points into readonly memory. Or out-of-bounds memory. Or
> contains NULL or other invalid memory address.
 
In C++, it's difficult to even attempt to modify a string literal
without either triggering a required diagnostic or explicitly doing
something to override const. (It's easier in C, unfortunately.)
 
> It is however useful to know what might TYPICALLY happen if you do try
> and write into a string literal.
 
Agreed. What typically happens in C++ is that you'll get a diagnostic
before you even try to modify a string literal, since C++ string
literals are const. What typically happens in C is that the program
crashes. There are C compilers that don't put string literals in
read-only memory (I've confirmed that tcc doesn't), but as far as I
known none of them are in widespread use.
 
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for Philips
void Void(void) { Void(); } /* The recursive call of the void */
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Oct 26 11:20AM -0700

On 10/25/2021 12:45 PM, Chris M. Thomasson wrote:
> self, I will get a nice warning. Its basically a habit of mine. 'self'
> is akin to the this pointer in C++.
 
> Oh well... ;^)
 
See the deliberate mistake in here? Besides the foo_compute function
returning int typo, argh!... Anyway, here is a program:
______________________________
#include <stdio.h>
 
 
struct foo
{
unsigned int a;
};
 
 
void
foo_init(
struct foo* const self,
unsigned int a
){
self->a = a;
}
 
 
unsigned int
foo_compute(
struct foo const* const self,
unsigned int a
){
return self->a *= a + 123;
}
 
int main()
{
struct foo foo;
 
foo_init(&foo, 42);
 
unsigned int foobar = foo_compute(&foo, 42);
 
printf("foobar = %u\n", foobar);
 
return 0;
}
______________________________
 
Does not compile... GOOD! Change foo_compute to:
 
______________________________
unsigned int
foo_compute(
struct foo* const self,
unsigned int a
){
return self->a *= a + 123;
}
______________________________
 
and it does compile! So, const has it's uses, indeed.
 
;^)
James Kuyper <jameskuyper@alumni.caltech.edu>: Oct 26 02:34PM -0400

On 10/26/21 12:23 PM, James Kuyper wrote:
...
> func(a);
> }
 
> When I run that program, it says "pointer". What do you get when you run it?
 
Unfortunately, that doesn't prove what I intended it to prove, because
even if a were an array, it would have been converted to a pointer when
passed to _Generic. That problem is not too difficult to work around, I
just have to use the '&' operator:
 
#include <stdio.h>
 
#define ARR_PTR(x) _Generic(&x, \
int(*)[2][3]: "array", \
int(**)[3]: "pointer", \
default: "other")
 
static void func(int a[2][3])
{
int b[2][3];
int (*c)[3];
printf("a:%s\n", ARR_PTR(a));
printf("b:%s\n", ARR_PTR(b));
printf("c:%s\n", ARR_PTR(c));
}
 
int main(void)
{
int a[2][3];
func(a);
}
 
I get the following output:
a:pointer
b:array
c:pointer
 
What do you get?
Bart <bc@freeuk.com>: Oct 26 08:32PM +0100

On 26/10/2021 19:20, Keith Thompson wrote:
>> were no diagnostics.
 
> Yes, there were. rextester.com didn't show them to you because you
> didn't enable the "Show compiler warnings" checkbox.
 
How about that? A professional-looking site, which, by default, enables
warnings for all those compilers and at the same time, by default,
chooses to hide those warnings!
Bart <bc@freeuk.com>: Oct 26 08:35PM +0100


> Sorry, I have limited tolerance for smart asses
 
Funny, that, so do I! So I'll leave you to it.
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Oct 26 12:57PM -0700

On 10/26/2021 11:34 AM, James Kuyper wrote:
> b:array
> c:pointer
 
> What do you get?
 
Fwiw, I get the same:
 
a:pointer
b:array
c:pointer
 
:^)
David Brown <david.brown@hesbynett.no>: Oct 26 10:18PM +0200

On 26/10/2021 21:32, Bart wrote:
 
> How about that? A professional-looking site, which, by default, enables
> warnings for all those compilers and at the same time, by default,
> chooses to hide those warnings!
 
When it comes to websites, "professional-looking" is not a good
indication of quality. I can't say much about that website, as I have
no experience with it, but any professional programmer who doesn't
enable warnings and pay attention to them should be looking for another
career. (Of course the exact choice of warnings, and appropriate ways
to handle them can vary by programmer style, project, and other factors.
Hiding them all, however, is never appropriate.)
 
I recommend <https://gotbolt.org> as having a wide selection of
compilers, and showing generated code in a helpful format. (I haven't
made a survey of alternatives and would be happy to hear of comparisons
if someone has a suggestion that is better than godbolt.)
David Brown <david.brown@hesbynett.no>: Oct 26 10:32PM +0200


> Noooo! Really??
 
> Have you actually read anything I wrote or are you just jumping on the
> bandwagon of what others have said in order to try and sound clever?
 
Rabbit, I believe you are missing a few key points here. James is not
trying to /sound/ clever - he /is/ clever. He is one of the top people
in this group in terms of his knowledge and experience of C and C++, his
accuracy in his explanations, and his patience in helping people.
(There are others here with a similar level of respect and reputation,
whom you have also insulted and disregarded.)
 
A second key point is that you are wrong about almost everything you
have been writing in this group - so wrong, that you don't even
understand the question.
 
You'd do well to stop being such an annoying little brat and listen to
the people who are spending time and effort trying to help you
understand the language a little better.
 
(And yes, I know you'll respond to this with insults - I'm old enough
not to be bothered about what some silly teenager thinks of me. But I
am also naïve enough to think that not even you are beyond hope.)
Keith Thompson <Keith.S.Thompson+u@gmail.com>: Oct 25 11:11AM -0700

> program construct or of erroneous data" actually relegates the
> language at the mercy of language lawyers, and led to the UB bloat
> that affects C++ nowadays.
[...]
 
I don't see how the omission of "upon use of a nonportable or erroneous
program construct or of erroneous data" in the C++ standard makes any
real difference.
 
C definition, all standard editions:
behavior, upon use of a nonportable or erroneous program construct
or of erroneous data, for which this International Standard imposes
no requirements
 
C++ definition, before C++11:
behavior, such as might arise upon use of an erroneous program
construct or erroneous data, for which this International Standard
imposes no requirement
 
C++ definition, C++11 and later:
behavior for which this International Standard imposes no requirements
 
In all cases, "undefined behavior" is determined either by an explicit
statement or by the omission of any definition of the behavior (or, in
C, by violation of a "shall" outside a constraint).
 
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for Philips
void Void(void) { Void(); } /* The recursive call of the void */
Lynn McGuire <lynnmcguire5@gmail.com>: Oct 26 03:55PM -0500

"C++ Smart Pointers and Arrays" by Bartlomiej Filipek
https://www.cppstories.com/2021/smartptr-array/
 
"Smart pointers are very versatile and can hold pointers not only to
single instances but also to arrays. Is that only a theoretical use
case? or maybe they might be handy in some cases? Let's have a look."
 
Lynn
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page.
To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com.

No comments: