• I think references should have been const by default

    From Juha Nieminen@21:1/5 to All on Thu Oct 21 05:23:03 2021
    Time and again I see beginner C++ programmers make the same mistake:
    Make functions take objects by non-const reference, even when (in the
    vast, vast majority of cases) the function doesn't modify those objects.

    In one particularly egregious case some beginner programmer had written
    a comparator lambda that took two std::string objects by reference,
    wondered why he was getting a compiler error, and then removed the
    references, so it was taking the objects by value. That way it compiled.
    Then he wondered why it was so slow.

    I think it was a mistake to make references non-const by default.
    It's logical (and consistent with pointer syntax) of course, but I think
    it was a mistake. In the vast, vast majority of cases if you create a
    reference to something else, you want it to be const (and there are
    many good reasons why it should be const). It's extremely rare to
    explicitly want a non-const reference.

    And the thing is, C++98 has a *perfect* keyword to explicitly denote
    that you want a non-const reference, which could have been used for
    this purpose, so no new keyword would be needed for this: 'mutable'.

    In other words, I think C++ would have been better if it worked like
    this:

    void foo1(std::string& str)
    {
    str = "hello"; // error: 'str' is const
    }

    void foo2(mutable std::string& str)
    {
    str = "hello"; // ok
    }

    (You could still write "const std::string&", but it would just have
    the exact same meaning as "std::string&", ie. in this case the
    'const' is superfluous, a bit like how 'signed' is superfluous
    in "signed int".)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Juha Nieminen on Thu Oct 21 08:54:27 2021
    On 21/10/2021 07:23, Juha Nieminen wrote:
    Time and again I see beginner C++ programmers make the same mistake:
    Make functions take objects by non-const reference, even when (in the
    vast, vast majority of cases) the function doesn't modify those objects.

    In one particularly egregious case some beginner programmer had written
    a comparator lambda that took two std::string objects by reference,
    wondered why he was getting a compiler error, and then removed the references, so it was taking the objects by value. That way it compiled.
    Then he wondered why it was so slow.

    I think it was a mistake to make references non-const by default.
    It's logical (and consistent with pointer syntax) of course, but I think
    it was a mistake. In the vast, vast majority of cases if you create a reference to something else, you want it to be const (and there are
    many good reasons why it should be const). It's extremely rare to
    explicitly want a non-const reference.

    And the thing is, C++98 has a *perfect* keyword to explicitly denote
    that you want a non-const reference, which could have been used for
    this purpose, so no new keyword would be needed for this: 'mutable'.

    In other words, I think C++ would have been better if it worked like
    this:

    void foo1(std::string& str)
    {
    str = "hello"; // error: 'str' is const
    }

    void foo2(mutable std::string& str)
    {
    str = "hello"; // ok
    }

    (You could still write "const std::string&", but it would just have
    the exact same meaning as "std::string&", ie. in this case the
    'const' is superfluous, a bit like how 'signed' is superfluous
    in "signed int".)


    I agree with you entirely. But if we are going for wishful thinking
    about how C++ could have been made better, I'd have preferred "const"
    for all variables and required "mutable" to declare a variable that
    could be modified. (Of course that would mean you'd need something else
    for class members that are today "mutable". But I think it's a lot more
    common for people to make variables that could have been "const" but
    aren't, than to use mutable members.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From RacingRabbit@watershipdown.co.uk@21:1/5 to Juha Nieminen on Thu Oct 21 09:12:29 2021
    On Thu, 21 Oct 2021 05:23:03 -0000 (UTC)
    Juha Nieminen <nospam@thanks.invalid> wrote:
    Time and again I see beginner C++ programmers make the same mistake:
    Make functions take objects by non-const reference, even when (in the
    vast, vast majority of cases) the function doesn't modify those objects.

    In one particularly egregious case some beginner programmer had written
    a comparator lambda that took two std::string objects by reference,
    wondered why he was getting a compiler error, and then removed the >references, so it was taking the objects by value. That way it compiled.
    Then he wondered why it was so slow.

    I think it was a mistake to make references non-const by default.
    It's logical (and consistent with pointer syntax) of course, but I think
    it was a mistake. In the vast, vast majority of cases if you create a >reference to something else, you want it to be const (and there are
    many good reasons why it should be const). It's extremely rare to
    explicitly want a non-const reference.

    And the thing is, C++98 has a *perfect* keyword to explicitly denote
    that you want a non-const reference, which could have been used for
    this purpose, so no new keyword would be needed for this: 'mutable'.

    In other words, I think C++ would have been better if it worked like
    this:

    void foo1(std::string& str)
    {
    str = "hello"; // error: 'str' is const
    }

    void foo2(mutable std::string& str)
    {
    str = "hello"; // ok
    }

    (You could still write "const std::string&", but it would just have
    the exact same meaning as "std::string&", ie. in this case the
    'const' is superfluous, a bit like how 'signed' is superfluous
    in "signed int".)

    Looking for logic in C++ keywords is a hiding to nothing. Eg const_cast actually means remove const'ness , not add it which is frankly bizarre.
    You might as well have true mean false and false mean true. Similarly
    putting throw() at the end of a function def means it can't throw! Though at least that insanity has been superceeded by noexcept now.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to RacingRabbit@watershipdown.co.uk on Thu Oct 21 09:52:53 2021
    RacingRabbit@watershipdown.co.uk wrote:
    Looking for logic in C++ keywords is a hiding to nothing. Eg const_cast actually means remove const'ness , not add it which is frankly bizarre.

    What would have been a better name for that keyword in your opinion?

    Similarly
    putting throw() at the end of a function def means it can't throw! Though at least that insanity has been superceeded by noexcept now.

    I think it's pretty logical. It just lists all the exceptions that the
    function can throw. If the list is empty, it means it doesn't throw
    any exception.

    If they wanted to be clearer, it should have been "throws(whatever)", but
    that would have required adding yet another single-use keyword.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paavo Helde@21:1/5 to All on Thu Oct 21 12:18:38 2021
    21.10.2021 08:23 Juha Nieminen kirjutas:

    I think it was a mistake to make references non-const by default.
    It's logical (and consistent with pointer syntax) of course, but I think
    it was a mistake.
    Agreed.

    By default in C is to pass objects by value, so the changes in the
    object will not affect the caller side. On the caller side the pass by
    value and pass by reference look the same, so this important feature (no surprises on the caller side) was lost in C++. Using a const reference
    fixes this loss, so one can even argue that using a const reference, at
    least by default would also be logical and consistent, in some sense.

    If it were up to me, I would have required extra syntax on the caller
    side to pass via non-const reference, like @var.

    Pushing everything more const would mean the language would behave and
    feel more "functional" and less "OOP", but that's a good thing.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paavo Helde@21:1/5 to All on Thu Oct 21 12:55:59 2021
    21.10.2021 12:48 Juha Nieminen kirjutas:
    David Brown <david.brown@hesbynett.no> wrote:
    I agree with you entirely. But if we are going for wishful thinking
    about how C++ could have been made better, I'd have preferred "const"
    for all variables and required "mutable" to declare a variable that
    could be modified. (Of course that would mean you'd need something else
    for class members that are today "mutable". But I think it's a lot more
    common for people to make variables that could have been "const" but
    aren't, than to use mutable members.)

    That would quite quickly turn quite annoying, eg. with things like
    for-loops:

    for(int i = 0; i < 10; ++i) // error, i is const


    Solved by C++20:

    for (int i : std::ranges::iota(0, 10))

    Having const default would mean additional bonus in that 'i' would be
    const inside the loop body.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to David Brown on Thu Oct 21 09:48:15 2021
    David Brown <david.brown@hesbynett.no> wrote:
    I agree with you entirely. But if we are going for wishful thinking
    about how C++ could have been made better, I'd have preferred "const"
    for all variables and required "mutable" to declare a variable that
    could be modified. (Of course that would mean you'd need something else
    for class members that are today "mutable". But I think it's a lot more common for people to make variables that could have been "const" but
    aren't, than to use mutable members.)

    That would quite quickly turn quite annoying, eg. with things like
    for-loops:

    for(int i = 0; i < 10; ++i) // error, i is const

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Juha Nieminen on Thu Oct 21 12:57:05 2021
    On 21/10/2021 11:48, Juha Nieminen wrote:
    David Brown <david.brown@hesbynett.no> wrote:
    I agree with you entirely. But if we are going for wishful thinking
    about how C++ could have been made better, I'd have preferred "const"
    for all variables and required "mutable" to declare a variable that
    could be modified. (Of course that would mean you'd need something else
    for class members that are today "mutable". But I think it's a lot more
    common for people to make variables that could have been "const" but
    aren't, than to use mutable members.)

    That would quite quickly turn quite annoying, eg. with things like
    for-loops:

    for(int i = 0; i < 10; ++i) // error, i is const


    for (mutable int i = 0; i < 10; ++i)

    Yes, there are many places where you want variable variables - but I
    think overall you typically have more that could be declared const than
    have to be variable. (The idea is not without precedence - there are
    other modern languages with constant objects by default.)

    For loops, I think the best solution would be a mixture - the loop
    variable should be mutable within the controlling expressions, but
    should be constant within the statement or block - as though you had
    written:

    for (int i = 0; i < 10; i++) {
    const auto i_ = i;
    const auto i = i_;

    // Here, "i" is constant
    }

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From RacingRabbit@watershipdown.co.uk@21:1/5 to Juha Nieminen on Thu Oct 21 10:36:14 2021
    On Thu, 21 Oct 2021 09:52:53 -0000 (UTC)
    Juha Nieminen <nospam@thanks.invalid> wrote:
    RacingRabbit@watershipdown.co.uk wrote:
    Looking for logic in C++ keywords is a hiding to nothing. Eg const_cast
    actually means remove const'ness , not add it which is frankly bizarre.

    What would have been a better name for that keyword in your opinion?

    unconst_cast , noconst_cast, take your pick.


    Similarly
    putting throw() at the end of a function def means it can't throw! Though at >> least that insanity has been superceeded by noexcept now.

    I think it's pretty logical. It just lists all the exceptions that the >function can throw. If the list is empty, it means it doesn't throw
    any exception.

    Except throw inside a function means throw any exception currently on the stack.
    You can't have it both ways and for once the C++ committee saw sense and replaced it with noexcept.

    If they wanted to be clearer, it should have been "throws(whatever)", but >that would have required adding yet another single-use keyword.

    There's nothing wrong with single use keywords if they make things clearer. This should have been done with the = 0 in pure virtuals and the spurious int in postfix operator definitions.

    eg:
    virtual void myfunc() = 0; -> pure virtual void myfunc();
    myclass &operator++(int) -> myclass &operator++() postfix

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Thu Oct 21 13:13:59 2021
    You're compulsive.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Manfred@21:1/5 to Juha Nieminen on Thu Oct 21 13:57:45 2021
    On 10/21/2021 7:23 AM, Juha Nieminen wrote:
    Time and again I see beginner C++ programmers make the same mistake:
    Make functions take objects by non-const reference, even when (in the
    vast, vast majority of cases) the function doesn't modify those objects.

    In one particularly egregious case some beginner programmer had written
    a comparator lambda that took two std::string objects by reference,
    wondered why he was getting a compiler error, and then removed the references, so it was taking the objects by value. That way it compiled.
    Then he wondered why it was so slow.

    I think it was a mistake to make references non-const by default.
    It's logical (and consistent with pointer syntax) of course, but I think
    it was a mistake. In the vast, vast majority of cases if you create a reference to something else, you want it to be const (and there are
    many good reasons why it should be const). It's extremely rare to
    explicitly want a non-const reference.

    And the thing is, C++98 has a *perfect* keyword to explicitly denote
    that you want a non-const reference, which could have been used for
    this purpose, so no new keyword would be needed for this: 'mutable'.

    In other words, I think C++ would have been better if it worked like
    this:

    void foo1(std::string& str)
    {
    str = "hello"; // error: 'str' is const
    }

    void foo2(mutable std::string& str)
    {
    str = "hello"; // ok
    }

    (You could still write "const std::string&", but it would just have
    the exact same meaning as "std::string&", ie. in this case the
    'const' is superfluous, a bit like how 'signed' is superfluous
    in "signed int".)


    Good point. However, I value syntax consistency a lot, so given the
    current state of things (non-const default pointers) I think non-const
    default references are preferable to me - and I write /many/ more 'const
    T&' declarations than 'T&' myself.
    In an ideal world, if we were able to have const default pointers as
    well, then I would endorse your view entirely.

    BTW Alf's alternative has its appeal.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Alf P. Steinbach@21:1/5 to Juha Nieminen on Thu Oct 21 13:22:42 2021
    On 21 Oct 2021 07:23, Juha Nieminen wrote:
    Time and again I see beginner C++ programmers make the same mistake:
    Make functions take objects by non-const reference, even when (in the
    vast, vast majority of cases) the function doesn't modify those objects.

    In one particularly egregious case some beginner programmer had written
    a comparator lambda that took two std::string objects by reference,
    wondered why he was getting a compiler error, and then removed the references, so it was taking the objects by value. That way it compiled.
    Then he wondered why it was so slow.

    I think it was a mistake to make references non-const by default.
    It's logical (and consistent with pointer syntax) of course, but I think
    it was a mistake. In the vast, vast majority of cases if you create a reference to something else, you want it to be const (and there are
    many good reasons why it should be const). It's extremely rare to
    explicitly want a non-const reference.

    Agreed. So what can you do about it?

    Just define

    template< class T > using Ref_ = const T&;

    And then write

    void foo( Ref_<Baluba> x )

    instead of

    void foo( const Baluba& x )

    A more clever definition in terms of a nested type of a class template,
    like the standard library usually does, does not support type deduction
    for function templates, but the above supports e.g.

    template< class T >
    void foo( Ref_<T> x )

    with calls like `foo( something )`.

    When the same approach is adopted for pointers it supports general
    left/west `const`.

    The one big drawback of this approach is that code using such
    definitions can't be posted to this group without Mr. Fibble complaining
    loudly and posting offensive remarks about how it's impossible to grok.


    And the thing is, C++98 has a *perfect* keyword to explicitly denote
    that you want a non-const reference, which could have been used for
    this purpose, so no new keyword would be needed for this: 'mutable'.

    In other words, I think C++ would have been better if it worked like
    this:

    void foo1(std::string& str)
    {
    str = "hello"; // error: 'str' is const
    }

    void foo2(mutable std::string& str)
    {
    str = "hello"; // ok
    }

    (You could still write "const std::string&", but it would just have
    the exact same meaning as "std::string&", ie. in this case the
    'const' is superfluous, a bit like how 'signed' is superfluous
    in "signed int".)

    - Alf

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bo Persson@21:1/5 to Bonita Montero on Thu Oct 21 15:08:59 2021
    On 2021-10-21 at 13:13, Bonita Montero wrote:
    You're compulsive.

    Who, me?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to RacingRabbit@watershipdown.co.uk on Thu Oct 21 15:04:19 2021
    On 21/10/2021 14:40, RacingRabbit@watershipdown.co.uk wrote:
    On Thu, 21 Oct 2021 12:57:05 +0200
    David Brown <david.brown@hesbynett.no> wrote:
    On 21/10/2021 11:48, Juha Nieminen wrote:
    David Brown <david.brown@hesbynett.no> wrote:
    I agree with you entirely. But if we are going for wishful thinking
    about how C++ could have been made better, I'd have preferred "const"
    for all variables and required "mutable" to declare a variable that
    could be modified. (Of course that would mean you'd need something else >>>> for class members that are today "mutable". But I think it's a lot more >>>> common for people to make variables that could have been "const" but
    aren't, than to use mutable members.)

    That would quite quickly turn quite annoying, eg. with things like
    for-loops:

    for(int i = 0; i < 10; ++i) // error, i is const


    for (mutable int i = 0; i < 10; ++i)

    Yes, there are many places where you want variable variables - but I
    think overall you typically have more that could be declared const than
    have to be variable. (The idea is not without precedence - there are
    other modern languages with constant objects by default.)

    For loops, I think the best solution would be a mixture - the loop
    variable should be mutable within the controlling expressions, but
    should be constant within the statement or block - as though you had
    written:

    I think const is a lot of fuss about nothing frankly. I barely use them anyway and I can't remember the last time that I had a bug due to updating
    a variable that shouldn't have been updated. For those who think its simply an indication to other devs that a variable won't be changed then fair enough but personally I find a lot of const/non const compilation errors related
    to functions a PITA.


    I don't about C++, but in C, you can take a program, remove all the
    'const' qualifiers, and it will still compile and work.

    Makes you think...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From RacingRabbit@watershipdown.co.uk@21:1/5 to David Brown on Thu Oct 21 13:40:34 2021
    On Thu, 21 Oct 2021 12:57:05 +0200
    David Brown <david.brown@hesbynett.no> wrote:
    On 21/10/2021 11:48, Juha Nieminen wrote:
    David Brown <david.brown@hesbynett.no> wrote:
    I agree with you entirely. But if we are going for wishful thinking
    about how C++ could have been made better, I'd have preferred "const"
    for all variables and required "mutable" to declare a variable that
    could be modified. (Of course that would mean you'd need something else >>> for class members that are today "mutable". But I think it's a lot more >>> common for people to make variables that could have been "const" but
    aren't, than to use mutable members.)

    That would quite quickly turn quite annoying, eg. with things like
    for-loops:

    for(int i = 0; i < 10; ++i) // error, i is const


    for (mutable int i = 0; i < 10; ++i)

    Yes, there are many places where you want variable variables - but I
    think overall you typically have more that could be declared const than
    have to be variable. (The idea is not without precedence - there are
    other modern languages with constant objects by default.)

    For loops, I think the best solution would be a mixture - the loop
    variable should be mutable within the controlling expressions, but
    should be constant within the statement or block - as though you had
    written:

    I think const is a lot of fuss about nothing frankly. I barely use them
    anyway and I can't remember the last time that I had a bug due to updating
    a variable that shouldn't have been updated. For those who think its simply
    an indication to other devs that a variable won't be changed then fair enough but personally I find a lot of const/non const compilation errors related
    to functions a PITA.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From RacingRabbit@watershipdown.co.uk@21:1/5 to Bart on Thu Oct 21 14:51:06 2021
    On Thu, 21 Oct 2021 15:04:19 +0100
    Bart <bc@freeuk.com> wrote:
    On 21/10/2021 14:40, RacingRabbit@watershipdown.co.uk wrote:
    On Thu, 21 Oct 2021 12:57:05 +0200
    David Brown <david.brown@hesbynett.no> wrote:
    On 21/10/2021 11:48, Juha Nieminen wrote:
    David Brown <david.brown@hesbynett.no> wrote:
    I agree with you entirely. But if we are going for wishful thinking >>>>> about how C++ could have been made better, I'd have preferred "const" >>>>> for all variables and required "mutable" to declare a variable that
    could be modified. (Of course that would mean you'd need something else >>>>> for class members that are today "mutable". But I think it's a lot more >>>>> common for people to make variables that could have been "const" but >>>>> aren't, than to use mutable members.)

    That would quite quickly turn quite annoying, eg. with things like
    for-loops:

    for(int i = 0; i < 10; ++i) // error, i is const


    for (mutable int i = 0; i < 10; ++i)

    Yes, there are many places where you want variable variables - but I
    think overall you typically have more that could be declared const than
    have to be variable. (The idea is not without precedence - there are
    other modern languages with constant objects by default.)

    For loops, I think the best solution would be a mixture - the loop
    variable should be mutable within the controlling expressions, but
    should be constant within the statement or block - as though you had
    written:

    I think const is a lot of fuss about nothing frankly. I barely use them
    anyway and I can't remember the last time that I had a bug due to updating >> a variable that shouldn't have been updated. For those who think its simply >> an indication to other devs that a variable won't be changed then fair enough

    but personally I find a lot of const/non const compilation errors related
    to functions a PITA.


    I don't about C++, but in C, you can take a program, remove all the
    'const' qualifiers, and it will still compile and work.

    Makes you think...

    Well quite. But then I started out in C and const wasn't a thing so you had
    to know what was going on and not always rely on the compiler to tell you.
    But then C doesn't have references I suppose and its a bit difficult to accidentaly dereference a pointer and update it unlike a C++ reference. Even so, I still don't like consts. But its just personal taste.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From RacingRabbit@watershipdown.co.uk@21:1/5 to Manfred on Thu Oct 21 14:57:06 2021
    On Thu, 21 Oct 2021 16:41:34 +0200
    Manfred <noname@add.invalid> wrote:
    On 10/21/2021 4:04 PM, Bart wrote:
    I don't about C++, but in C, you can take a program, remove all the
    'const' qualifiers, and it will still compile and work.

    Makes you think...


    ....mumble, obviously 'const' is almost exclusively for the programmer's >convenience (some implementations may use it to map some const data to
    read only memory, but that's probably a minor use compared to the
    massive benefit in accurate modeling of a process).

    The categories of immutable and mutable data are pretty relevant in
    process modeling, and since SW design is a lot about modeling, long live >'const'.

    Tbh if you don't know which data should be left alone and which you can update you probably shouldn't be working on the code until you learn it a bit better. As for private/protected variables in library classes, that just pisses me off. If I want to alter something then let me Mr Library Coder. Its not up to you
    to decide what I can and can't alter in my own program and you're not going to be able to think up every possible use case in which your "private" variable might need to be accessed or changed.

    I actually had this issue with an in house lib at a company but the muppet
    who wrote it refused to make the variable public or even available via a
    setter even though we needed to access it. In the end I got the address
    of a public variable in the class, counted back in memory and dereferenced the pointer where his private var was stored. Worked a treat though very fragile. Eventually management made him add a getter function so that hack was removed.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Manfred@21:1/5 to Bart on Thu Oct 21 16:41:34 2021
    On 10/21/2021 4:04 PM, Bart wrote:
    On 21/10/2021 14:40, RacingRabbit@watershipdown.co.uk wrote:
    On Thu, 21 Oct 2021 12:57:05 +0200
    David Brown <david.brown@hesbynett.no> wrote:
    On 21/10/2021 11:48, Juha Nieminen wrote:
    David Brown <david.brown@hesbynett.no> wrote:
    I agree with you entirely.  But if we are going for wishful thinking >>>>> about how C++ could have been made better, I'd have preferred "const" >>>>> for all variables and required "mutable" to declare a variable that
    could be modified.  (Of course that would mean you'd need something >>>>> else
    for class members that are today "mutable".  But I think it's a lot >>>>> more
    common for people to make variables that could have been "const" but >>>>> aren't, than to use mutable members.)

    That would quite quickly turn quite annoying, eg. with things like
    for-loops:

       for(int i = 0; i < 10; ++i) // error, i is const


        for (mutable int i = 0; i < 10; ++i)

    Yes, there are many places where you want variable variables - but I
    think overall you typically have more that could be declared const than
    have to be variable.  (The idea is not without precedence - there are
    other modern languages with constant objects by default.)

    For loops, I think the best solution would be a mixture - the loop
    variable should be mutable within the controlling expressions, but
    should be constant within the statement or block - as though you had
    written:

    I think const is a lot of fuss about nothing frankly. I barely use them
    anyway and I can't remember the last time that I had a bug due to
    updating
    a variable that shouldn't have been updated. For those who think its
    simply
    an indication to other devs that a variable won't be changed then fair
    enough
    but personally I find a lot of const/non const compilation errors related
    to functions a PITA.


    I don't about C++, but in C, you can take a program, remove all the
    'const' qualifiers, and it will still compile and work.

    Makes you think...


    ...mumble, obviously 'const' is almost exclusively for the programmer's convenience (some implementations may use it to map some const data to
    read only memory, but that's probably a minor use compared to the
    massive benefit in accurate modeling of a process).

    The categories of immutable and mutable data are pretty relevant in
    process modeling, and since SW design is a lot about modeling, long live 'const'.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Bart on Thu Oct 21 12:09:19 2021
    On Thu, 21 Oct 2021 15:04:19 +0100
    Bart <bc@freeuk.com> wrote:
    ...
    I don't about C++, but in C, you can take a program, remove all the
    'const' qualifiers, and it will still compile and work.

    Makes you think...

    Keep in mind that this is strictly true in C only if you remove all the
    "const" qualifiers from all of the #included header files, and you can't
    do that with standard library headers. It would also be difficult to do
    with the headers associated with third-party libraries.

    More importantly, that's true only if the program would compile without diagnostics before the removal of the 'const' qualifiers, and there's a
    simple, blindingly obvious reason for that, which does NOT count as an
    argument against the proper use of "const":

    The declaration of an identifier containing the 'const' keyword
    indicates that this identifier should not be used in any way that puts
    the object qualified by that keyword in danger of being modified. The
    purpose of that keyword is to enable diagnostic messages when the
    identifier is used in an expression that could put that object in danger
    of being modified. If your program generates no diagnostics, that means
    that it contains no such expressions. Removing "const" from the entire translation unit, including #included headers, will not change the fact
    that there is no danger, and the program should therefore work just the
    same as before. However, if you modify the program again after removing
    all "const" keywords, that modification might create such a danger, but
    the implementation no longer has any obligation of warning you about the danger.

    You said that you don't know about C++. Well there's an important
    difference between C++ and C in this regard: function overloading. A
    function can be overloaded based upon the difference in the way one or
    more of its arguments is qualified. The overloads may do significantly different things - if so, the non-const overload usually attempts to
    modify the relevant object, whereas the const overload uses some
    work-around to avoid the need to modify it. That work-around is usually inconvenient in some way, otherwise there would have been no need to
    declare the const overload. Therefore, at best, removing all use of the
    "const" keyword would require that the non-const overload be dropped,
    and that the less convenient const overload be used at all times.

    That's "usually". Overloads are entirely under the programmer's control,
    and the difference between them need not be "usual" - it very often
    isn't. It would be trivial to create overloads that do significantly
    different things with const and non-const arguments (most trivially,
    they could print out "const" and "non-const" respectively). No single
    function can replace both overloads, but it would have to do so if you
    removed all occurances of the "const" keyword.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to RacingRabbit@watershipdown.co.uk on Thu Oct 21 12:07:30 2021
    On 10/21/21 5:12 AM, RacingRabbit@watershipdown.co.uk wrote:
    ...
    Looking for logic in C++ keywords is a hiding to nothing. Eg const_cast actually means remove const'ness , not add it which is frankly bizarre.

    You can use const_cast<> to add const as easily as removing it. Doing so
    is normally unnecessary, because such conversions can be done
    implicitly, which might be what gave you that impression.

    You can also add and remove the other qualifier, volatile. Therefore, I
    think qual_cast<> might have been a better name for it.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Branimir Maksimovic@21:1/5 to Juha Nieminen on Thu Oct 21 21:27:50 2021
    On 2021-10-21, Juha Nieminen <nospam@thanks.invalid> wrote:
    Time and again I see beginner C++ programmers make the same mistake:
    Make functions take objects by non-const reference, even when (in the
    vast, vast majority of cases) the function doesn't modify those objects.

    In one particularly egregious case some beginner programmer had written
    a comparator lambda that took two std::string objects by reference,
    wondered why he was getting a compiler error, and then removed the references, so it was taking the objects by value. That way it compiled.
    Then he wondered why it was so slow.

    I think it was a mistake to make references non-const by default.
    It's logical (and consistent with pointer syntax) of course, but I think
    it was a mistake. In the vast, vast majority of cases if you create a reference to something else, you want it to be const (and there are
    many good reasons why it should be const). It's extremely rare to
    explicitly want a non-const reference.

    And the thing is, C++98 has a *perfect* keyword to explicitly denote
    that you want a non-const reference, which could have been used for
    this purpose, so no new keyword would be needed for this: 'mutable'.

    In other words, I think C++ would have been better if it worked like
    this:

    void foo1(std::string& str)
    {
    str = "hello"; // error: 'str' is const
    }

    void foo2(mutable std::string& str)
    {
    str = "hello"; // ok
    }

    (You could still write "const std::string&", but it would just have
    the exact same meaning as "std::string&", ie. in this case the
    'const' is superfluous, a bit like how 'signed' is superfluous
    in "signed int".)
    Good thinking, but now to late, perhaps with -std=c++22
    and latter on?

    --

    7-77-777
    Evil Sinner!
    with software, you repeat same experiment, expecting different results...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to James Kuyper on Thu Oct 21 23:49:03 2021
    On 21/10/2021 17:09, James Kuyper wrote:
    On Thu, 21 Oct 2021 15:04:19 +0100
    Bart <bc@freeuk.com> wrote:
    ...
    I don't about C++, but in C, you can take a program, remove all the
    'const' qualifiers, and it will still compile and work.

    Makes you think...

    Keep in mind that this is strictly true in C only if you remove all the "const" qualifiers from all of the #included header files, and you can't
    do that with standard library headers. It would also be difficult to do
    with the headers associated with third-party libraries.

    More importantly, that's true only if the program would compile without diagnostics before the removal of the 'const' qualifiers, and there's a simple, blindingly obvious reason for that, which does NOT count as an argument against the proper use of "const":

    The declaration of an identifier containing the 'const' keyword
    indicates that this identifier should not be used in any way that puts
    the object qualified by that keyword in danger of being modified. The
    purpose of that keyword is to enable diagnostic messages when the
    identifier is used in an expression that could put that object in danger
    of being modified. If your program generates no diagnostics, that means
    that it contains no such expressions. Removing "const" from the entire translation unit, including #included headers, will not change the fact
    that there is no danger, and the program should therefore work just the
    same as before. However, if you modify the program again after removing
    all "const" keywords, that modification might create such a danger, but
    the implementation no longer has any obligation of warning you about the danger.

    You said that you don't know about C++. Well there's an important
    difference between C++ and C in this regard: function overloading. A
    function can be overloaded based upon the difference in the way one or
    more of its arguments is qualified. The overloads may do significantly different things - if so, the non-const overload usually attempts to
    modify the relevant object, whereas the const overload uses some
    work-around to avoid the need to modify it. That work-around is usually inconvenient in some way, otherwise there would have been no need to
    declare the const overload. Therefore, at best, removing all use of the "const" keyword would require that the non-const overload be dropped,
    and that the less convenient const overload be used at all times.

    Actually here's an example from C where const changes the behaviour:

    #include <stdio.h>
    #include <stdint.h>

    int main(void) {
    #define issigned(x) _Generic((x),\
    int8_t: "S",\
    int16_t: "S",\
    int32_t: "S",\
    const int32_t: "const S",\
    int64_t: "S",\
    uint8_t: "u",\
    uint16_t: "u",\
    uint32_t: "u",\
    uint64_t: "u",\
    default: "other")

    int32_t x;
    // const int32_t x;
    puts(issigned(x));
    }

    The output is different when x has a const attribute.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From jameskuyper@alumni.caltech.edu@21:1/5 to Bart on Thu Oct 21 16:21:45 2021
    On Thursday, October 21, 2021 at 6:49:29 PM UTC-4, Bart wrote:
    On 21/10/2021 17:09, James Kuyper wrote:
    On Thu, 21 Oct 2021 15:04:19 +0100
    Bart <b...@freeuk.com> wrote:
    ...
    I don't about C++, but in C, you can take a program, remove all the
    'const' qualifiers, and it will still compile and work.

    Makes you think...

    Keep in mind that this is strictly true in C only if you remove all the "const" qualifiers from all of the #included header files, and you can't
    do that with standard library headers. It would also be difficult to do with the headers associated with third-party libraries.
    ...
    You said that you don't know about C++. Well there's an important difference between C++ and C in this regard: function overloading. A function can be overloaded based upon the difference in the way one or
    more of its arguments is qualified. The overloads may do significantly different things - if so, the non-const overload usually attempts to
    modify the relevant object, whereas the const overload uses some work-around to avoid the need to modify it. That work-around is usually inconvenient in some way, otherwise there would have been no need to declare the const overload. Therefore, at best, removing all use of the "const" keyword would require that the non-const overload be dropped,
    and that the less convenient const overload be used at all times.
    Actually here's an example from C where const changes the behaviour:

    #include <stdio.h>
    #include <stdint.h>

    int main(void) {
    #define issigned(x) _Generic((x),\
    int8_t: "S",\
    int16_t: "S",\
    int32_t: "S",\
    const int32_t: "const S",\
    int64_t: "S",\
    uint8_t: "u",\
    uint16_t: "u",\
    uint32_t: "u",\
    uint64_t: "u",\
    default: "other")

    int32_t x;
    // const int32_t x;
    puts(issigned(x));
    }

    The output is different when x has a const attribute.

    You're correct, I forgot about that. _Generic provides a capability similar to but much more restricted than function overloading, and as such allows the behavior to depend upon the qualifiers.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to RacingRabbit@watershipdown.co.uk on Fri Oct 22 04:45:05 2021
    RacingRabbit@watershipdown.co.uk wrote:
    I think it's pretty logical. It just lists all the exceptions that the >>function can throw. If the list is empty, it means it doesn't throw
    any exception.

    Except throw inside a function means throw any exception currently on the stack.
    You can't have it both ways and for once the C++ committee saw sense and replaced it with noexcept.

    If 'throw(...)' after a function declaration specifies a list of exceptions that the function may throw, it's only logical that if this list is empty
    then it doesn't throw anything.

    You could just as well complain about the multiple different uses of {}.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to Alf P. Steinbach on Fri Oct 22 04:48:12 2021
    Alf P. Steinbach <alf.p.steinbach@gmail.com> wrote:
    Agreed. So what can you do about it?

    Just define

    template< class T > using Ref_ = const T&;

    And then write

    void foo( Ref_<Baluba> x )

    instead of

    void foo( const Baluba& x )

    Doesn't really help with the problem of beginners using non-const
    references everywhere...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to Bonita Montero on Fri Oct 22 04:45:56 2021
    Bonita Montero <Bonita.Montero@gmail.com> wrote:
    You're compulsive.

    And you are an asshole.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to Bart on Fri Oct 22 04:41:47 2021
    Bart <bc@freeuk.com> wrote:
    I don't about C++, but in C, you can take a program, remove all the
    'const' qualifiers, and it will still compile and work.

    At least if you turn off warnings.

    Also, you'll likely make some programs less efficient because the compiler
    will do compile-time calculations on things like const arrays containing compile-time literals, which it won't if the array is not const.

    So yes, 'const' can actually make the program more efficient (especially
    in C++, where it guarantees to the compiler that it can assume the
    contents won't change).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Fri Oct 22 07:55:40 2021
    You're compulsive.

    And you are an asshole.

    That depends on the situation, but sometimes that's true.
    I just don't like compulsive personalities that arise problems
    that don't really exist.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Fri Oct 22 09:46:30 2021
    On 21/10/2021 16:04, Bart wrote:
    On 21/10/2021 14:40, RacingRabbit@watershipdown.co.uk wrote:
    On Thu, 21 Oct 2021 12:57:05 +0200
    David Brown <david.brown@hesbynett.no> wrote:
    On 21/10/2021 11:48, Juha Nieminen wrote:
    David Brown <david.brown@hesbynett.no> wrote:
    I agree with you entirely.  But if we are going for wishful thinking >>>>> about how C++ could have been made better, I'd have preferred "const" >>>>> for all variables and required "mutable" to declare a variable that
    could be modified.  (Of course that would mean you'd need something >>>>> else
    for class members that are today "mutable".  But I think it's a lot >>>>> more
    common for people to make variables that could have been "const" but >>>>> aren't, than to use mutable members.)

    That would quite quickly turn quite annoying, eg. with things like
    for-loops:

       for(int i = 0; i < 10; ++i) // error, i is const


        for (mutable int i = 0; i < 10; ++i)

    Yes, there are many places where you want variable variables - but I
    think overall you typically have more that could be declared const than
    have to be variable.  (The idea is not without precedence - there are
    other modern languages with constant objects by default.)

    For loops, I think the best solution would be a mixture - the loop
    variable should be mutable within the controlling expressions, but
    should be constant within the statement or block - as though you had
    written:

    I think const is a lot of fuss about nothing frankly. I barely use them
    anyway and I can't remember the last time that I had a bug due to
    updating
    a variable that shouldn't have been updated. For those who think its
    simply
    an indication to other devs that a variable won't be changed then fair
    enough
    but personally I find a lot of const/non const compilation errors related
    to functions a PITA.


    I don't about C++, but in C, you can take a program, remove all the
    'const' qualifiers, and it will still compile and work.

    Makes you think...


    I makes you think that it is true that good programming language design
    is more about what you /can't/ do, rather than about what you /can/ do.
    "const" in C does not let you do things you could not otherwise do - it restricts you, thus making code clearer, safer, more maintainable, and
    perhaps sometimes more efficient.

    Const in C++ is more integral to the language, and can't be removed in
    the same way (not that anyone would want to).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to James Kuyper on Fri Oct 22 11:13:39 2021
    On 21/10/2021 18:09, James Kuyper wrote:
    On Thu, 21 Oct 2021 15:04:19 +0100
    Bart <bc@freeuk.com> wrote:
    ...
    I don't about C++, but in C, you can take a program, remove all the
    'const' qualifiers, and it will still compile and work.

    Makes you think...

    Keep in mind that this is strictly true in C only if you remove all the "const" qualifiers from all of the #included header files, and you can't
    do that with standard library headers. It would also be difficult to do
    with the headers associated with third-party libraries.

    I'm not suggesting that this would be at all a good idea, and it would certainly be undefined and undocumented behaviour, but you could
    probably remove "const" by adding "-Dconst=" to your compiler flags. It
    works for gcc (maybe I should file a bug here - it is even accepted with -std=c99 -Wpedantic).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Branimir Maksimovic@21:1/5 to Bonita Montero on Fri Oct 22 11:21:03 2021
    On 2021-10-22, Bonita Montero <Bonita.Montero@gmail.com> wrote:
    You're compulsive.

    And you are an asshole.

    That depends on the situation, but sometimes that's true.
    I just don't like compulsive personalities that arise problems
    that don't really exist.
    PEACE&LOVE, brothers and sisters :P

    --

    7-77-777
    Evil Sinner!
    with software, you repeat same experiment, expecting different results...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to james...@alumni.caltech.edu on Fri Oct 22 16:14:01 2021
    On 22/10/2021 00:21, james...@alumni.caltech.edu wrote:
    On Thursday, October 21, 2021 at 6:49:29 PM UTC-4, Bart wrote:
    On 21/10/2021 17:09, James Kuyper wrote:
    On Thu, 21 Oct 2021 15:04:19 +0100
    Bart <b...@freeuk.com> wrote:
    ...
    I don't about C++, but in C, you can take a program, remove all the
    'const' qualifiers, and it will still compile and work.

    Makes you think...

    Keep in mind that this is strictly true in C only if you remove all the
    "const" qualifiers from all of the #included header files, and you can't >>> do that with standard library headers. It would also be difficult to do
    with the headers associated with third-party libraries.
    ...
    You said that you don't know about C++. Well there's an important
    difference between C++ and C in this regard: function overloading. A
    function can be overloaded based upon the difference in the way one or
    more of its arguments is qualified. The overloads may do significantly
    different things - if so, the non-const overload usually attempts to
    modify the relevant object, whereas the const overload uses some
    work-around to avoid the need to modify it. That work-around is usually
    inconvenient in some way, otherwise there would have been no need to
    declare the const overload. Therefore, at best, removing all use of the
    "const" keyword would require that the non-const overload be dropped,
    and that the less convenient const overload be used at all times.
    Actually here's an example from C where const changes the behaviour:

    #include <stdio.h>
    #include <stdint.h>

    int main(void) {
    #define issigned(x) _Generic((x),\
    int8_t: "S",\
    int16_t: "S",\
    int32_t: "S",\
    const int32_t: "const S",\
    int64_t: "S",\
    uint8_t: "u",\
    uint16_t: "u",\
    uint32_t: "u",\
    uint64_t: "u",\
    default: "other")

    int32_t x;
    // const int32_t x;
    puts(issigned(x));
    }

    The output is different when x has a const attribute.

    You're correct, I forgot about that. _Generic provides a capability similar to but much more restricted than function overloading, and as such allows the behavior to depend upon the qualifiers.

    That program only behaves as I said when I use my own compiler.

    With gcc and others that support _Generic, any 'const' attributes of the
    types of controlling expressions appear to be stripped away.

    I'm not sure why that is. It looked to be a bug in gcc, but other
    compilers do the same. Maybe they are all copying gcc's behaviour, but I
    don't what C itself says about it.

    With the version below, gcc et al display:

    int
    int

    With mine, it displays:

    int
    const int

    --------------------------------

    #include <stdio.h>

    int main(void) {
    #define strtype(x) _Generic((x),\
    int: "int",\
    const int: "const int")

    int x;
    const int y;
    puts(strtype(x));
    puts(strtype(y));
    }

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Fri Oct 22 15:48:45 2021
    On 22/10/2021 08:46, David Brown wrote:
    On 21/10/2021 16:04, Bart wrote:

    I don't [know] about C++, but in C, you can take a program, remove all the >> 'const' qualifiers, and it will still compile and work.

    Makes you think...


    I makes you think that it is true that good programming language design
    is more about what you /can't/ do, rather than about what you /can/ do.
    "const" in C does not let you do things you could not otherwise do - it restricts you, thus making code clearer, safer, more maintainable, and perhaps sometimes more efficient.

    There are better ways of doing it. In C, it is just adds a lot of
    clutter that effects readability and can hide real problems.

    Neither does the syntax make it that obvious which bit of the type is
    refered to, as in:

    const int * const * x;

    The first const applies to the following int; the second const refers to
    the /previous/ * (AIUI).

    This can give a false sense of security, especially when you have, say,
    a const pointer to a struct which contains non-const pointers. The
    'const' only protects that top level; it does not stop you writing
    nested non-const data.

    In my example, x can still be written to! (x=0 is allowed; but *x=0 and
    **x=0 are not.)

    Use of 'const' can also proliferate through interactions with non-const versions of the type, adding to the clutter.

    (I don't do much with readonly stuff [in my languages]. My experiments
    focus on mutability of objects, or specific variables, not of types.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Keith Thompson@21:1/5 to Bart on Fri Oct 22 12:04:56 2021
    Bart <bc@freeuk.com> writes:
    [...]
    That program only behaves as I said when I use my own compiler.

    With gcc and others that support _Generic, any 'const' attributes of
    the types of controlling expressions appear to be stripped away.

    I'm not sure why that is. It looked to be a bug in gcc, but other
    compilers do the same. Maybe they are all copying gcc's behaviour, but
    I don't what C itself says about it.
    [...]

    _Generic is a C feature that does not appear in C++.

    The first operand of _Generic is an expression, not an object, and it determines the type of that expression. If the expression happens to be
    an lvalue, it undergoes *lvalue conversion* (N1570 6.3.2.1p2), which
    strips any qualifiers. So if the operand of _Generic is the name of an
    object of type const int, the type of that *expression* is int, not
    const int. (Something like _Generic that operates on lvalues rather
    than expressions might have been useful, but I've never had a need for
    it.)

    Consider an expression like (n + 1). Would you expect it to have a
    different type depending on whether n was defined as const or not?

    C++ overloading, unlike C's _Generic, can use references to distinguish
    between const and non-const objects. You can't have two overloaded
    functions that differ only in their parameter type, int vs. const int,
    but you can have overloaded functions with parameters of type int& and
    const int&.

    --
    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 */

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Keith Thompson on Fri Oct 22 20:47:50 2021
    On 22/10/2021 20:04, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:
    [...]
    That program only behaves as I said when I use my own compiler.

    With gcc and others that support _Generic, any 'const' attributes of
    the types of controlling expressions appear to be stripped away.

    I'm not sure why that is. It looked to be a bug in gcc, but other
    compilers do the same. Maybe they are all copying gcc's behaviour, but
    I don't what C itself says about it.
    [...]

    _Generic is a C feature that does not appear in C++.

    The first operand of _Generic is an expression, not an object, and it determines the type of that expression. If the expression happens to be
    an lvalue, it undergoes *lvalue conversion* (N1570 6.3.2.1p2), which
    strips any qualifiers. So if the operand of _Generic is the name of an object of type const int, the type of that *expression* is int, not
    const int. (Something like _Generic that operates on lvalues rather
    than expressions might have been useful, but I've never had a need for
    it.)

    Consider an expression like (n + 1). Would you expect it to have a
    different type depending on whether n was defined as const or not?

    If I get my C compiler to display the type, then for 'const int n', n
    has type 'const int', and n+1 has type 'int'.

    But let me ask you, if p has type 'const int* p', do you expect 'p+1' to
    have a different type from 'p'? (Namely, 'int*' instead of 'const int*'.)

    I understand now that the const qualifiers are only removed at the top
    level, so only the leftmost 'const' if types were written left to right
    (my const int* p example would have it after 'pointer to').

    This still makes the way _Generic works unintuitive.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From danielaparker@gmail.com@21:1/5 to David Brown on Fri Oct 22 12:28:32 2021
    On Thursday, October 21, 2021 at 2:54:45 AM UTC-4, David Brown wrote:
    On 21/10/2021 07:23, Juha Nieminen wrote:

    In other words, I think C++ would have been better if it worked like
    this:

    void foo1(std::string& str)
    {
    str = "hello"; // error: 'str' is const
    }

    void foo2(mutable std::string& str)
    {
    str = "hello"; // ok
    }

    It would have been better if std::string was immutable.

    I agree with you entirely. But if we are going for wishful thinking
    about how C++ could have been made better, I'd have preferred "const"
    for all variables and required "mutable" to declare a variable that
    could be modified.

    Note though that const does not mean immutable.

    Daniel

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Keith Thompson@21:1/5 to Bart on Fri Oct 22 13:29:24 2021
    Bart <bc@freeuk.com> writes:
    On 22/10/2021 20:04, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:
    [...]
    That program only behaves as I said when I use my own compiler.

    With gcc and others that support _Generic, any 'const' attributes of
    the types of controlling expressions appear to be stripped away.

    I'm not sure why that is. It looked to be a bug in gcc, but other
    compilers do the same. Maybe they are all copying gcc's behaviour, but
    I don't what C itself says about it.
    [...]
    _Generic is a C feature that does not appear in C++.
    The first operand of _Generic is an expression, not an object, and
    it
    determines the type of that expression. If the expression happens to be
    an lvalue, it undergoes *lvalue conversion* (N1570 6.3.2.1p2), which
    strips any qualifiers. So if the operand of _Generic is the name of an
    object of type const int, the type of that *expression* is int, not
    const int. (Something like _Generic that operates on lvalues rather
    than expressions might have been useful, but I've never had a need for
    it.)
    Consider an expression like (n + 1). Would you expect it to have a
    different type depending on whether n was defined as const or not?

    If I get my C compiler to display the type, then for 'const int n', n
    has type 'const int', and n+1 has type 'int'.

    If your C compiler displays the type using some non-standard extension,
    then of course that extension can do anything you like.

    But let me ask you, if p has type 'const int* p', do you expect 'p+1'
    to have a different type from 'p'? (Namely, 'int*' instead of 'const
    int*'.)

    No, p+1 would have type const int* (pointer to const int).

    I understand now that the const qualifiers are only removed at the top
    level, so only the leftmost 'const' if types were written left to
    right (my const int* p example would have it after 'pointer to').

    This still makes the way _Generic works unintuitive.

    I find it intuitive, but perhaps not immediately so. I had to remind
    myself about the lvalue conversion, and that its operand is an
    expression, not an object, for it to make sense.

    If you want to continue discussing this, I suggest starting a new thread
    in comp.lang.c.

    --
    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 */

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to David Brown on Fri Oct 22 19:18:17 2021
    On 10/22/21 5:13 AM, David Brown wrote:
    ...
    I'm not suggesting that this would be at all a good idea, and it would certainly be undefined and undocumented behaviour, but you could
    probably remove "const" by adding "-Dconst=" to your compiler flags. It works for gcc (maybe I should file a bug here - it is even accepted with -std=c99 -Wpedantic).


    You are right - the behavior would be undefined:

    "The program shall not have any macros with names lexically identical to keywords currently defined prior to the inclusion of the header or when
    any macro defined in the header is expanded." (C standard, 7.1.2p5)

    As a "shall" occurring outside of a "Constraints" section, the behavior
    of a program that violates that rule is undefined (4p2).

    But it would probably work as intended on many implementations.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Sat Oct 23 11:54:44 2021
    On 22/10/2021 17:14, Bart wrote:
    On 22/10/2021 00:21, james...@alumni.caltech.edu wrote:
    On Thursday, October 21, 2021 at 6:49:29 PM UTC-4, Bart wrote:
    On 21/10/2021 17:09, James Kuyper wrote:
    On Thu, 21 Oct 2021 15:04:19 +0100
    Bart <b...@freeuk.com> wrote:
    ...
    I don't about C++, but in C, you can take a program, remove all the
    'const' qualifiers, and it will still compile and work.

    Makes you think...

    Keep in mind that this is strictly true in C only if you remove all the >>>> "const" qualifiers from all of the #included header files, and you
    can't
    do that with standard library headers. It would also be difficult to do >>>> with the headers associated with third-party libraries.
    ...
    You said that you don't know about C++. Well there's an important
    difference between C++ and C in this regard: function overloading. A
    function can be overloaded based upon the difference in the way one or >>>> more of its arguments is qualified. The overloads may do significantly >>>> different things - if so, the non-const overload usually attempts to
    modify the relevant object, whereas the const overload uses some
    work-around to avoid the need to modify it. That work-around is usually >>>> inconvenient in some way, otherwise there would have been no need to
    declare the const overload. Therefore, at best, removing all use of the >>>> "const" keyword would require that the non-const overload be dropped,
    and that the less convenient const overload be used at all times.
    Actually here's an example from C where const changes the behaviour:

    #include <stdio.h>
    #include <stdint.h>

    int main(void) {
    #define issigned(x) _Generic((x),\
    int8_t: "S",\
    int16_t: "S",\
    int32_t: "S",\
    const int32_t: "const S",\
    int64_t: "S",\
    uint8_t: "u",\
    uint16_t: "u",\
    uint32_t: "u",\
    uint64_t: "u",\
    default: "other")

    int32_t x;
    // const int32_t x;
    puts(issigned(x));
    }

    The output is different when x has a const attribute.

    You're correct, I forgot about that. _Generic provides a capability
    similar to but much more restricted than function overloading, and as
    such allows the behavior to depend upon the qualifiers.

    That program only behaves as I said when I use my own compiler.

    With gcc and others that support _Generic, any 'const' attributes of the types of controlling expressions appear to be stripped away.

    I'm not sure why that is. It looked to be a bug in gcc, but other
    compilers do the same. Maybe they are all copying gcc's behaviour, but I don't what C itself says about it.

    With the version below, gcc et al display:

      int
      int

    With mine, it displays:

      int
      const int

    It turns out - who would have guessed? - that gcc is correct. The gcc developers these days tend to be very careful and strict about this kind
    of thing.

    As Keith says, the controlling expression undergoes "lvalue conversion"
    (this is in 6.5.1.1p2, if you want to look it up). C18 helpfully adds a footnote that did not exist in C11, saying "An lvalue conversion drops
    type qualifiers". (I think the standard could benefit from more of such explanatory footnotes.)

    I think it is odd, however, that you can have qualified types in the
    generic association list, since they can't ever match anything (AFAICS).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Sat Oct 23 12:21:18 2021
    On 22/10/2021 16:48, Bart wrote:
    On 22/10/2021 08:46, David Brown wrote:
    On 21/10/2021 16:04, Bart wrote:

    I don't [know] about C++, but in C, you can take a program, remove
    all the
    'const' qualifiers, and it will still compile and work.

    Makes you think...


    I makes you think that it is true that good programming language design
    is more about what you /can't/ do, rather than about what you /can/ do.
      "const" in C does not let you do things you could not otherwise do - it >> restricts you, thus making code clearer, safer, more maintainable, and
    perhaps sometimes more efficient.

    There are better ways of doing it.

    There are certainly /different/ ways of doing things. There are lots of different programming languages, with their different strengths and
    weaknesses.

    In C, it is just adds a lot of
    clutter that effects readability and can hide real problems.

    What an odd idea.

    If you don't like "const", don't use it in your programming. Others
    find it useful to aid readability and avoid problems.


    Neither does the syntax make it that obvious which bit of the type is
    refered to, as in:

      const int * const * x;

    If you find this kind of thing confusing, use "typedef". It exists to
    improve readability (amongst other benefits).


    The first const applies to the following int; the second const refers to
    the /previous/ * (AIUI).

    This can give a false sense of security, especially when you have, say,
    a const pointer to a struct which contains non-const pointers. The
    'const' only protects that top level; it does not stop you writing
    nested non-const data.


    You mean, people who don't really understand what they are doing and
    write code that confuses themselves, get mixed up? And how is C
    different from any other language in that respect?

    I appreciate that you personally prefer a different ordering when
    writing types, and that you are not alone in that. Fine. C has a
    different ordering, and people usually manage perfectly well. The
    difference between "const int * x", "int * const x" and "const int *
    const x" is one of these things newbies to C often find hard, and it
    turns up in every FAQ and tutorial on the language. If /you/ still find
    it hard, read a FAQ.

    In my example, x can still be written to! (x=0 is allowed; but *x=0 and
    **x=0 are not.)

    Yes - x is not const.


    Use of 'const' can also proliferate through interactions with non-const versions of the type, adding to the clutter.

    (I don't do much with readonly stuff [in my languages]. My experiments
    focus on mutability of objects, or specific variables, not of types.)


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Sat Oct 23 12:06:44 2021
    On 23/10/2021 11:21, David Brown wrote:
    On 22/10/2021 16:48, Bart wrote:
    On 22/10/2021 08:46, David Brown wrote:
    On 21/10/2021 16:04, Bart wrote:

    I don't [know] about C++, but in C, you can take a program, remove
    all the
    'const' qualifiers, and it will still compile and work.

    Makes you think...


    I makes you think that it is true that good programming language design
    is more about what you /can't/ do, rather than about what you /can/ do.
      "const" in C does not let you do things you could not otherwise do - it >>> restricts you, thus making code clearer, safer, more maintainable, and
    perhaps sometimes more efficient.

    There are better ways of doing it.

    There are certainly /different/ ways of doing things. There are lots of different programming languages, with their different strengths and weaknesses.

    In C, it is just adds a lot of
    clutter that effects readability and can hide real problems.

    What an odd idea.

    If you don't like "const", don't use it in your programming. Others
    find it useful to aid readability and avoid problems.

    I was thinking more about other people's code. Mine doesn't use const at
    all.


    Neither does the syntax make it that obvious which bit of the type is
    refered to, as in:

      const int * const * x;

    If you find this kind of thing confusing, use "typedef". It exists to improve readability (amongst other benefits).

    So, even /more/ clutter?! I's also like to see a typedefed version of my example that is not harder to understand.


    The first const applies to the following int; the second const refers to
    the /previous/ * (AIUI).

    This can give a false sense of security, especially when you have, say,
    a const pointer to a struct which contains non-const pointers. The
    'const' only protects that top level; it does not stop you writing
    nested non-const data.


    You mean, people who don't really understand what they are doing and
    write code that confuses themselves, get mixed up? And how is C
    different from any other language in that respect?

    Yes, everybody. You have a dynamic tree data structure for example,
    using non-const references within its nodes to allow it to be updated.

    How do you write a function that takes a reference to that tree, but is
    not allowed to update it?

    This is what someone might expect of an immutable parameter.

    This is not to say that I know how to achieve this; I don't (but I
    haven't researched it much either). The nearest I can do is pass a deep
    copy of such a tree, to protect the original, but that is hardly efficient.

    I just see C's const as a waste of time. I'm starting to use readonly
    data in a few places without my languages, but where it's handled sensibly.

    What I don't do is introduce such a polarising type attribute at every
    level of a data structure, one that poisons every other type it comes
    into contact with, such that it becomes challenging to do perfectly
    innocuous things.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Sat Oct 23 12:35:57 2021
    On 23/10/2021 12:22, Bart wrote:
    On 23/10/2021 10:54, David Brown wrote:
    On 22/10/2021 17:14, Bart wrote:
    On 22/10/2021 00:21, james...@alumni.caltech.edu wrote:
    On Thursday, October 21, 2021 at 6:49:29 PM UTC-4, Bart wrote:
    On 21/10/2021 17:09, James Kuyper wrote:
    On Thu, 21 Oct 2021 15:04:19 +0100
    Bart <b...@freeuk.com> wrote:
    ...
    I don't about C++, but in C, you can take a program, remove all the >>>>>>> 'const' qualifiers, and it will still compile and work.

    Makes you think...

    Keep in mind that this is strictly true in C only if you remove
    all the
    "const" qualifiers from all of the #included header files, and you >>>>>> can't
    do that with standard library headers. It would also be difficult
    to do
    with the headers associated with third-party libraries.
    ...
    You said that you don't know about C++. Well there's an important
    difference between C++ and C in this regard: function overloading. A >>>>>> function can be overloaded based upon the difference in the way
    one or
    more of its arguments is qualified. The overloads may do
    significantly
    different things - if so, the non-const overload usually attempts to >>>>>> modify the relevant object, whereas the const overload uses some
    work-around to avoid the need to modify it. That work-around is
    usually
    inconvenient in some way, otherwise there would have been no need to >>>>>> declare the const overload. Therefore, at best, removing all use
    of the
    "const" keyword would require that the non-const overload be dropped, >>>>>> and that the less convenient const overload be used at all times.
    Actually here's an example from C where const changes the behaviour: >>>>>
    #include <stdio.h>
    #include <stdint.h>

    int main(void) {
    #define issigned(x) _Generic((x),\
    int8_t: "S",\
    int16_t: "S",\
    int32_t: "S",\
    const int32_t: "const S",\
    int64_t: "S",\
    uint8_t: "u",\
    uint16_t: "u",\
    uint32_t: "u",\
    uint64_t: "u",\
    default: "other")

    int32_t x;
    // const int32_t x;
    puts(issigned(x));
    }

    The output is different when x has a const attribute.

    You're correct, I forgot about that. _Generic provides a capability
    similar to but much more restricted than function overloading, and as
    such allows the behavior to depend upon the qualifiers.

    That program only behaves as I said when I use my own compiler.

    With gcc and others that support _Generic, any 'const' attributes of the >>> types of controlling expressions appear to be stripped away.

    I'm not sure why that is. It looked to be a bug in gcc, but other
    compilers do the same. Maybe they are all copying gcc's behaviour, but I >>> don't what C itself says about it.

    With the version below, gcc et al display:

       int
       int

    With mine, it displays:

       int
       const int

    It turns out - who would have guessed? - that gcc is correct.  The gcc
    developers these days tend to be very careful and strict about this kind
    of thing.

    As Keith says, the controlling expression undergoes "lvalue conversion"
    (this is in 6.5.1.1p2, if you want to look it up).  C18 helpfully adds a
    footnote that did not exist in C11, saying "An lvalue conversion drops
    type qualifiers".  (I think the standard could benefit from more of such
    explanatory footnotes.)

    I think it is odd, however, that you can have qualified types in the
    generic association list, since they can't ever match anything (AFAICS).



    They can be used in examples like this:

      #include <stdio.h>

      #define strtypeof(t) _Generic(t,\
          const int*: "pointer to const int",\
          int*:       "pointer to int",\
          default:    "other")

      int main(void) {
          int * p;
          const int * q;

          puts(strtypeof(p));
          puts(strtypeof(q));
      }

    Yes - but those are not qualified types. Using your preferred ordering,
    a "pointer to int" and "pointer to const int" are different types.


    All compilers that support _Generic show:

        pointer to int
        pointer to const int

    This suggests a way to maintain those top level qualifiers, by wrapping
    a pointer around a type. But it would be an ungainly workaround (and the
    fact that typeof() also drops those qualifiers would might make it impractical).

    Why are you inventing an ugly workaround for a non-existent problem?
    _Generic in C looks at the unqualified type of an expression - there
    isn't a problem.

    It turns out your compiler has a bug due to a slight misunderstanding of _Generic. I'm glad you've found it, and can correct it (assuming you
    want to be closer to following the standards). But no one is looking
    for a "workaround" here. (Especially not /here/, in c.l.c++ !)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Sat Oct 23 11:22:03 2021
    On 23/10/2021 10:54, David Brown wrote:
    On 22/10/2021 17:14, Bart wrote:
    On 22/10/2021 00:21, james...@alumni.caltech.edu wrote:
    On Thursday, October 21, 2021 at 6:49:29 PM UTC-4, Bart wrote:
    On 21/10/2021 17:09, James Kuyper wrote:
    On Thu, 21 Oct 2021 15:04:19 +0100
    Bart <b...@freeuk.com> wrote:
    ...
    I don't about C++, but in C, you can take a program, remove all the >>>>>> 'const' qualifiers, and it will still compile and work.

    Makes you think...

    Keep in mind that this is strictly true in C only if you remove all the >>>>> "const" qualifiers from all of the #included header files, and you
    can't
    do that with standard library headers. It would also be difficult to do >>>>> with the headers associated with third-party libraries.
    ...
    You said that you don't know about C++. Well there's an important
    difference between C++ and C in this regard: function overloading. A >>>>> function can be overloaded based upon the difference in the way one or >>>>> more of its arguments is qualified. The overloads may do significantly >>>>> different things - if so, the non-const overload usually attempts to >>>>> modify the relevant object, whereas the const overload uses some
    work-around to avoid the need to modify it. That work-around is usually >>>>> inconvenient in some way, otherwise there would have been no need to >>>>> declare the const overload. Therefore, at best, removing all use of the >>>>> "const" keyword would require that the non-const overload be dropped, >>>>> and that the less convenient const overload be used at all times.
    Actually here's an example from C where const changes the behaviour:

    #include <stdio.h>
    #include <stdint.h>

    int main(void) {
    #define issigned(x) _Generic((x),\
    int8_t: "S",\
    int16_t: "S",\
    int32_t: "S",\
    const int32_t: "const S",\
    int64_t: "S",\
    uint8_t: "u",\
    uint16_t: "u",\
    uint32_t: "u",\
    uint64_t: "u",\
    default: "other")

    int32_t x;
    // const int32_t x;
    puts(issigned(x));
    }

    The output is different when x has a const attribute.

    You're correct, I forgot about that. _Generic provides a capability
    similar to but much more restricted than function overloading, and as
    such allows the behavior to depend upon the qualifiers.

    That program only behaves as I said when I use my own compiler.

    With gcc and others that support _Generic, any 'const' attributes of the
    types of controlling expressions appear to be stripped away.

    I'm not sure why that is. It looked to be a bug in gcc, but other
    compilers do the same. Maybe they are all copying gcc's behaviour, but I
    don't what C itself says about it.

    With the version below, gcc et al display:

      int
      int

    With mine, it displays:

      int
      const int

    It turns out - who would have guessed? - that gcc is correct. The gcc developers these days tend to be very careful and strict about this kind
    of thing.

    As Keith says, the controlling expression undergoes "lvalue conversion"
    (this is in 6.5.1.1p2, if you want to look it up). C18 helpfully adds a footnote that did not exist in C11, saying "An lvalue conversion drops
    type qualifiers". (I think the standard could benefit from more of such explanatory footnotes.)

    I think it is odd, however, that you can have qualified types in the
    generic association list, since they can't ever match anything (AFAICS).



    They can be used in examples like this:

    #include <stdio.h>

    #define strtypeof(t) _Generic(t,\
    const int*: "pointer to const int",\
    int*: "pointer to int",\
    default: "other")

    int main(void) {
    int * p;
    const int * q;

    puts(strtypeof(p));
    puts(strtypeof(q));
    }

    All compilers that support _Generic show:

    pointer to int
    pointer to const int

    This suggests a way to maintain those top level qualifiers, by wrapping
    a pointer around a type. But it would be an ungainly workaround (and the
    fact that typeof() also drops those qualifiers would might make it impractical).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ben Bacarisse@21:1/5 to David Brown on Sat Oct 23 13:23:00 2021
    David Brown <david.brown@hesbynett.no> writes:

    I think it is odd, however, that you can have qualified types in the
    generic association list, since they can't ever match anything
    (AFAICS).

    I was curious so I tried this:

    #include <stdio.h>

    struct S { const int i; } s;
    struct S f(void) { return s; }

    int main(void)
    {
    const char *t =
    _Generic(f().i,
    int: "int",
    const int: "const int");
    puts(t);
    }

    f().i is not a lvalue and has a const-qualified type. gcc prints "int",
    but clang prints "const int". I think clang is right here. (So much
    for "they all copy gcc"!)

    --
    Ben.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Ben Bacarisse on Sat Oct 23 13:57:18 2021
    On 23/10/2021 13:23, Ben Bacarisse wrote:
    David Brown <david.brown@hesbynett.no> writes:

    I think it is odd, however, that you can have qualified types in the
    generic association list, since they can't ever match anything
    (AFAICS).

    I was curious so I tried this:

    #include <stdio.h>

    struct S { const int i; } s;
    struct S f(void) { return s; }

    int main(void)
    {
    const char *t =
    _Generic(f().i,
    int: "int",
    const int: "const int");
    puts(t);
    }

    f().i is not a lvalue and has a const-qualified type. gcc prints "int",
    but clang prints "const int". I think clang is right here. (So much
    for "they all copy gcc"!)

    You need to file a bug report to Clang's developers so that they can fix
    that oversight!

    But, why do think Clang is wrong? The type has a top-level const qualifier.

    I don't get why this is only removed for an lvalue (where you'd think
    that a const attribute is more critical).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Sat Oct 23 18:45:22 2021
    On 23/10/2021 13:06, Bart wrote:
    On 23/10/2021 11:21, David Brown wrote:
    On 22/10/2021 16:48, Bart wrote:
    On 22/10/2021 08:46, David Brown wrote:
    On 21/10/2021 16:04, Bart wrote:

    I don't [know] about C++, but in C, you can take a program, remove
    all the
    'const' qualifiers, and it will still compile and work.

    Makes you think...


    I makes you think that it is true that good programming language design >>>> is more about what you /can't/ do, rather than about what you /can/ do. >>>>    "const" in C does not let you do things you could not otherwise
    do - it
    restricts you, thus making code clearer, safer, more maintainable, and >>>> perhaps sometimes more efficient.

    There are better ways of doing it.

    There are certainly /different/ ways of doing things.  There are lots of
    different programming languages, with their different strengths and
    weaknesses.

    In C, it is just adds a lot of
    clutter that effects readability and can hide real problems.

    What an odd idea.

    If you don't like "const", don't use it in your programming.  Others
    find it useful to aid readability and avoid problems.

    I was thinking more about other people's code. Mine doesn't use const at
    all.

    Perhaps if you used more of C's common features yourself, you'd be less confused about them and less inclined to think they are "clutter" or
    hinder readability. (If you only want to use C as an output language
    from your transpilers, and thus only use a subset of the language, then
    that's absolutely fine - but it makes you a poor judge of what features
    are useful to people working with human-written C code rather than machine-generated C code.)



    Neither does the syntax make it that obvious which bit of the type is
    refered to, as in:

       const int * const * x;

    If you find this kind of thing confusing, use "typedef".  It exists to
    improve readability (amongst other benefits).

    So, even /more/ clutter?! I's also like to see a typedefed version of my example that is not harder to understand.


    typedef const int constant_integer;
    typedef constant_integer * pointer_to_constant_integer;
    typedef const pointer_to_constant_integer
    constant_pointer_to_constant_integer;
    typedef constant_pointer_to_constant_integer * pointer_to_constant_pointer_to_constant_integer;

    pointer_to_constant_pointer_to_constant_integer x;


    That's the order you prefer, is it not? (I'm not suggesting it's a good
    way to write it, I'm merely showing you how it could be done with a
    choice of names that might suit your liking.)


    Maybe you want it more compact:

    typedef const int * p_cint;
    typedef const p_cint * p_cp_cint;
    p_cp_cint x;


    In real code, of course, it would usually make more sense to think about
    what your types actually are and how they will be used, and then use
    type names that fit.



    The first const applies to the following int; the second const refers to >>> the /previous/ * (AIUI).

    This can give a false sense of security, especially when you have, say,
    a const pointer to a struct which contains non-const pointers. The
    'const' only protects that top level; it does not stop you writing
    nested non-const data.


    You mean, people who don't really understand what they are doing and
    write code that confuses themselves, get mixed up?  And how is C
    different from any other language in that respect?

    Yes, everybody. You have a dynamic tree data structure for example,
    using non-const references within its nodes to allow it to be updated.

    How do you write a function that takes a reference to that tree, but is
    not allowed to update it?

    This is what someone might expect of an immutable parameter.

    There are occasions when it is more convenient to cast away const, or
    where it is hard to maintain full const correctness. "const" does not
    absolve the programmer of having to think. But it does make a lot of
    code clearer and easier to understand.


    This is not to say that I know how to achieve this; I don't (but I
    haven't researched it much either). The nearest I can do is pass a deep
    copy of such a tree, to protect the original, but that is hardly efficient.

    I just see C's const as a waste of time. I'm starting to use readonly
    data in a few places without my languages, but where it's handled sensibly.

    What I don't do is introduce such a polarising type attribute at every
    level of a data structure, one that poisons every other type it comes
    into contact with, such that it becomes challenging to do perfectly
    innocuous things.


    Nobody does that with "const". I guess it is just yet another of C's
    features that you don't quite understand, and prefer to hate
    irrationally than learn.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Sat Oct 23 18:58:20 2021
    On 23/10/2021 17:45, David Brown wrote:
    On 23/10/2021 13:06, Bart wrote:

       const int * const * x;

    If you find this kind of thing confusing, use "typedef".  It exists to
    improve readability (amongst other benefits).

    So, even /more/ clutter?! I's also like to see a typedefed version of my
    example that is not harder to understand.


    typedef const int constant_integer;
    typedef constant_integer * pointer_to_constant_integer;
    typedef const pointer_to_constant_integer constant_pointer_to_constant_integer;
    typedef constant_pointer_to_constant_integer * pointer_to_constant_pointer_to_constant_integer;

    pointer_to_constant_pointer_to_constant_integer x;


    That's the order you prefer, is it not? (I'm not suggesting it's a good
    way to write it, I'm merely showing you how it could be done with a
    choice of names that might suit your liking.)


    Maybe you want it more compact:

    typedef const int * p_cint;
    typedef const p_cint * p_cp_cint;
    p_cp_cint x;


    Well, I was right, the alternatives are worse.

    If you are interested in the actual type, or the 'shape' of that type,
    devoid of qualifiers, then you don't want all that. You want to know the
    type is 'int**'.


    This is what someone might expect of an immutable parameter.

    There are occasions when it is more convenient to cast away const, or
    where it is hard to maintain full const correctness. "const" does not absolve the programmer of having to think. But it does make a lot of
    code clearer and easier to understand.

    Somebody writes an informal library but doesn't bother to mark with
    'const' those functions that take char* that don't happen to modify the
    string:

    void f1(char*);
    void f2(char*);
    void f3(char*);

    Now someone who has a mania for 'const' wants to use it:

    const char* s="ABC";
    f1(s);

    However, it doesn't work. They will know from the specs that f1 doesn't
    write into the string, but the compiler doesn't know that.

    Now, it starts to get messy. Either casts have to be inserted, or the
    library needs to be heavily revised. Then that library may import
    another which is also missing consts. And so const-poisoning infects the
    whole code-base.

    At some point, it will also stop you doing things legally, and you have
    to start using casts. Now, you are starting to fight the language.

    Was it Pascal or Ada that first had those in/out parameter attributes?

    I can write this [in my syntax]:

    proc f1(ichar s) = {} # anything goes
    proc f2(ichar in s) = {} # s is input to the function
    proc f3(ichar out s) = {} # s is output from the function

    I don't do anything with these at the minute (I think 'out' and 'inout',
    not shown, are just aliases for '&') but they can do a lot just as
    annotations.

    At some point an implementation can enforce them and ensure that an 'in'
    data structure is not modified in the function, even one that has
    mutable components. The programmer doesn't need to micro-manage every
    level of the type structure, or have to think about exactly how
    foolproof those 'const' attributes are.

    It should be like a write-protect switch on the whole caboodle.

    (At least, within the bounds of what the language can help with. A data structure may contain references to external data, such as files, disks, images, which can all be modifible, or they can be altered via another
    path to the original data.

    But C's const doesn't prevent that either.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ben Bacarisse@21:1/5 to Bart on Sat Oct 23 21:02:06 2021
    Bart <bc@freeuk.com> writes:

    On 23/10/2021 13:23, Ben Bacarisse wrote:
    David Brown <david.brown@hesbynett.no> writes:

    I think it is odd, however, that you can have qualified types in the
    generic association list, since they can't ever match anything
    (AFAICS).
    I was curious so I tried this:
    #include <stdio.h>
    struct S { const int i; } s;
    struct S f(void) { return s; }
    int main(void)
    {
    const char *t =
    _Generic(f().i,
    int: "int",
    const int: "const int");
    puts(t);
    }
    f().i is not a lvalue and has a const-qualified type. gcc prints "int",
    but clang prints "const int". I think clang is right here. (So much
    for "they all copy gcc"!)

    You need to file a bug report to Clang's developers so that they can
    fix that oversight!

    But, why do think Clang is wrong? The type has a top-level const
    qualifier.

    I said I think clang is right (because the expression f().i is not an
    lvalue).

    I don't get why this is only removed for an lvalue (where you'd think
    that a const attribute is more critical).

    lvalue conversion converts an lvalue to the value stored. It makes no
    sense for the result to have any qualifiers -- they are anything but
    critical for pure values.

    --
    Ben.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris Vine@21:1/5 to Juha Nieminen on Sat Oct 23 21:30:39 2021
    On Fri, 22 Oct 2021 04:41:47 -0000 (UTC)
    Juha Nieminen <nospam@thanks.invalid> wrote:
    So yes, 'const' can actually make the program more efficient (especially
    in C++, where it guarantees to the compiler that it can assume the
    contents won't change).

    Since this thread is entitled "I think references should have been
    const by default", it may be worth mentioning that holding a const
    reference to an object does not mean that the compiler "can assume the
    contents won't change". It guarantees that, in the absence of a const
    cast, non-mutable non-static data won't be modified through the
    reference. If the object concerned is a lvalue it says nothing about
    what might be done to the object's non-mutable data through its
    variable name (assuming that is non-const) or by some other non-const reference. It also says nothing about the mutability of the object's
    static data (if any).

    I say this in case it is used to put forward the incorrect notion that
    "const" means "thread safe", which I have occasionally seen propagated
    by the ill-informed.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Keith Thompson@21:1/5 to Bart on Sat Oct 23 15:07:16 2021
    Bart <bc@freeuk.com> writes:
    On 23/10/2021 13:23, Ben Bacarisse wrote:
    David Brown <david.brown@hesbynett.no> writes:

    I think it is odd, however, that you can have qualified types in the
    generic association list, since they can't ever match anything
    (AFAICS).
    I was curious so I tried this:
    #include <stdio.h>
    struct S { const int i; } s;
    struct S f(void) { return s; }
    int main(void)
    {
    const char *t =
    _Generic(f().i,
    int: "int",
    const int: "const int");
    puts(t);
    }
    f().i is not a lvalue and has a const-qualified type. gcc prints
    "int",
    but clang prints "const int". I think clang is right here. (So much
    for "they all copy gcc"!)

    You need to file a bug report to Clang's developers so that they can
    fix that oversight!

    But, why do think Clang is wrong? The type has a top-level const qualifier.

    I don't get why this is only removed for an lvalue (where you'd think
    that a const attribute is more critical).

    The removal of type qualifiers is part of lvalue conversion. No lvalue,
    no lvalue conversion.

    I can see that it would make sense for the expression `f().i` to have
    type int rather than const int, but the standard doesn't say so.

    --
    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 */

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Ben Bacarisse on Sat Oct 23 21:12:35 2021
    Ben Bacarisse <ben.usenet@bsb.me.uk> writes:

    [...]

    I was curious so I tried this:

    #include <stdio.h>

    struct S { const int i; } s;
    struct S f(void) { return s; }

    int main(void)
    {
    const char *t =
    _Generic(f().i,
    int: "int",
    const int: "const int");
    puts(t);
    }

    f().i is not a lvalue and has a const-qualified type. gcc prints "int",
    but clang prints "const int". I think clang is right here. (So much
    for "they all copy gcc"!)

    I have looked into this question and just now posted in comp.std.c
    giving the results of my investigation.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Keith Thompson on Sat Oct 23 21:15:29 2021
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    Bart <bc@freeuk.com> writes:

    On 23/10/2021 13:23, Ben Bacarisse wrote:

    David Brown <david.brown@hesbynett.no> writes:

    I think it is odd, however, that you can have qualified types in the
    generic association list, since they can't ever match anything
    (AFAICS).

    I was curious so I tried this:
    #include <stdio.h>
    struct S { const int i; } s;
    struct S f(void) { return s; }
    int main(void)
    {
    const char *t =
    _Generic(f().i,
    int: "int",
    const int: "const int");
    puts(t);
    }
    f().i is not a lvalue and has a const-qualified type. gcc prints
    "int",
    but clang prints "const int". I think clang is right here. (So much
    for "they all copy gcc"!)

    You need to file a bug report to Clang's developers so that they can
    fix that oversight!

    But, why do think Clang is wrong? The type has a top-level const qualifier. >>
    I don't get why this is only removed for an lvalue (where you'd think
    that a const attribute is more critical).

    The removal of type qualifiers is part of lvalue conversion. No lvalue,
    no lvalue conversion.

    I can see that it would make sense for the expression `f().i` to have
    type int rather than const int, but the standard doesn't say so.

    There is some confusion about that. More info in comp.std.c.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Sun Oct 24 12:11:20 2021
    On 23/10/2021 19:58, Bart wrote:
    On 23/10/2021 17:45, David Brown wrote:
    On 23/10/2021 13:06, Bart wrote:

        const int * const * x;

    If you find this kind of thing confusing, use "typedef".  It exists to >>>> improve readability (amongst other benefits).

    So, even /more/ clutter?! I's also like to see a typedefed version of my >>> example that is not harder to understand.


    typedef const int constant_integer;
    typedef constant_integer * pointer_to_constant_integer;
    typedef const pointer_to_constant_integer
    constant_pointer_to_constant_integer;
    typedef constant_pointer_to_constant_integer *
    pointer_to_constant_pointer_to_constant_integer;

    pointer_to_constant_pointer_to_constant_integer x;


    That's the order you prefer, is it not?  (I'm not suggesting it's a good
    way to write it, I'm merely showing you how it could be done with a
    choice of names that might suit your liking.)


    Maybe you want it more compact:

    typedef const int * p_cint;
    typedef const p_cint * p_cp_cint;
    p_cp_cint x;


    Well, I was right, the alternatives are worse.

    If you are interested in the actual type, or the 'shape' of that type,
    devoid of qualifiers, then you don't want all that. You want to know the
    type is 'int**'.


    This is what someone might expect of an immutable parameter.

    There are occasions when it is more convenient to cast away const, or
    where it is hard to maintain full const correctness.  "const" does not
    absolve the programmer of having to think.  But it does make a lot of
    code clearer and easier to understand.

    Somebody writes an informal library but doesn't bother to mark with
    'const' those functions that take char* that don't happen to modify the string:

        void f1(char*);
        void f2(char*);
        void f3(char*);

    Now someone who has a mania for 'const' wants to use it:

        const char* s="ABC";
        f1(s);

    However, it doesn't work. They will know from the specs that f1 doesn't
    write into the string, but the compiler doesn't know that.

    Now, it starts to get messy. Either casts have to be inserted, or the
    library needs to be heavily revised. Then that library may import
    another which is also missing consts. And so const-poisoning infects the whole code-base.

    At some point, it will also stop you doing things legally, and you have
    to start using casts. Now, you are starting to fight the language.

    Was it Pascal or Ada that first had those in/out parameter attributes?

    I can write this [in my syntax]:

        proc f1(ichar s) = {}              # anything goes
        proc f2(ichar in s) = {}           # s is input to the function
        proc f3(ichar out s) = {}          # s is output from the function

    I don't do anything with these at the minute (I think 'out' and 'inout',
    not shown, are just aliases for '&') but they can do a lot just as annotations.


    People can write code in a lazy way (or perhaps an old way), and this
    can cause some inconveniences in using it along with code written in
    newer and better ways. That's true in general - it is not special for
    C, nor special for "const" in C.

    Your solution to C's const problem (as you see it), is to have a
    language where you can distinguish between pointers which cannot be used
    to change the data, and pointers which /can/ be used to change the data.
    As long as people use these correctly in your language, or Pascal, or
    Ada, everything works correctly.

    That's fine - and a good idea.

    It is /exactly/ the same as is done in C. "void f1(char *)" is like an
    "inout" parameter, and "void f2(const char *)" is like an "in" parameter.

    Using "char *" as a parameter in C when it is read-only data is exactly
    like using "inout" parameters in Ada or Pascal, or "ichar s" in your
    language - it is lazy, unhelpful, and it stops people using it for
    constant data (without extra effort).

    Therefore, I simply don't understand what you have against "const" in C
    - your own language is almost identical except for minor syntax differences.

    (You do have a syntax for saying the pointer is used only for writing,
    not for reading, which standard C is missing.)

    At some point an implementation can enforce them and ensure that an 'in'
    data structure is not modified in the function, even one that has
    mutable components. The programmer doesn't need to micro-manage every
    level of the type structure, or have to think about exactly how
    foolproof those 'const' attributes are.

    It should be like a write-protect switch on the whole caboodle.

    (At least, within the bounds of what the language can help with. A data structure may contain references to external data, such as files, disks, images, which can all be modifible, or they can be altered via another
    path to the original data.

    But C's const doesn't prevent that either.)


    Yes, "const" has its limitations - the same ones as you have in your
    language. Programming languages /always/ have compromises.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Jorgen Grahn@21:1/5 to Juha Nieminen on Sun Oct 24 14:20:04 2021
    On Thu, 2021-10-21, Juha Nieminen wrote:
    Time and again I see beginner C++ programmers make the same mistake:
    Make functions take objects by non-const reference, even when (in the
    vast, vast majority of cases) the function doesn't modify those objects.

    I don't often see that mistake. If they do it, surely they must have
    learned from really lousy sources, and do other stupid things, too?

    It's important and easy for an author or a teacher to describe the
    common cases of parameter passing:

    void foo(Bar bar);
    void foo(const Bar& bar);

    And the more exotic ones:

    void foo(Bar& bar);
    void foo(Bar&& bar);
    void foo(Bar* bar);
    void foo(const Bar* bar);
    void foo(std::unique_ptr<Bar> bar);
    // and some I missed I guess

    /Jorgen

    --
    // Jorgen Grahn <grahn@ Oo o. . .
    \X/ snipabacken.se> O o .

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Sun Oct 24 23:11:38 2021
    On 24/10/2021 11:11, David Brown wrote:
    On 23/10/2021 19:58, Bart wrote:

    I don't do anything with these at the minute (I think 'out' and 'inout',
    not shown, are just aliases for '&') but they can do a lot just as
    annotations.


    People can write code in a lazy way (or perhaps an old way), and this
    can cause some inconveniences in using it along with code written in
    newer and better ways. That's true in general - it is not special for
    C, nor special for "const" in C.

    Your solution to C's const problem (as you see it), is to have a
    language where you can distinguish between pointers which cannot be used
    to change the data, and pointers which /can/ be used to change the data.
    As long as people use these correctly in your language, or Pascal, or
    Ada, everything works correctly.

    That's fine - and a good idea.

    It is /exactly/ the same as is done in C. "void f1(char *)" is like an "inout" parameter, and "void f2(const char *)" is like an "in" parameter.

    Using "char *" as a parameter in C when it is read-only data is exactly
    like using "inout" parameters in Ada or Pascal, or "ichar s" in your
    language - it is lazy, unhelpful, and it stops people using it for
    constant data (without extra effort).

    Therefore, I simply don't understand what you have against "const" in C
    - your own language is almost identical except for minor syntax differences.

    I've played around with C-style readonly type attributes in the past. I
    didnt't like them. It disrupts the type system (see the fuss about how
    _Generic should work) and it didn't give the necessary protection.

    'const' in C only affects one level of a complex type. It doesn't
    affects those parts not specified (like the members of a struct type, or
    rather those values at the other side of an embedded pointer type, which
    is not itself const).

    I want 'readonly' to protect an entire data structure, especially one
    that is otherwise mutable that is passed to a function, by only
    specifying one thing.

    Now, I haven't fully implemented such an attribute (I've only reserved
    syntax like 'let' and 'in'), I just know how I'd like it to work.

    That is, propagate down deep into the data structure. I think that is
    possible. I don't think it would be part of the type system; it's likely
    to be a property of an expression.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?B?w5bDtiBUaWli?=@21:1/5 to Bart on Sun Oct 24 16:18:21 2021
    On Monday, 25 October 2021 at 01:11:57 UTC+3, Bart wrote:
    On 24/10/2021 11:11, David Brown wrote:
    On 23/10/2021 19:58, Bart wrote:

    I don't do anything with these at the minute (I think 'out' and 'inout', >> not shown, are just aliases for '&') but they can do a lot just as
    annotations.


    People can write code in a lazy way (or perhaps an old way), and this
    can cause some inconveniences in using it along with code written in
    newer and better ways. That's true in general - it is not special for
    C, nor special for "const" in C.

    Your solution to C's const problem (as you see it), is to have a
    language where you can distinguish between pointers which cannot be used
    to change the data, and pointers which /can/ be used to change the data.
    As long as people use these correctly in your language, or Pascal, or
    Ada, everything works correctly.

    That's fine - and a good idea.

    It is /exactly/ the same as is done in C. "void f1(char *)" is like an "inout" parameter, and "void f2(const char *)" is like an "in" parameter.

    Using "char *" as a parameter in C when it is read-only data is exactly like using "inout" parameters in Ada or Pascal, or "ichar s" in your language - it is lazy, unhelpful, and it stops people using it for
    constant data (without extra effort).

    Therefore, I simply don't understand what you have against "const" in C
    - your own language is almost identical except for minor syntax differences.
    I've played around with C-style readonly type attributes in the past. I didnt't like them. It disrupts the type system (see the fuss about how _Generic should work) and it didn't give the necessary protection.

    'const' in C only affects one level of a complex type. It doesn't
    affects those parts not specified (like the members of a struct type, or rather those values at the other side of an embedded pointer type, which
    is not itself const).

    I want 'readonly' to protect an entire data structure, especially one
    that is otherwise mutable that is passed to a function, by only
    specifying one thing.

    Now, I haven't fully implemented such an attribute (I've only reserved
    syntax like 'let' and 'in'), I just know how I'd like it to work.

    Can it be that you haven't implemented it because it is what you would
    like to want but do not always want?

    That is, propagate down deep into the data structure. I think that is possible. I don't think it would be part of the type system; it's likely
    to be a property of an expression.

    In most software I have seen pointers in object often point at other
    objects that are not logically components of said object. So the
    pointers often do not go to "down deep" but entirely elsewhere.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to All on Mon Oct 25 00:58:25 2021
    On 25/10/2021 00:18, Öö Tiib wrote:
    On Monday, 25 October 2021 at 01:11:57 UTC+3, Bart wrote:

    'const' in C only affects one level of a complex type. It doesn't
    affects those parts not specified (like the members of a struct type, or
    rather those values at the other side of an embedded pointer type, which
    is not itself const).

    I want 'readonly' to protect an entire data structure, especially one
    that is otherwise mutable that is passed to a function, by only
    specifying one thing.

    Now, I haven't fully implemented such an attribute (I've only reserved
    syntax like 'let' and 'in'), I just know how I'd like it to work.

    Can it be that you haven't implemented it because it is what you would
    like to want but do not always want?

    Partly because I've classed it as low priority; this would not allow me
    to do anything new, just apply extra restrictions!

    However it is something interesting to explore.

    That is, propagate down deep into the data structure. I think that is
    possible. I don't think it would be part of the type system; it's likely
    to be a property of an expression.

    In most software I have seen pointers in object often point at other
    objects that are not logically components of said object. So the
    pointers often do not go to "down deep" but entirely elsewhere.

    Determining the boundaries of a data structure, beyond which
    write-protection shouldn't apply or can't be applied, would be one of
    the problems to look at.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to Bart on Mon Oct 25 05:00:35 2021
    Bart <bc@freeuk.com> wrote:
    Neither does the syntax make it that obvious which bit of the type is
    refered to, as in:

    const int * const * x;

    Actually the syntax *does* make it obvious. You are just reading the type declaration in the wrong direction. Pointer variable declarations should
    be read from right to left (this is a simple but non-obvious trick that surprisingly few programmers know.) In your example, when we read the declaration from right to left, it becomes:

    "x is a pointer to a const pointer that points to an int that's const".

    Or, if you want to be a bit clearer:

    "x is a pointer to a (const pointer) that points to an int, the int
    itself being const".

    (In other words, x itself is not const and can be modified, but it
    points to a const pointer, ie. *x cannot be modified, and this
    const pointer is pointing to a const int, ie. **x cannot be modified
    either.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to Chris Vine on Mon Oct 25 04:51:35 2021
    Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
    I say this in case it is used to put forward the incorrect notion that "const" means "thread safe", which I have occasionally seen propagated
    by the ill-informed.

    "const means thread-safe" is not said in the context of const references,
    but in the context of const member functions, which is a completely
    different thing.

    (And, in this case, the idea is "const member functions *should be* re-entrant", rather than "const member functions are thread-safe".)

    And when I said "const can make the program more efficient" I'm
    referring to compile-time literals. Especially ones in a const
    array. (When the compiler sees the definition of a const array
    full of compile-time literals, it can assume that the contents
    of the array will never change, and can start taking values from
    it at compile time if it's able to. It doesn't need to assume
    that the values may change.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From RacingRabbit@watershipdown.co.uk@21:1/5 to David Brown on Mon Oct 25 08:21:06 2021
    On Sat, 23 Oct 2021 18:45:22 +0200
    David Brown <david.brown@hesbynett.no> wrote:
    On 23/10/2021 13:06, Bart wrote:
    So, even /more/ clutter?! I's also like to see a typedefed version of my
    example that is not harder to understand.


    typedef const int constant_integer;
    typedef constant_integer * pointer_to_constant_integer;
    typedef const pointer_to_constant_integer >constant_pointer_to_constant_integer;
    typedef constant_pointer_to_constant_integer * >pointer_to_constant_pointer_to_constant_integer;

    pointer_to_constant_pointer_to_constant_integer x;

    Consts in C are pointless because it doesn't have references and its rather difficult to "accidentaly" dereference a pointer to update the value its pointing to.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to RacingRabbit@watershipdown.co.uk on Mon Oct 25 09:47:50 2021
    RacingRabbit@watershipdown.co.uk wrote:
    Consts in C are pointless because it doesn't have references and its rather difficult to "accidentaly" dereference a pointer to update the value its pointing to.

    Actually it isn't. Very old-style C code, mostly prior to the C89 standard,
    but you can see even modern examples sometimes (for some old-school C coders habits die hard), often used non-const pointers to char as "strings".
    In fact, I think even the K&R famous book as examples with non-const char*'s being initialized to point to string literals.

    The problem with this is that it can be too easy to accidentally try to
    modify the contents of the "string" through that pointer. If you are
    accustomed to never using 'const' when dealing with char*'s, you'll
    probably pay little attention to the fact that some function somewhere
    is taking a non-const char* as parameter, and you might at some point
    call it with a pointer that's pointing to a string literal. If said
    function does modify the "string" it's getting as parameter, that's UB.

    Most modern C compilers will give you a warning if you try to assign
    a const char* (eg. a string literal) to a non-const char* (or give
    one to a function taking a non-const char*), but if you were
    determined to never use 'const' and turn off such warnings, such
    mistakes are not extraordinarily unlikely.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bo Persson@21:1/5 to Juha Nieminen on Mon Oct 25 12:33:37 2021
    On 2021-10-25 at 11:47, Juha Nieminen wrote:
    RacingRabbit@watershipdown.co.uk wrote:
    Consts in C are pointless because it doesn't have references and its rather >> difficult to "accidentaly" dereference a pointer to update the value its
    pointing to.

    Actually it isn't. Very old-style C code, mostly prior to the C89 standard, but you can see even modern examples sometimes (for some old-school C coders habits die hard), often used non-const pointers to char as "strings".
    In fact, I think even the K&R famous book as examples with non-const char*'s being initialized to point to string literals.


    In defense of K&R. :-)

    They didn't have const in original C. It was Bjarne who first added it
    to C++, and only later did C also adopt the keyword.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Juha Nieminen on Mon Oct 25 12:13:06 2021
    On 25/10/2021 06:00, Juha Nieminen wrote:
    Bart <bc@freeuk.com> wrote:
    Neither does the syntax make it that obvious which bit of the type is
    refered to, as in:

    const int * const * x;

    Actually the syntax *does* make it obvious. You are just reading the type declaration in the wrong direction. Pointer variable declarations should
    be read from right to left (this is a simple but non-obvious trick that surprisingly few programmers know.) In your example, when we read the declaration from right to left, it becomes:

    "x is a pointer to a const pointer that points to an int that's const".

    Or, if you want to be a bit clearer:

    "x is a pointer to a (const pointer) that points to an int, the int
    itself being const".

    (In other words, x itself is not const and can be modified, but it
    points to a const pointer, ie. *x cannot be modified, and this
    const pointer is pointing to a const int, ie. **x cannot be modified
    either.)


    If only it was that simple to read declarations!

    Ones such as int** can work by going from right to left, but in general
    it is inside out.

    I noticed you deftly bypassed the fact that 'const' for 'int' can be
    written either side of 'int', or both!

    At least this example helps highlight which of those ** comes first.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From RacingRabbit@watershipdown.co.uk@21:1/5 to James Kuyper on Mon Oct 25 14:39:19 2021
    On Mon, 25 Oct 2021 10:30:04 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/25/21 4:21 AM, RacingRabbit@watershipdown.co.uk wrote:
    ....
    Consts in C are pointless because it doesn't have references and its rather >> difficult to "accidentaly" dereference a pointer to update the value its
    pointing to.

    Actually, it isn't. All it takes is unfamiliarity with the functions
    you're using. I remember, in particular, I've seen messages from several >people expressing surprise that strtok() writes to the string that you
    pass it as it's first argument. If the pointers they had tried to pass

    Those are the sorts of people who should stick to python or javascript.

    However, that's only a part of the problem that "const" is intended to
    help avoid. The other part is intentionally dereferencing a pointer to
    update the value it's pointing act, due to being unaware of the fact
    that what it's pointing at is something that shouldn't be written to. In

    They'll soon find out if they try to write to it.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to RacingRabbit@watershipdown.co.uk on Mon Oct 25 10:30:04 2021
    On 10/25/21 4:21 AM, RacingRabbit@watershipdown.co.uk wrote:
    ...
    Consts in C are pointless because it doesn't have references and its rather difficult to "accidentaly" dereference a pointer to update the value its pointing to.

    Actually, it isn't. All it takes is unfamiliarity with the functions
    you're using. I remember, in particular, I've seen messages from several
    people expressing surprise that strtok() writes to the string that you
    pass it as it's first argument. If the pointers they had tried to pass
    to strtok() had been const char * rather than char*, they would have
    been reminded of the problem. Of course, they might not have understood
    the reminder, if they weren't familiar with functions whose declarations
    use "const" appropriately. All of the standard library functions do so,
    many other libraries don't.

    However, that's only a part of the problem that "const" is intended to
    help avoid. The other part is intentionally dereferencing a pointer to
    update the value it's pointing act, due to being unaware of the fact
    that what it's pointing at is something that shouldn't be written to. In
    code which doesn't make proper use of "const", that's a fairly common
    mistake, at least in my experience (which is admittedly limited, since
    my own code does make proper use of "const").

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Juha Nieminen on Mon Oct 25 10:39:11 2021
    On 10/25/21 5:47 AM, Juha Nieminen wrote:
    ...
    Most modern C compilers will give you a warning if you try to assign
    a const char* (eg. a string literal) to a non-const char* (or give
    one to a function taking a non-const char*),

    They must do so on assignment; 6.5.16p2 occurs in a "Constraints" section:
    "An assignment operator shall have a modifiable lvalue as its left operand."

    And if a function prototype is in scope "... the arguments are
    implicitly converted, as if by assignment, to the types of the
    corresponding parameters ..." (6.5.2.2p7), so the same constraints apply
    there, too. For the same reason, they also apply to return statements.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From RacingRabbit@watershipdown.co.uk@21:1/5 to Juha Nieminen on Mon Oct 25 14:19:09 2021
    On Mon, 25 Oct 2021 09:47:50 -0000 (UTC)
    Juha Nieminen <nospam@thanks.invalid> wrote:
    RacingRabbit@watershipdown.co.uk wrote:
    Consts in C are pointless because it doesn't have references and its rather >> difficult to "accidentaly" dereference a pointer to update the value its
    pointing to.

    Actually it isn't. Very old-style C code, mostly prior to the C89 standard, >but you can see even modern examples sometimes (for some old-school C coders >habits die hard), often used non-const pointers to char as "strings".

    And? How do you accidentaly write *str or str[0] for example?

    In fact, I think even the K&R famous book as examples with non-const char*'s >being initialized to point to string literals.

    The concept of const didn't exist in K&R C so what would be your alternative?

    The problem with this is that it can be too easy to accidentally try to >modify the contents of the "string" through that pointer. If you are >accustomed to never using 'const' when dealing with char*'s, you'll
    probably pay little attention to the fact that some function somewhere
    is taking a non-const char* as parameter, and you might at some point
    call it with a pointer that's pointing to a string literal. If said
    function does modify the "string" it's getting as parameter, that's UB.

    No idea what UB means, but what'll happen is it'll crash immediately so you'll soon find out.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to RacingRabbit@watershipdown.co.uk on Mon Oct 25 10:59:15 2021
    On 10/25/21 10:19 AM, RacingRabbit@watershipdown.co.uk wrote:
    On Mon, 25 Oct 2021 09:47:50 -0000 (UTC)
    Juha Nieminen <nospam@thanks.invalid> wrote:
    RacingRabbit@watershipdown.co.uk wrote:
    Consts in C are pointless because it doesn't have references and its rather >>> difficult to "accidentaly" dereference a pointer to update the value its >>> pointing to.

    Actually it isn't. Very old-style C code, mostly prior to the C89 standard, >> but you can see even modern examples sometimes (for some old-school C coders >> habits die hard), often used non-const pointers to char as "strings".

    And? How do you accidentaly write *str or str[0] for example?

    It's not the *str that's accidental, its the call to a function that
    contains *str, with an argument that points to a string that shouldn't
    be written to. That is in fact a fairly easy mistake to made, and when
    people didn't use "const" properly, it's actually a fairly common one.

    ...
    In fact, I think even the K&R famous book as examples with non-const char*'s >> being initialized to point to string literals.

    The concept of const didn't exist in K&R C so what would be your alternative?

    There was no alternative, which is why that was the case. After "const"
    was added to the language, K&R 2nd edition was updated accordingly.

    ...
    call it with a pointer that's pointing to a string literal. If said
    function does modify the "string" it's getting as parameter, that's UB.

    No idea what UB means, but what'll happen is it'll crash immediately so you'll
    soon find out.

    UB means "Undefined Behavior", a technical term from the C standard
    which does NOT mean "behavior for which there is no definition". It
    means "behavior, upon use of a nonportable or erroneous program
    construct or of erroneous data, for which this document imposes no requirements" (3.4.3). Note that "this document" refers to the C
    standard; other documents (such as compiler documentation or ABI
    standards) might define the behavior, without changing the fact that is qualifies as "undefined behavior" as far as the C standard is concerned.

    A lot of people have trouble understanding how breath-takingly wide the
    scope of "imposes no requirements" is. The standard tries to make that
    clear with the following examples "Possible undefined behavior ranges
    from ignoring the situation completely with unpredictable results, to
    behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a
    diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message)."

    Note, in particular, that the most insidious form of undefined behavior
    is that your program can behave exactly the way you incorrectly thought
    it was required to behave. The reason that's dangerous is that it leaves
    you with no warning that the behavior might change when you recompile
    with a different compiler, or with different compiler options, or even
    with the same compiler options, or even if you simply run the program a
    second time, even if you give it the same inputs as the previous time.
    That's how comprehensive the phrase "no requirements" is - the undefined behavior is NOT required to be the same each time you execute the
    offending program.

    Getting back to your comment - it's not required to crash immediately -
    that would constitute a requirement. And it's actually possible, as a
    result of optimizations performed by the compiler, that it might
    actually do something quite different. In particular, one possibility is
    the attempt to write to the object might become a NOp (as indicated by
    the phrase "ignoring the situation completely").

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Manfred@21:1/5 to Juha Nieminen on Mon Oct 25 16:58:03 2021
    On 10/25/2021 7:00 AM, Juha Nieminen wrote:
    Bart <bc@freeuk.com> wrote:
    Neither does the syntax make it that obvious which bit of the type is
    refered to, as in:

    const int * const * x;

    Actually the syntax *does* make it obvious. You are just reading the type declaration in the wrong direction. Pointer variable declarations should
    be read from right to left (this is a simple but non-obvious trick that surprisingly few programmers know.) In your example, when we read the declaration from right to left, it becomes:

    "x is a pointer to a const pointer that points to an int that's const".

    Or, if you want to be a bit clearer:

    "x is a pointer to a (const pointer) that points to an int, the int
    itself being const".

    (In other words, x itself is not const and can be modified, but it
    points to a const pointer, ie. *x cannot be modified, and this
    const pointer is pointing to a const int, ie. **x cannot be modified
    either.)


    There's still the point that the C standard describes "type qualifiers"
    both in the context of "declaration-specifiers" and "declarators", and,
    in the first case, it says that "type specifiers" (e.g. 'int') and "type qualifiers" (like 'const') may appear "in any order".
    This flexibility is handy in simple declarations, but may be seen as
    less consistent in case of multiple levels of indirection.
    In the case of pointer "declarators", on the other hand, "type
    qualifiers", if any, always occur /after/ their respective '*'.

    All of this makes sense, after you pay the necessary attention, and it
    allows to specify the desired qualifiers for each level of indirection,
    which is a valuable feature. I'd say this is one of the cases where
    flexibility comes at a price, which, in this case, is worth its value.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to RacingRabbit@watershipdown.co.uk on Mon Oct 25 11:05:30 2021
    On 10/25/21 10:39 AM, RacingRabbit@watershipdown.co.uk wrote:
    On Mon, 25 Oct 2021 10:30:04 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    ...
    However, that's only a part of the problem that "const" is intended to
    help avoid. The other part is intentionally dereferencing a pointer to
    update the value it's pointing act, due to being unaware of the fact
    that what it's pointing at is something that shouldn't be written to. In

    They'll soon find out if they try to write to it.

    Not necessarily - the fact that the behavior is undefined gives
    implementations the freedom to implement such code anyway they want,
    including ways that can be quite hard to recognize as errors - even
    though they are.
    Back when I was first converting a lot of other people's K&R C code to
    make use of the new features of C90, I frequently found errors like that
    which had been masked for years - the errors were quite capable of
    causing serious problems, but for one reason or the other, they had
    failed to do frequently enough for the problem to be successfully
    tracked down. Most of that code ran much more reliably after I finished converting it.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From RacingRabbit@watershipdown.co.uk@21:1/5 to James Kuyper on Mon Oct 25 16:14:30 2021
    On Mon, 25 Oct 2021 10:59:15 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/25/21 10:19 AM, RacingRabbit@watershipdown.co.uk wrote:
    call it with a pointer that's pointing to a string literal. If said
    function does modify the "string" it's getting as parameter, that's UB.

    No idea what UB means, but what'll happen is it'll crash immediately so >you'll
    soon find out.

    UB means "Undefined Behavior", a technical term from the C standard
    which does NOT mean "behavior for which there is no definition". It
    means "behavior, upon use of a nonportable or erroneous program
    construct or of erroneous data, for which this document imposes no >requirements" (3.4.3). Note that "this document" refers to the C
    standard; other documents (such as compiler documentation or ABI
    standards) might define the behavior, without changing the fact that is >qualifies as "undefined behavior" as far as the C standard is concerned.

    Any attempt to write to a read only program text area will result in a crash regardless of the language. It is implicit that its read only in C because
    C also provides the following initialisation which places the string (presumably) on the heap:

    char str[] = "hello world";

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Manfred@21:1/5 to James Kuyper on Mon Oct 25 17:56:39 2021
    On 10/25/2021 4:59 PM, James Kuyper wrote:
    On 10/25/21 10:19 AM, RacingRabbit@watershipdown.co.uk wrote:
    <snip>

    No idea what UB means, but what'll happen is it'll crash immediately so you'll
    soon find out.

    UB means "Undefined Behavior", a technical term from the C standard
    which does NOT mean "behavior for which there is no definition". It
    means "behavior, upon use of a nonportable or erroneous program
    construct or of erroneous data, for which this document imposes no requirements" (3.4.3). Note that "this document" refers to the C
    standard; other documents (such as compiler documentation or ABI
    standards) might define the behavior, without changing the fact that is qualifies as "undefined behavior" as far as the C standard is concerned.

    Thanks for the quote, it made me compare it with the definition of UB in
    the C++ standard, which simply states "behavior for which this
    International Standard imposes no requirements".

    The lack of the sentence "upon use of a nonportable or erroneous 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.


    A lot of people have trouble understanding how breath-takingly wide the
    scope of "imposes no requirements" is. The standard tries to make that
    clear with the following examples "Possible undefined behavior ranges
    from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message)."

    Note, in particular, that the most insidious form of undefined behavior
    is that your program can behave exactly the way you incorrectly thought
    it was required to behave. The reason that's dangerous is that it leaves
    you with no warning that the behavior might change when you recompile
    with a different compiler, or with different compiler options, or even
    with the same compiler options, or even if you simply run the program a second time, even if you give it the same inputs as the previous time.
    That's how comprehensive the phrase "no requirements" is - the undefined behavior is NOT required to be the same each time you execute the
    offending program.

    Getting back to your comment - it's not required to crash immediately -
    that would constitute a requirement. And it's actually possible, as a
    result of optimizations performed by the compiler, that it might
    actually do something quite different. In particular, one possibility is
    the attempt to write to the object might become a NOp (as indicated by
    the phrase "ignoring the situation completely").


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to RacingRabbit@watershipdown.co.uk on Mon Oct 25 13:14:30 2021
    On 10/25/21 12:14 PM, RacingRabbit@watershipdown.co.uk wrote:
    ...
    Any attempt to write to a read only program text area will result in a crash regardless of the language.

    Perhaps that is true at the hardware level, on some processors. However, there's also some processors which don't even have the concept of
    read-only memory, and there are fully conforming C implementation that
    can target some of those processors.

    However, I'm talking about the level of C code, not hardware. The
    translation from C code to machine code is defined only in terms of the required behavior, and when there is NO required behavior, that
    translation can get distinctly weird if you believe the mistaken idea
    that C is a "portable assembler".

    ... It is implicit that its read only in C because
    C also provides the following initialisation which places the string (presumably) on the heap:

    char str[] = "hello world";

    Such code cannot result in the string being placed in read-only memory,
    because it's perfectly legal to modify str. On the other hand, both of
    the following C declarations do allow strings to be placed in read-only
    memory, even if they occur at block scope:

    const char str[] = "Hello world!";
    char *strptr = "Good bye!";

    The first one is allowed to be placed in read-only memory because the
    object str is declared "const". The second is allowed to be placed in
    read-only memory because it's undefined behavior to write to the memory
    pointed at by by strptr, despite the fact that, in C, the string literal
    does NOT have the type const char[10], as it would in C++.

    However, just because it would be permissible for an implementation to
    place those objects in read-only memory, it's not actually required that
    they be placed there. Many implementations won't do so, especially if
    those declarations occur at block scope.

    And even if they were placed in read-only memory, writing C code that
    attempts to modify that memory need not result in machine language
    instructions being executed to attempt such a read. Because the behavior
    of such code is undefined, an implementation is free to translate such
    source code into machine code that does nothing of the kind - and this
    is, in fact, the natural result, in some contexts, of certain optimizations.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to RacingRabbit@watershipdown.co.uk on Mon Oct 25 18:19:36 2021
    On 25/10/2021 17:14, RacingRabbit@watershipdown.co.uk wrote:
    On Mon, 25 Oct 2021 10:59:15 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/25/21 10:19 AM, RacingRabbit@watershipdown.co.uk wrote:
    call it with a pointer that's pointing to a string literal. If said
    function does modify the "string" it's getting as parameter, that's UB. >>>
    No idea what UB means, but what'll happen is it'll crash immediately so
    you'll
    soon find out.

    UB means "Undefined Behavior", a technical term from the C standard
    which does NOT mean "behavior for which there is no definition". It
    means "behavior, upon use of a nonportable or erroneous program
    construct or of erroneous data, for which this document imposes no
    requirements" (3.4.3). Note that "this document" refers to the C
    standard; other documents (such as compiler documentation or ABI
    standards) might define the behavior, without changing the fact that is
    qualifies as "undefined behavior" as far as the C standard is concerned.

    Any attempt to write to a read only program text area will result in a crash regardless of the language.

    Data is only put into readonly, write-protected memory when the data
    values are already known before the program starts.

    Lots of uses of 'const' are for data not known until the program starts execution, and many of these will be reinitialised many times as they
    are declared inside blocks.

    Other uses will make take normally mutable data and make it readonly
    when passed to function.

    So using write-protected memory is not that much help.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Keith Thompson@21:1/5 to Manfred on Mon Oct 25 11:11:47 2021
    Manfred <noname@add.invalid> writes:
    On 10/25/2021 4:59 PM, James Kuyper wrote:
    On 10/25/21 10:19 AM, RacingRabbit@watershipdown.co.uk wrote:
    <snip>

    No idea what UB means, but what'll happen is it'll crash immediately so you'll
    soon find out.
    UB means "Undefined Behavior", a technical term from the C standard
    which does NOT mean "behavior for which there is no definition". It
    means "behavior, upon use of a nonportable or erroneous program
    construct or of erroneous data, for which this document imposes no
    requirements" (3.4.3). Note that "this document" refers to the C
    standard; other documents (such as compiler documentation or ABI
    standards) might define the behavior, without changing the fact that is
    qualifies as "undefined behavior" as far as the C standard is concerned.

    Thanks for the quote, it made me compare it with the definition of UB
    in the C++ standard, which simply states "behavior for which this International Standard imposes no requirements".

    The lack of the sentence "upon use of a nonportable or erroneous
    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 */

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Keith Thompson@21:1/5 to James Kuyper on Mon Oct 25 10:56:24 2021
    James Kuyper <jameskuyper@alumni.caltech.edu> writes:
    On 10/25/21 5:47 AM, Juha Nieminen wrote:
    ...
    Most modern C compilers will give you a warning if you try to assign
    a const char* (eg. a string literal) to a non-const char* (or give
    one to a function taking a non-const char*),

    They must do so on assignment; 6.5.16p2 occurs in a "Constraints" section: "An assignment operator shall have a modifiable lvalue as its left operand."

    And if a function prototype is in scope "... the arguments are
    implicitly converted, as if by assignment, to the types of the
    corresponding parameters ..." (6.5.2.2p7), so the same constraints apply there, too. For the same reason, they also apply to return statements.

    I think the example being referred to was something like:
    char *s;
    s = "hello";
    which does not require a diagnostic in C (because C string literals are
    not const). The following is recommended in C and required in C++:
    const char *s;
    s = "hello";
    but here s is still a modifiable lvalue because the "const" applies to
    what s points to, not to s itself.

    A case that would invoke the constraint in 6.5.16p2 is:
    char *const s;
    s = "hello";
    because s itself is read-only; you can't assign *anything* to it.

    --
    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 */

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Keith Thompson@21:1/5 to RacingRabbit@watershipdown.co.uk on Mon Oct 25 10:48:57 2021
    RacingRabbit@watershipdown.co.uk writes:
    On Mon, 25 Oct 2021 10:59:15 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/25/21 10:19 AM, RacingRabbit@watershipdown.co.uk wrote:
    call it with a pointer that's pointing to a string literal. If said
    function does modify the "string" it's getting as parameter, that's UB. >>>
    No idea what UB means, but what'll happen is it'll crash immediately so >>you'll
    soon find out.

    UB means "Undefined Behavior", a technical term from the C standard
    which does NOT mean "behavior for which there is no definition". It
    means "behavior, upon use of a nonportable or erroneous program
    construct or of erroneous data, for which this document imposes no >>requirements" (3.4.3). Note that "this document" refers to the C
    standard; other documents (such as compiler documentation or ABI
    standards) might define the behavior, without changing the fact that is >>qualifies as "undefined behavior" as far as the C standard is concerned.

    Any attempt to write to a read only program text area will result in a crash regardless of the language. It is implicit that its read only in C because
    C also provides the following initialisation which places the string (presumably) on the heap:

    char str[] = "hello world";

    I suggest that you would benefit more here from asking questions than
    from making assertions.

    That declaration does not place anything on the heap. The contents of
    str is placed on the stack if it appears within a function definition.
    or in the static data area if it appears outside a function definition.

    Others have addresses your errors regarding "const".

    --
    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 */

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to RacingRabbit@watershipdown.co.uk on Mon Oct 25 12:45:03 2021
    On 10/25/2021 1:21 AM, RacingRabbit@watershipdown.co.uk wrote:
    On Sat, 23 Oct 2021 18:45:22 +0200
    David Brown <david.brown@hesbynett.no> wrote:
    On 23/10/2021 13:06, Bart wrote:
    So, even /more/ clutter?! I's also like to see a typedefed version of my >>> example that is not harder to understand.


    typedef const int constant_integer;
    typedef constant_integer * pointer_to_constant_integer;
    typedef const pointer_to_constant_integer
    constant_pointer_to_constant_integer;
    typedef constant_pointer_to_constant_integer *
    pointer_to_constant_pointer_to_constant_integer;

    pointer_to_constant_pointer_to_constant_integer x;

    Consts in C are pointless because it doesn't have references and its rather difficult to "accidentaly" dereference a pointer to update the value its pointing to.


    Fwiw, when writing code in C, I tend to use the following pattern:

    struct foo
    {
    unsigned int a;
    };


    void
    foo_init(
    struct foo* const self,
    unsigned int a
    ){
    self->a = a;
    }


    int
    foo_compute(
    struct foo const* const self,
    unsigned int a
    ){
    return self->a *= a + 123;
    }


    I like to use a const pointer to self so that if I accidentally modify
    self, I will get a nice warning. Its basically a habit of mine. 'self'
    is akin to the this pointer in C++.

    Oh well... ;^)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris Vine@21:1/5 to Juha Nieminen on Tue Oct 26 00:53:26 2021
    On Mon, 25 Oct 2021 04:51:35 -0000 (UTC)
    Juha Nieminen <nospam@thanks.invalid> wrote:
    Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
    I say this in case it is used to put forward the incorrect notion that "const" means "thread safe", which I have occasionally seen propagated
    by the ill-informed.

    "const means thread-safe" is not said in the context of const references,
    but in the context of const member functions, which is a completely
    different thing.

    I think you are confused: the two go together. Where a const reference references an object, the only member functions of the object that you
    may call via that reference are const ones. const member functions are
    not thread safe in the general case. If you are suggesting otherwise
    you are wrong.

    (And, in this case, the idea is "const member functions *should be* re-entrant", rather than "const member functions are thread-safe".)

    No, I was referring to misguided suggestions as to the latter.

    And when I said "const can make the program more efficient" I'm
    referring to compile-time literals.

    That _is_ a completely different thing: compilers can certainly make assumptions about literals.

    Especially ones in a const
    array. (When the compiler sees the definition of a const array
    full of compile-time literals, it can assume that the contents
    of the array will never change, and can start taking values from
    it at compile time if it's able to. It doesn't need to assume
    that the values may change.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to James Kuyper on Tue Oct 26 05:18:35 2021
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    A lot of people have trouble understanding how breath-takingly wide the
    scope of "imposes no requirements" is. The standard tries to make that
    clear with the following examples "Possible undefined behavior ranges
    from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message)."

    No better example of "undefined behavior" causing a major problem than
    that bug in the Linux kernel discovered some years ago, where the kernel
    would deliberately dereference a null pointer (I don't remember anymore
    for what reason), and gcc saw that it was a null pointer dereference,
    which according to the C standard is undefined behavior, and since that
    allows the compiler to do with it whatever it wants, it (if I remember correctly) just optimized it away, causing the extraordinarily hard-to-find
    bug in the kernel.

    (Also, if I remember correctly, it caused quite a discussion about
    whether compilers should actually be allowed to "do whatever they want"
    with such code, or whether they should do as they are told.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to RacingRabbit@watershipdown.co.uk on Tue Oct 26 05:29:31 2021
    RacingRabbit@watershipdown.co.uk wrote:
    Any attempt to write to a read only program text area will result in a crash regardless of the language.

    There's absolutely nothing requiring C (or C++) compilers to put string literals in a read-only memory segment. They are free to put them in a
    normal read/write memory segment if they so wish.

    Nothing guarantees that the target architecture even *has* such a thing
    as "read-only memory segments".

    This means that your program may well work "correctly" in one target architecture but not in another.

    It is implicit that its read only in C because
    C also provides the following initialisation which places the string (presumably) on the heap:

    char str[] = "hello world";

    It cannot place it on the heap because that would just be a memory leak
    (there would be nothing freeing it). It would allocate that array on
    the stack, if it's inside a function (and if it's at the global scope, whichever segment is dedicated to those).

    And that string literal there, if it actually gets generated into the
    final binary, will still be in read-only memory (if the architecture
    supports such a thing). It's just that its contents are copied to the
    array when the array is allocated on the stack.

    (Btw, this is the reason why I say that C as "strings", rather than
    strings. They are just char arrays, with a zero byte as an element
    that by convention indicates the final character. This causes a
    lot of confusion, especially since it induces many people to
    think that a char* is a "string". Which it isn't. It's a pointer
    to a value of type char. It *might* point to a null-terminated
    char array, or it might not. It's not guaranteed that it's a
    "string".)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to Bart on Tue Oct 26 05:36:13 2021
    Bart <bc@freeuk.com> wrote:
    I noticed you deftly bypassed the fact that 'const' for 'int' can be
    written either side of 'int', or both!

    That's not really a problem in the right-to-left reading.

    int const *ptr;

    can be read as:

    "ptr is a pointer to a (const int)."

    and:

    const int *ptr;

    can be read as:

    "ptr is a pointer to an int that's const (ie. a const int)."

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Juha Nieminen on Tue Oct 26 09:07:30 2021
    On 26/10/2021 07:18, Juha Nieminen wrote:
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    A lot of people have trouble understanding how breath-takingly wide the
    scope of "imposes no requirements" is. The standard tries to make that
    clear with the following examples "Possible undefined behavior ranges
    from ignoring the situation completely with unpredictable results, to
    behaving during translation or program execution in a documented manner
    characteristic of the environment (with or without the issuance of a
    diagnostic message), to terminating a translation or execution (with the
    issuance of a diagnostic message)."

    No better example of "undefined behavior" causing a major problem than
    that bug in the Linux kernel discovered some years ago, where the kernel would deliberately dereference a null pointer (I don't remember anymore
    for what reason), and gcc saw that it was a null pointer dereference,
    which according to the C standard is undefined behavior, and since that allows the compiler to do with it whatever it wants, it (if I remember correctly) just optimized it away, causing the extraordinarily hard-to-find bug in the kernel.


    The compiler did not cause a bug in the kernel. There was a bug in the
    source code - the programmer got the order of the code wrong, and
    checked the pointer after using it. This was a simple mistake in the
    code, and should have been spotted by the reviewer - it was an
    embarrasing failure in the development chain of the kernel. (The review
    and moderation process in the kernel development usually maintains very
    high standards.)

    The new optimisation in gcc did not /cause/ the bug, it merely changed
    the /consequences/ of the bug. The optimisation was entirely valid.

    It is, however, also reasonable for a project like an OS kernel to
    accept that there is a risk of human error leading to bugs in the code,
    and want to reduce the consequences that might result from such bugs.

    But we can learn from our mistakes - the kernel gained the feature of
    having a memory page at address zero mapped with no access, so that any
    later attempt to dereference a null pointer would be caught. At that
    point, -fdelete-null-pointer-checks can (and should) be re-enabled,
    along with the warning "-Wnull-derefence" that was also added as a
    consequence of this issue.

    (Also, if I remember correctly, it caused quite a discussion about
    whether compilers should actually be allowed to "do whatever they want"
    with such code, or whether they should do as they are told.)


    The compiler /did/ do as it was told. It was not told to do what the programmer wanted to tell it.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Juha Nieminen on Tue Oct 26 08:53:49 2021
    On 26/10/2021 07:29, Juha Nieminen wrote:
    RacingRabbit@watershipdown.co.uk wrote:
    Any attempt to write to a read only program text area will result in a crash >> regardless of the language.

    There's absolutely nothing requiring C (or C++) compilers to put string literals in a read-only memory segment. They are free to put them in a
    normal read/write memory segment if they so wish.

    Nothing guarantees that the target architecture even *has* such a thing
    as "read-only memory segments".

    This means that your program may well work "correctly" in one target architecture but not in another.

    There is also nothing to guarantee that attempting to write to read-only
    memory will result in a "crash". It could result in nothing happening
    at all (the write being ignored), or a hang, or a reset of the entire
    system, or a write to somewhere different in memory. (I've worked with
    systems with all four such behaviours to at least some extent.)

    A particular /OS/ might guarantee that attempting to write to read-only
    memory segments results in a particular handling of the process, but it
    is certainly not guaranteed by C or C++.

    And of course, the C compiler might not actually attempt to make the
    write, but act as though it had. (I think that would be unlikely in
    practice, but it could be done for strings local to a function.)


    It is implicit that its read only in C because
    C also provides the following initialisation which places the string
    (presumably) on the heap:

    char str[] = "hello world";

    It cannot place it on the heap because that would just be a memory leak (there would be nothing freeing it). It would allocate that array on
    the stack, if it's inside a function (and if it's at the global scope, whichever segment is dedicated to those).


    (Hypothetically, it /could/ be allocated on the heap, or elsewhere, if
    the compiler also generated code to free it appropriately. While almost
    all C implementations use a stack for local data, there are a few
    exceptions.)

    And that string literal there, if it actually gets generated into the
    final binary, will still be in read-only memory (if the architecture
    supports such a thing). It's just that its contents are copied to the
    array when the array is allocated on the stack.

    (Btw, this is the reason why I say that C as "strings", rather than
    strings. They are just char arrays, with a zero byte as an element
    that by convention indicates the final character. This causes a
    lot of confusion, especially since it induces many people to
    think that a char* is a "string". Which it isn't. It's a pointer
    to a value of type char. It *might* point to a null-terminated
    char array, or it might not. It's not guaranteed that it's a
    "string".)


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to RacingRabbit@watershipdown.co.uk on Tue Oct 26 08:37:13 2021
    RacingRabbit@watershipdown.co.uk wrote:
    Not really. They're just trying to make a case for const being useful in C. I've yet to see that.

    It can catch errors where you accidentally try to modify the contents of,
    for example, a string literal.

    (This doesn't mean that you do like
    char* str = "hello"; str[0] = 'H';
    but it does mean that you might do like
    doSomething("hello");
    where that doSomething() actually modifies the data behind the pointer
    it's given.)

    It can also make code more efficient.

    What more do you need?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From RacingRabbit@watershipdown.co.uk@21:1/5 to James Kuyper on Tue Oct 26 08:18:56 2021
    On Mon, 25 Oct 2021 13:14:30 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/25/21 12:14 PM, RacingRabbit@watershipdown.co.uk wrote:
    ....
    Any attempt to write to a read only program text area will result in a crash >> regardless of the language.

    Perhaps that is true at the hardware level, on some processors. However, >there's also some processors which don't even have the concept of
    read-only memory, and there are fully conforming C implementation that
    can target some of those processors.

    However, I'm talking about the level of C code, not hardware. The
    translation from C code to machine code is defined only in terms of the >required behavior, and when there is NO required behavior, that
    translation can get distinctly weird if you believe the mistaken idea
    that C is a "portable assembler".

    ... It is implicit that its read only in C because
    C also provides the following initialisation which places the string
    (presumably) on the heap:

    char str[] = "hello world";

    Such code cannot result in the string being placed in read-only memory, >because it's perfectly legal to modify str. On the other hand, both of

    Yes, that was my point. [] means modifyable, * means read only in every
    C implementation I've ever used.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From RacingRabbit@watershipdown.co.uk@21:1/5 to Juha Nieminen on Tue Oct 26 08:23:16 2021
    On Tue, 26 Oct 2021 05:29:31 -0000 (UTC)
    Juha Nieminen <nospam@thanks.invalid> wrote:
    RacingRabbit@watershipdown.co.uk wrote:
    It is implicit that its read only in C because
    C also provides the following initialisation which places the string
    (presumably) on the heap:

    char str[] = "hello world";

    It cannot place it on the heap because that would just be a memory leak >(there would be nothing freeing it). It would allocate that array on

    It wouldn't need to be free'd if it existed for the lifetime of the program.

    strings. They are just char arrays, with a zero byte as an element
    that by convention indicates the final character. This causes a

    Wow, really? Who knew!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From RacingRabbit@watershipdown.co.uk@21:1/5 to Keith Thompson on Tue Oct 26 08:21:05 2021
    On Mon, 25 Oct 2021 10:48:57 -0700
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote: >RacingRabbit@watershipdown.co.uk writes:
    Any attempt to write to a read only program text area will result in a crash >> regardless of the language. It is implicit that its read only in C because >> C also provides the following initialisation which places the string
    (presumably) on the heap:

    char str[] = "hello world";

    I suggest that you would benefit more here from asking questions than
    from making assertions.

    I suggest you ease up on being patronising.

    That declaration does not place anything on the heap. The contents of
    str is placed on the stack if it appears within a function definition.
    or in the static data area if it appears outside a function definition.

    Wherever its placed, the point is its modifyable unlike *str = which isn't.

    Others have addresses your errors regarding "const".

    Not really. They're just trying to make a case for const being useful in C. I've yet to see that.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to RacingRabbit@watershipdown.co.uk on Tue Oct 26 08:40:28 2021
    RacingRabbit@watershipdown.co.uk wrote:
    strings. They are just char arrays, with a zero byte as an element
    that by convention indicates the final character. This causes a

    Wow, really? Who knew!

    A lot of beginner C programmers don't.

    And some not-so-beginner C programmers either. (Well, they do tend to know about the trailing-zero-byte thing, but otherwise they may have a
    surprisingly poor grasp of what a "string" in C actually is, and may
    even think that a char* is a "string" (which it most definitely is not).)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From RacingRabbit@watershipdown.co.uk@21:1/5 to Juha Nieminen on Tue Oct 26 09:02:04 2021
    On Tue, 26 Oct 2021 08:37:13 -0000 (UTC)
    Juha Nieminen <nospam@thanks.invalid> wrote:
    RacingRabbit@watershipdown.co.uk wrote:
    Not really. They're just trying to make a case for const being useful in C. >> I've yet to see that.

    It can catch errors where you accidentally try to modify the contents of,
    for example, a string literal.

    (This doesn't mean that you do like
    char* str = "hello"; str[0] = 'H';
    but it does mean that you might do like
    doSomething("hello");
    where that doSomething() actually modifies the data behind the pointer
    it's given.)

    fenris$ cat t.c
    #include <stdio.h>

    void func(char *str)
    {
    str[0] = 0;
    }


    int main()
    {
    char *str = "hello";
    func(str);
    puts("Worked");
    return 0;
    }
    fenris$ cc t.c
    fenris$ a.out
    Bus error: 10
    fenris$

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to RacingRabbit@watershipdown.co.uk on Tue Oct 26 11:14:58 2021
    RacingRabbit@watershipdown.co.uk wrote:
    fenris$ a.out
    Bus error: 10

    For starters, that's in no way guaranteed to happen. Learn standard C.

    Secondly, if you think that a runtime diagnostic is as good as a compile-time diagnostic, then you have still a LOT to learn about software development.
    The earlier in the development process that a bug can be caught, the better. This is basic software development 101.

    The writing-to-a-string-literal might happen only in some cases, not always. For example, it could depend on the particular contents of some of input
    file, or a particular action by the user, a particular command line
    parameter, or a myriad of other things that can vary from execution to execution. In the worst case scenarios the error may happen sporadically
    and without a clear pattern, which can make extraordinarily difficult to
    debug. Counless hours could be spent in trying to find such an elusive
    and obscure bug.

    All of which could have been avoided if you just used 'const' and
    turned on compiler warnings, and paid attention to them.

    There's literally zero reason not to use 'const' for pointers that
    are not intended to be used to modify the values they are pointing to.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to RacingRabbit@watershipdown.co.uk on Tue Oct 26 12:39:54 2021
    On 26/10/2021 09:18, RacingRabbit@watershipdown.co.uk wrote:
    On Mon, 25 Oct 2021 13:14:30 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/25/21 12:14 PM, RacingRabbit@watershipdown.co.uk wrote:
    ....
    Any attempt to write to a read only program text area will result in a crash
    regardless of the language.

    Perhaps that is true at the hardware level, on some processors. However,
    there's also some processors which don't even have the concept of
    read-only memory, and there are fully conforming C implementation that
    can target some of those processors.

    However, I'm talking about the level of C code, not hardware. The
    translation from C code to machine code is defined only in terms of the
    required behavior, and when there is NO required behavior, that
    translation can get distinctly weird if you believe the mistaken idea
    that C is a "portable assembler".

    ... It is implicit that its read only in C because
    C also provides the following initialisation which places the string
    (presumably) on the heap:

    char str[] = "hello world";

    Such code cannot result in the string being placed in read-only memory,
    because it's perfectly legal to modify str. On the other hand, both of

    Yes, that was my point. [] means modifyable, * means read only in every
    C implementation I've ever used.



    You've misunderstood then.

    But * and [] types are modifable:

    char* s = "ABC";
    puts(s);
    *s = 'Z';

    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
    shared across the program, it will change the value of "ABC" everywhere.

    This is on those implementations that don't put ABC into readonly memory
    (eg. tcc, bcc, DMC, lcc, msvc). Ones like gcc and clang will crash.

    You can't compare that with this:

    char t[] = "ABC";
    puts(t)
    t[0] = 'Z';

    Here, "ABC" is left unmolested. But the reason is because the
    initialisation /copies/ the literal string to the array. So it modifies
    a copy. The declaration of s directly points it to the literal.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ben Bacarisse@21:1/5 to Bart on Tue Oct 26 14:29:22 2021
    Bart <bc@freeuk.com> writes:

    On 26/10/2021 09:18, RacingRabbit@watershipdown.co.uk wrote:
    On Mon, 25 Oct 2021 13:14:30 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/25/21 12:14 PM, RacingRabbit@watershipdown.co.uk wrote:
    ....
    Any attempt to write to a read only program text area will result in a crash
    regardless of the language.

    Perhaps that is true at the hardware level, on some processors. However, >>> there's also some processors which don't even have the concept of
    read-only memory, and there are fully conforming C implementation that
    can target some of those processors.

    However, I'm talking about the level of C code, not hardware. The
    translation from C code to machine code is defined only in terms of the
    required behavior, and when there is NO required behavior, that
    translation can get distinctly weird if you believe the mistaken idea
    that C is a "portable assembler".

    ... It is implicit that its read only in C because
    C also provides the following initialisation which places the string
    (presumably) on the heap:

    char str[] = "hello world";

    Such code cannot result in the string being placed in read-only memory,
    because it's perfectly legal to modify str. On the other hand, both of
    Yes, that was my point. [] means modifyable, * means read only in every
    C implementation I've ever used.

    You've misunderstood then.

    Yes, RR has misunderstood (or is expressing the point in a confusing
    way).

    But * and [] types are modifable:

    And this is bad wording. Some objects with pointer type are modifiable
    and some are not. No objects with array types are modifiable. But in
    fact you seem to be referring to the /target/ of pointer types (again,
    some of which are modifiable and some are not) and to array /elements/
    about which the same is also true. There is no general rule about "*
    and [] types".

    char* s = "ABC";

    This relies on an a conversion that is valid (bad unwise) in C and not permitted in C++.

    puts(s);
    *s = 'Z';

    This is undefined behaviour in both C and C++. The target of the
    assignment (the first character of the string) is not a modifiable
    object.

    This shows ABC the first time it's executed. The second time it shows
    ZBC; the code has changed the string literal!

    It might show ABC again, or it may not get that far. Or, formally,
    anything at all could happen.

    This is on those implementations that don't put ABC into readonly
    memory (eg. tcc, bcc, DMC, lcc, msvc). Ones like gcc and clang will
    crash.

    It may vary depending on the command-line options, the platform and
    compiler version. Talking about what "gcc" or "tcc" does is not very
    helpful. Anyway, people should be encouraged to write, where possible,
    code that does not depend on such things.

    You can't compare that with this:

    char t[] = "ABC";
    puts(t)
    t[0] = 'Z';

    Here, "ABC" is left unmolested. But the reason is because the
    initialisation /copies/ the literal string to the array. So it
    modifies a copy. The declaration of s directly points it to the
    literal.

    Yes.

    --
    Ben.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Ben Bacarisse on Tue Oct 26 15:11:48 2021
    On 26/10/2021 14:29, Ben Bacarisse wrote:
    Bart <bc@freeuk.com> writes:

    On 26/10/2021 09:18, RacingRabbit@watershipdown.co.uk wrote:
    On Mon, 25 Oct 2021 13:14:30 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/25/21 12:14 PM, RacingRabbit@watershipdown.co.uk wrote:
    ....
    Any attempt to write to a read only program text area will result in a crash
    regardless of the language.

    Perhaps that is true at the hardware level, on some processors. However, >>>> there's also some processors which don't even have the concept of
    read-only memory, and there are fully conforming C implementation that >>>> can target some of those processors.

    However, I'm talking about the level of C code, not hardware. The
    translation from C code to machine code is defined only in terms of the >>>> required behavior, and when there is NO required behavior, that
    translation can get distinctly weird if you believe the mistaken idea
    that C is a "portable assembler".

    ... It is implicit that its read only in C because
    C also provides the following initialisation which places the string >>>>> (presumably) on the heap:

    char str[] = "hello world";

    Such code cannot result in the string being placed in read-only memory, >>>> because it's perfectly legal to modify str. On the other hand, both of
    Yes, that was my point. [] means modifyable, * means read only in every
    C implementation I've ever used.

    You've misunderstood then.

    Yes, RR has misunderstood (or is expressing the point in a confusing
    way).

    But * and [] types are modifable:

    And this is bad wording.

    It's a modification of what RR said.

    Some objects with pointer type are modifiable
    and some are not. No objects with array types are modifiable.

    I don't know what you mean by that. Unless it is that you can't directly
    assign to a whole array object at once; only an element at a time. Or,
    going the other way, when the array is a member of a struct and you
    assign to the whole struct.

    (I know that you can't make a whole array const, only the elements.)

    But in
    fact you seem to be referring to the /target/ of pointer types (again,
    some of which are modifiable and some are not) and to array /elements/
    about which the same is also true. There is no general rule about "*
    and [] types".

    char* s = "ABC";

    This relies on an a conversion that is valid (bad unwise) in C and not 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.)


    puts(s);
    *s = 'Z';

    This is undefined behaviour in both C and C++. The target of the
    assignment (the first character of the string) is not a modifiable
    object.

    This shows ABC the first time it's executed. The second time it shows
    ZBC; the code has changed the string literal!

    It might show ABC again, or it may not get that far. Or, formally,
    anything at all could happen.

    This is on those implementations that don't put ABC into readonly
    memory (eg. tcc, bcc, DMC, lcc, msvc). Ones like gcc and clang will
    crash.

    It may vary depending on the command-line options, the platform and
    compiler version. Talking about what "gcc" or "tcc" does is not very helpful. Anyway, people should be encouraged to write, where possible,
    code that does not depend on such things.

    I'm writing about what is typically observed.

    (I don't put string literals into a readonly segment because I haven't
    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.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From RacingRabbit@watershipdown.co.uk@21:1/5 to Bart on Tue Oct 26 14:36:54 2021
    On Tue, 26 Oct 2021 12:39:54 +0100
    Bart <bc@freeuk.com> wrote:
    On 26/10/2021 09:18, RacingRabbit@watershipdown.co.uk wrote:
    But * and [] types are modifable:

    char* s = "ABC";
    puts(s);
    *s = 'Z';

    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From RacingRabbit@watershipdown.co.uk@21:1/5 to Juha Nieminen on Tue Oct 26 14:35:58 2021
    On Tue, 26 Oct 2021 11:14:58 -0000 (UTC)
    Juha Nieminen <nospam@thanks.invalid> wrote:
    RacingRabbit@watershipdown.co.uk wrote:
    fenris$ a.out
    Bus error: 10

    For starters, that's in no way guaranteed to happen. Learn standard C.

    It is on *nix and thats good enough for me.

    Secondly, if you think that a runtime diagnostic is as good as a compile-time >diagnostic, then you have still a LOT to learn about software development.

    All I'm saying is the bug would exhibit itself pretty quickly.

    All of which could have been avoided if you just used 'const' and
    turned on compiler warnings, and paid attention to them.

    I always have warnings on so const not required.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From RacingRabbit@watershipdown.co.uk@21:1/5 to James Kuyper on Tue Oct 26 14:42:23 2021
    On Tue, 26 Oct 2021 10:31:41 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/26/21 4:18 AM, RacingRabbit@watershipdown.co.uk wrote:
    On Mon, 25 Oct 2021 13:14:30 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/25/21 12:14 PM, RacingRabbit@watershipdown.co.uk wrote:
    ....
    ... It is implicit that its read only in C because
    C also provides the following initialisation which places the string
    (presumably) on the heap:

    char str[] = "hello world";

    Such code cannot result in the string being placed in read-only memory,
    because it's perfectly legal to modify str. On the other hand, both of

    Yes, that was my point. [] means modifyable, * means read only in every
    C implementation I've ever used.

    Incorrect. In most declarations, [] means array, and * means pointer.
    Neither one means "read only".
    I think you may be thinking of a different fact that has nothing to do
    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.

    Exception 1: it's not permitted to declare functions that take arrays as >arguments,

    Since when?

    fenris$ cat t.c
    #include <stdio.h>

    void func(int a[2][3])
    {
    printf("%d\n",a[1][2]);
    }


    int main()
    {
    int a[2][3];
    a[1][2] = 123;
    func(a);
    return 0;
    }
    fenris$ cc t.c; a.out
    123

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to RacingRabbit@watershipdown.co.uk on Tue Oct 26 10:31:41 2021
    On 10/26/21 4:18 AM, RacingRabbit@watershipdown.co.uk wrote:
    On Mon, 25 Oct 2021 13:14:30 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/25/21 12:14 PM, RacingRabbit@watershipdown.co.uk wrote:
    ...
    ... It is implicit that its read only in C because
    C also provides the following initialisation which places the string
    (presumably) on the heap:

    char str[] = "hello world";

    Such code cannot result in the string being placed in read-only memory,
    because it's perfectly legal to modify str. On the other hand, both of

    Yes, that was my point. [] means modifyable, * means read only in every
    C implementation I've ever used.

    Incorrect. In most declarations, [] means array, and * means pointer.
    Neither one means "read only".
    I think you may be thinking of a different fact that has nothing to do
    with read-only memory. Within the scope of an identifier that identifies
    an array, that identifier can only ever identify that particular array.
    An identifier that identifies a pointer to an object type need not point
    at any actual object, and unless it itself is declared const, can be
    changed to point at a different object. But that difference between
    arrays and pointers has nothing to do with read-only memory. The address
    of a named array is not necessarily stored in any pointer - it is
    normally hard-coded into the machine language instructions that refer to
    the array, so the fact that you can't change that address is not because
    the address is stored in read-only memory.

    Exception 1: 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 is automatically converting into a declaration of a pointer to the element type of an array. Thus, the
    following two function declarations are functionally identical, despite
    being syntactically different:

    void func(int array[]);
    void func(int *ptr);

    Exception 2: in a function parameter declaration, the construct [*]
    marks the corresponding dimension of the relevant array as having a
    variably modified type with an unknown length for that dimension. This
    feature cannot be used in the defining declaration for a function,
    because the function definition requires that the variable length be
    explicitly specified. It is still an array, and not in any sense a
    pointer (unless the relevant dimension is the top-most one, in which
    case exception 1 described above also applies).

    Any attempt to modify the contents of a string literal is undefined. Any attempt to modify an object whose definition is const-qualified is also undefined. Those facts permit, but do not require, that those objects be
    stored in read-only memory.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to RacingRabbit@watershipdown.co.uk on Tue Oct 26 10:42:24 2021
    On 10/26/21 4:21 AM, RacingRabbit@watershipdown.co.uk wrote:
    On Mon, 25 Oct 2021 10:48:57 -0700
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    RacingRabbit@watershipdown.co.uk writes:
    Any attempt to write to a read only program text area will result in a crash
    regardless of the language. It is implicit that its read only in C because >>> C also provides the following initialisation which places the string
    (presumably) on the heap:

    char str[] = "hello world";

    I suggest that you would benefit more here from asking questions than
    from making assertions.

    I suggest you ease up on being patronising.

    You'll get less patronizing responses when you cease displaying such an
    abysmal understanding of C, while believing you understand it better
    than others.

    That declaration does not place anything on the heap. The contents of
    str is placed on the stack if it appears within a function definition.
    or in the static data area if it appears outside a function definition.

    Wherever its placed, the point is its modifyable unlike *str = which isn't.

    Your right, but for the wrong reasons. It's true that *str isn't
    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
    could equally easily have been initialized to point at modifiable
    memory. Nothing about the str itself makes it read-only.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to RacingRabbit@watershipdown.co.uk on Tue Oct 26 10:43:09 2021
    On 10/26/21 5:02 AM, RacingRabbit@watershipdown.co.uk wrote:
    ...
    fenris$ cat t.c
    #include <stdio.h>

    void func(char *str)
    {
    str[0] = 0;
    }


    int main()
    {

    Try changing the following line:
    char *str = "hello";

    to
    char greeting[] = "hello";
    char *str = greeting;

    func(str);
    puts("Worked");
    return 0;
    }

    You shouldn't get a bus error this time. Do you understand why?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From RacingRabbit@watershipdown.co.uk@21:1/5 to James Kuyper on Tue Oct 26 14:48:14 2021
    On Tue, 26 Oct 2021 10:42:24 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/26/21 4:21 AM, RacingRabbit@watershipdown.co.uk wrote:
    On Mon, 25 Oct 2021 10:48:57 -0700
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    RacingRabbit@watershipdown.co.uk writes:
    Any attempt to write to a read only program text area will result in a >crash
    regardless of the language. It is implicit that its read only in C because >>>> C also provides the following initialisation which places the string
    (presumably) on the heap:

    char str[] = "hello world";

    I suggest that you would benefit more here from asking questions than >>>from making assertions.

    I suggest you ease up on being patronising.

    You'll get less patronizing responses when you cease displaying such an >abysmal understanding of C, while believing you understand it better
    than others.

    Says the preening fool.

    Your right, but for the wrong reasons. It's true that *str isn't
    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.

    Ok, well thanks for that. Helpful.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to RacingRabbit@watershipdown.co.uk on Tue Oct 26 15:55:38 2021
    On 26/10/2021 15:36, RacingRabbit@watershipdown.co.uk wrote:
    On Tue, 26 Oct 2021 12:39:54 +0100
    Bart <bc@freeuk.com> wrote:
    On 26/10/2021 09:18, RacingRabbit@watershipdown.co.uk wrote:
    But * and [] types are modifable:

    char* s = "ABC";
    puts(s);
    *s = 'Z';

    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.


    What makes you think I didn't?

    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?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Juha Nieminen on Tue Oct 26 11:03:53 2021
    On 10/26/21 1:18 AM, Juha Nieminen wrote:
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    A lot of people have trouble understanding how breath-takingly wide the
    scope of "imposes no requirements" is. The standard tries to make that
    clear with the following examples "Possible undefined behavior ranges
    from ignoring the situation completely with unpredictable results, to
    behaving during translation or program execution in a documented manner
    characteristic of the environment (with or without the issuance of a
    diagnostic message), to terminating a translation or execution (with the
    issuance of a diagnostic message)."

    No better example of "undefined behavior" causing a major problem than
    that bug in the Linux kernel discovered some years ago, where the kernel would deliberately dereference a null pointer (I don't remember anymore
    for what reason), and gcc saw that it was a null pointer dereference,
    which according to the C standard is undefined behavior, and since that allows the compiler to do with it whatever it wants, it (if I remember correctly) just optimized it away, causing the extraordinarily hard-to-find bug in the kernel.

    (Also, if I remember correctly, it caused quite a discussion about
    whether compilers should actually be allowed to "do whatever they want"
    with such code, or whether they should do as they are told.)

    That bug serves to illustrate a very important point about undefined
    behavior. UB only refers to behavior that is not defined by the C
    standard; the behavior might in fact be defined by some other document.
    If such a document has authority over every place that your code needs
    to work, there's absolutely nothing wrong with writing code with
    undefined behavior that relies upon the definition of that behavior
    provided by that document. But when you write such code, it's absolutely essential that you know what the relevant definition is.

    The developers in question were absolutely certain that they knew what
    the defined behavior was for the platform that they were using: the
    hardware had an instruction that could be used to load the data from a specified memory location, and nothing problematic would occur if that instruction was passed an address of 0.

    There were two key mistakes in this thinking:
    1. It's not the hardware that defines the behavior, it's the
    implementation of C.

    2. Popular misconceptions to the contrary notwithstanding, C is NOT a
    portable assembler. C code does not instruct the compiler to generate a particular set of machine code instructions. It only tells the
    implementation what the desired behavior of the program is. An
    implementation has no obligation to produce any specific set of machine
    code instructions to achieve that goal. The only requirement is that the observable behavior of the program (a term defined in 5.1.2.3p2, and
    that definition is significantly more complicated than "behavior which
    can be observed") must meet the requirements of the rest of the standard.

    When the behavior is undefined, the standard doesn't impose any
    requirements. In this particular case, the implementation provided it's
    own definition of the behavior, and it was significantly more complex
    than merely the single machine instruction that they expected to be
    generated. Specifically, the defined behavior was to optimize all code
    between the last time the pointer was updated, until the next time it
    was updated, on the assumption that the pointer's value was not null.
    Such an optimization is normally a good idea - if they had taken proper
    care to prevent the pointer from having a null value, the code that was
    removed would have been dead code - removing it sped up the program.

    But they didn't take proper care to make sure the pointer was not null.
    They dealt with that possibility only after dereferencing it, and as a
    result, the code that they wrote to deal with that possibility got
    optimized away.

    The most ironic aspect of this problem was that the optimization that
    revealed the bug in their code was not on by default. It was turned on
    because they had explicitly requested it. Your obligation to know how an implementation defines behavior that is undefined by the standard is
    even higher when choosing non-default optimizations.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Manfred@21:1/5 to Keith Thompson on Tue Oct 26 17:15:09 2021
    On 10/25/2021 8:11 PM, Keith Thompson wrote:
    Manfred <noname@add.invalid> writes:
    On 10/25/2021 4:59 PM, James Kuyper wrote:
    On 10/25/21 10:19 AM, RacingRabbit@watershipdown.co.uk wrote:
    <snip>

    No idea what UB means, but what'll happen is it'll crash immediately so you'll
    soon find out.
    UB means "Undefined Behavior", a technical term from the C standard
    which does NOT mean "behavior for which there is no definition". It
    means "behavior, upon use of a nonportable or erroneous program
    construct or of erroneous data, for which this document imposes no
    requirements" (3.4.3). Note that "this document" refers to the C
    standard; other documents (such as compiler documentation or ABI
    standards) might define the behavior, without changing the fact that is
    qualifies as "undefined behavior" as far as the C standard is concerned.

    Thanks for the quote, it made me compare it with the definition of UB
    in the C++ standard, which simply states "behavior for which this
    International Standard imposes no requirements".

    The lack of the sentence "upon use of a nonportable or erroneous
    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.

    It depends on the reader: whether it is some sort of text processing
    machine, a language lawyer or a human being.


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


    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From RacingRabbit@watershipdown.co.uk@21:1/5 to Bart on Tue Oct 26 15:26:15 2021
    On Tue, 26 Oct 2021 15:55:38 +0100
    Bart <bc@freeuk.com> wrote:
    On 26/10/2021 15:36, RacingRabbit@watershipdown.co.uk wrote:
    On Tue, 26 Oct 2021 12:39:54 +0100
    Bart <bc@freeuk.com> wrote:
    On 26/10/2021 09:18, RacingRabbit@watershipdown.co.uk wrote:
    But * and [] types are modifable:

    char* s = "ABC";
    puts(s);
    *s = 'Z';

    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.


    What makes you think I didn't?

    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From RacingRabbit@watershipdown.co.uk@21:1/5 to James Kuyper on Tue Oct 26 15:21:39 2021
    On Tue, 26 Oct 2021 10:43:09 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/26/21 5:02 AM, RacingRabbit@watershipdown.co.uk wrote:
    ....
    fenris$ cat t.c
    #include <stdio.h>

    void func(char *str)
    {
    str[0] = 0;
    }


    int main()
    {

    Try changing the following line:
    char *str = "hello";

    to
    char greeting[] = "hello";
    char *str = greeting;

    func(str);
    puts("Worked");
    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?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to RacingRabbit@watershipdown.co.uk on Tue Oct 26 11:22:18 2021
    On 10/26/21 10:42 AM, RacingRabbit@watershipdown.co.uk wrote:
    On Tue, 26 Oct 2021 10:31:41 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/26/21 4:18 AM, RacingRabbit@watershipdown.co.uk wrote:
    On Mon, 25 Oct 2021 13:14:30 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/25/21 12:14 PM, RacingRabbit@watershipdown.co.uk wrote:
    ....
    ... It is implicit that its read only in C because
    C also provides the following initialisation which places the string >>>>> (presumably) on the heap:

    char str[] = "hello world";

    Such code cannot result in the string being placed in read-only memory, >>>> because it's perfectly legal to modify str. On the other hand, both of

    Yes, that was my point. [] means modifyable, * means read only in every
    C implementation I've ever used.

    Incorrect. In most declarations, [] means array, and * means pointer.
    Neither one means "read only".
    I think you may be thinking of a different fact that has nothing to do
    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.

    fenris$ cat t.c
    #include <stdio.h>

    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 main()
    {
    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".


    return 0;
    }
    fenris$ cc t.c; a.out
    123

    Neither of these features are new, they both date back to K&R C.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to RacingRabbit@watershipdown.co.uk on Tue Oct 26 11:27:43 2021
    On 10/26/21 10:36 AM, RacingRabbit@watershipdown.co.uk wrote:
    On Tue, 26 Oct 2021 12:39:54 +0100
    Bart <bc@freeuk.com> wrote:
    On 26/10/2021 09:18, RacingRabbit@watershipdown.co.uk wrote:
    But * and [] types are modifable:

    char* s = "ABC";
    puts(s);
    *s = 'Z';

    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to RacingRabbit@watershipdown.co.uk on Tue Oct 26 11:52:13 2021
    On 10/26/21 10:48 AM, RacingRabbit@watershipdown.co.uk wrote:
    On Tue, 26 Oct 2021 10:42:24 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    ...
    Your right, but for the wrong reasons. It's true that *str isn't
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From RacingRabbit@watershipdown.co.uk@21:1/5 to James Kuyper on Tue Oct 26 15:30:37 2021
    On Tue, 26 Oct 2021 11:22:18 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/26/21 10:42 AM, RacingRabbit@watershipdown.co.uk wrote:
    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.

    Hello, we speak English on this group. Do. You. Understand. It?

    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.

    Thats exactly what it is. Too bad it made you look stupid.

    "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]);

    [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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to RacingRabbit@watershipdown.co.uk on Tue Oct 26 12:23:37 2021
    On 10/26/21 11:30 AM, RacingRabbit@watershipdown.co.uk wrote:
    On Tue, 26 Oct 2021 11:22:18 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    ...
    "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]);

    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?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to James Kuyper on Tue Oct 26 12:51:24 2021
    On 10/26/21 12:23 PM, James Kuyper wrote:
    ...
    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"));]]][

    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.

    }

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Keith Thompson@21:1/5 to Bart on Tue Oct 26 09:49:34 2021
    Bart <bc@freeuk.com> writes:
    On 26/10/2021 14:29, Ben Bacarisse wrote:
    Bart <bc@freeuk.com> writes:
    [...]
    char* s = "ABC";
    This relies on an a conversion that is valid (bad unwise) in C and
    not
    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.

    (I don't put string literals into a readonly segment because I haven't
    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 */

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Keith Thompson@21:1/5 to Keith Thompson on Tue Oct 26 10:27:01 2021
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    [...]
    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.

    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 */

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Keith Thompson on Tue Oct 26 14:06:19 2021
    On 10/26/21 1:27 PM, Keith Thompson wrote:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    [...]
    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.

    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Keith Thompson on Tue Oct 26 18:38:35 2021
    On 26/10/2021 17:49, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:
    On 26/10/2021 14:29, Ben Bacarisse wrote:
    Bart <bc@freeuk.com> writes:
    [...]
    char* s = "ABC";
    This relies on an a conversion that is valid (bad unwise) in C and
    not
    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 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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Keith Thompson@21:1/5 to RacingRabbit@watershipdown.co.uk on Tue Oct 26 10:22:51 2021
    RacingRabbit@watershipdown.co.uk writes:
    On Tue, 26 Oct 2021 10:42:24 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/26/21 4:21 AM, RacingRabbit@watershipdown.co.uk wrote:
    On Mon, 25 Oct 2021 10:48:57 -0700
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    RacingRabbit@watershipdown.co.uk writes:
    Any attempt to write to a read only program text area will result in a >>crash
    regardless of the language. It is implicit that its read only in C because
    C also provides the following initialisation which places the string >>>>> (presumably) on the heap:

    char str[] = "hello world";

    I suggest that you would benefit more here from asking questions than >>>>from making assertions.

    I suggest you ease up on being patronising.

    You'll get less patronizing responses when you cease displaying such an >>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.

    Your right, but for the wrong reasons. It's true that *str isn't >>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.

    Ok, well thanks for that. Helpful.

    --
    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 */

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to James Kuyper on Tue Oct 26 14:34:19 2021
    On 10/26/21 12:23 PM, James Kuyper wrote:
    ...
    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?

    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?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Keith Thompson@21:1/5 to Bart on Tue Oct 26 11:20:24 2021
    Bart <bc@freeuk.com> writes:
    On 26/10/2021 17:49, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:
    On 26/10/2021 14:29, Ben Bacarisse wrote:
    Bart <bc@freeuk.com> writes:
    [...]
    char* s = "ABC";
    This relies on an a conversion that is valid (bad unwise) in C and
    not
    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 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".)

    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.

    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 */

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Tue Oct 26 11:20:36 2021
    On 10/25/2021 12:45 PM, Chris M. Thomasson wrote:
    On 10/25/2021 1:21 AM, RacingRabbit@watershipdown.co.uk wrote:
    On Sat, 23 Oct 2021 18:45:22 +0200
    David Brown <david.brown@hesbynett.no> wrote:
    On 23/10/2021 13:06, Bart wrote:
    So, even /more/ clutter?! I's also like to see a typedefed version
    of my
    example that is not harder to understand.


    typedef const int constant_integer;
    typedef constant_integer * pointer_to_constant_integer;
    typedef const pointer_to_constant_integer
    constant_pointer_to_constant_integer;
    typedef constant_pointer_to_constant_integer *
    pointer_to_constant_pointer_to_constant_integer;

    pointer_to_constant_pointer_to_constant_integer x;

    Consts in C are pointless because it doesn't have references and its
    rather
    difficult to "accidentaly" dereference a pointer to update the value its
    pointing to.


    Fwiw, when writing code in C, I tend to use the following pattern:

    struct foo
    {
       unsigned int a;
    };


    void
    foo_init(
        struct foo* const self,
        unsigned int a
    ){
        self->a = a;
    }


    int
    foo_compute(
        struct foo const* const self,
        unsigned int a
    ){
        return self->a *= a + 123;
    }


    I like to use a const pointer to self so that if I accidentally modify
    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.

    ;^)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Keith Thompson on Tue Oct 26 20:32:05 2021
    On 26/10/2021 19:20, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:
    On 26/10/2021 17:49, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:
    On 26/10/2021 14:29, Ben Bacarisse wrote:
    Bart <bc@freeuk.com> writes:
    [...]
    char* s = "ABC";
    This relies on an a conversion that is valid (bad unwise) in C and
    not
    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 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.

    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!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to RacingRabbit@watershipdown.co.uk on Tue Oct 26 20:35:57 2021
    On 26/10/2021 16:26, RacingRabbit@watershipdown.co.uk wrote:

    Sorry, I have limited tolerance for smart asses

    Funny, that, so do I! So I'll leave you to it.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to James Kuyper on Tue Oct 26 12:57:43 2021
    On 10/26/2021 11:34 AM, James Kuyper wrote:
    On 10/26/21 12:23 PM, James Kuyper wrote:
    ...
    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?

    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?


    Fwiw, I get the same:

    a:pointer
    b:array
    c:pointer

    :^)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to RacingRabbit@watershipdown.co.uk on Tue Oct 26 22:32:44 2021
    On 26/10/2021 17:21, RacingRabbit@watershipdown.co.uk wrote:
    On Tue, 26 Oct 2021 10:43:09 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:

    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?


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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Tue Oct 26 22:18:21 2021
    On 26/10/2021 21:32, Bart wrote:
    On 26/10/2021 19:20, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:
    On 26/10/2021 17:49, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:
    On 26/10/2021 14:29, Ben Bacarisse wrote:
    Bart <bc@freeuk.com> writes:
    [...]
          char* s = "ABC";
    This relies on an a conversion that is valid (bad unwise) in C and >>>>>> not
    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 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.

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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to Keith Thompson on Wed Oct 27 04:43:23 2021
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    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.

    If gcc (or whichever compiler this is) doesn't give an outright error from trying to assign a const pointer to a non-const one without an explicit
    cast, then it's non-standard-conforming and I would classify it as a
    defect in the compiler.

    I think that the compiler should be fully standard-conforming by default,
    and be more permissive and have non-standard extensions and behavior only
    when explicitly specified using command-line parameters. Not the other
    way around.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to RacingRabbit@watershipdown.co.uk on Wed Oct 27 05:01:22 2021
    RacingRabbit@watershipdown.co.uk wrote:
    On Tue, 26 Oct 2021 11:14:58 -0000 (UTC)
    Juha Nieminen <nospam@thanks.invalid> wrote:
    RacingRabbit@watershipdown.co.uk wrote:
    fenris$ a.out
    Bus error: 10

    For starters, that's in no way guaranteed to happen. Learn standard C.

    It is on *nix and thats good enough for me.

    I would like a citation to the Single Unix Specification, or to POSIX,
    that states so.

    (I'm not saying it's not there. I just find it doubtful because not all
    CPU architectures have support for that.)

    Secondly, if you think that a runtime diagnostic is as good as a compile-time >>diagnostic, then you have still a LOT to learn about software development.

    All I'm saying is the bug would exhibit itself pretty quickly.

    Then you are very inexperienced in software development.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to RacingRabbit@watershipdown.co.uk on Wed Oct 27 04:57:59 2021
    RacingRabbit@watershipdown.co.uk wrote:
    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.

    You seem to be confusing pointers with arrays. Which is not completely unexpected given that this confusing is very common among less
    experienced C (and C++) programmers, as the language makes that
    distinction oftentimes hard to discern.

    (I know that you will not read past this, but I'm going to try to
    explain it anyway.)

    An array and a pointer are not the same thing, not even in C. While an
    array can often be confused with a pointer, because an array almost always implicitly converts to a pointer-to-its-fist-element implicitly whenever
    one is needed, they are not the same thing. The most prominent situation
    where this distinction is made is when using the sizeof operator.
    The sizeof of a pointer will be just the size of the pointer itself
    (typically 4 or 8). The sizeof of an array will be the entire amount
    of bytes that the array takes.

    uint32_t values[10];
    uint32_t *ptr = values;

    sizeof(values); // will be 40.
    sizeof(ptr); // will typically be 4 or 8.

    In C, and C++, you cannot pass arrays by value to a function as-is.
    You can only pass a pointer to an array. (This pointer will carry
    no information about the number of elements in the array, even if
    you use the confusing syntax where it looks like it does.)

    The only way you can pass an array to a function by value (both
    in C and C++) is by enclosing it inside a struct (or class) and
    passing an object of that type by value:

    struct MyArray
    {
    int values[10];
    };

    // Genuinely gets the entire array by value, not just a pointer:
    void foo(struct MyArray theArray);

    You can use the array syntax in a function parameter declaration, but
    that's just confusing syntax sugar:

    // It does not get an array of 10 elements by value, it's just
    // getting a pointer. The "10" in the declaration is meaningless:
    void foo(int values[10])
    {
    printf("%u\n", sizeof(values)); // will print 4 or 8, not 40
    }

    Honestly, I think it was a bad idea to add that syntactic sugar to C.
    It only causes confusion for no benefit.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to RacingRabbit@watershipdown.co.uk on Wed Oct 27 05:04:55 2021
    RacingRabbit@watershipdown.co.uk wrote:
    void func(char *str)

    Try changing the following line:
    char *str = "hello";

    to
    char greeting[] = "hello";
    char *str = greeting;

    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?

    Actually he has. He read your assertion that "[] means modifiable
    and * means non-modifiable", and is proving you wrong: * can be
    perfectly well modifiable.

    Of course you will never acknowledge this. Instead you'll just start
    dodging with insults.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to RacingRabbit@watershipdown.co.uk on Wed Oct 27 04:44:34 2021
    RacingRabbit@watershipdown.co.uk wrote:
    Sorry, I have limited tolerance for smart asses so yes, I stopped reading.

    Then why are you responding?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Juha Nieminen on Wed Oct 27 08:29:07 2021
    On 27/10/2021 06:43, Juha Nieminen wrote:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    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.

    If gcc (or whichever compiler this is) doesn't give an outright error from trying to assign a const pointer to a non-const one without an explicit
    cast, then it's non-standard-conforming and I would classify it as a
    defect in the compiler.

    You may /prefer/ an error (stopping compilation) rather than a warning,
    but the standard does not require it. A diagnostic message is all that
    is needed on constraint errors.

    I guess someone decided that a warning (always enabled, without
    requiring any flags) is a good compromise for the convenience of people converting old C code to C++.

    (I personally would prefer a hard error on this and many other faults in
    code, requiring particular options to allow people to compile
    questionable code. But backwards compatibility applies to build systems
    as well - there is a strong feeling that where possible, code that could
    be compiled with particular flags before should continue to be compilable.)

    It's the website rextester.com that is at fault in hiding warnings by
    default. That is idiotic, IMHO.


    I think that the compiler should be fully standard-conforming by default,
    and be more permissive and have non-standard extensions and behavior only when explicitly specified using command-line parameters. Not the other
    way around.


    In this respect, gcc /is/ fully conforming.

    From C++14, 1.4p2 "Implementation compliance"

    """
    If a program contains a violation of any diagnosable rule or an
    occurrence of a construct described in this Standard as “conditionally-supported” when the implementation does not support that construct, a conforming implementation shall issue at least one
    diagnostic message.
    """

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From RacingRabbit@watershipdown.co.uk@21:1/5 to James Kuyper on Wed Oct 27 07:57:39 2021
    On Tue, 26 Oct 2021 12:23:37 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/26/21 11:30 AM, RacingRabbit@watershipdown.co.uk wrote:
    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."

    There is no difference. As I said, what the compiler does with it under the hood is not the point. The syntax declares an array.

    When I run that program, it says "pointer". What do you get when you run it?

    Same. Not the point, its declared as an array.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to RacingRabbit@watershipdown.co.uk on Wed Oct 27 08:47:42 2021
    RacingRabbit@watershipdown.co.uk wrote:
    On Tue, 26 Oct 2021 12:23:37 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/26/21 11:30 AM, RacingRabbit@watershipdown.co.uk wrote:
    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."

    There is no difference. As I said, what the compiler does with it under the hood is not the point. The syntax declares an array.

    No, it doesn't. It declares a pointer. What do you think this will print?

    #include <stdio.h>

    void foo(int array[10])
    {
    printf("foo: sizeof(array) = %lu\n", sizeof(array));
    }

    int main()
    {
    int array[10];
    int *ptr = array;
    printf("main: sizeof(array) = %lu\n", sizeof(array));
    printf("main: sizeof(ptr) = %lu\n", sizeof(ptr));
    foo(array);
    }

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to RacingRabbit@watershipdown.co.uk on Wed Oct 27 11:07:03 2021
    On 27/10/2021 09:57, RacingRabbit@watershipdown.co.uk wrote:
    On Tue, 26 Oct 2021 12:23:37 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/26/21 11:30 AM, RacingRabbit@watershipdown.co.uk wrote:
    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."

    There is no difference. As I said, what the compiler does with it under the hood is not the point. The syntax declares an array.

    When I run that program, it says "pointer". What do you get when you run it?

    Same. Not the point, its declared as an array.


    Do you have trouble understanding the difference between an array, and a pointer to the first element of the array?

    C arrays are /never/ passed to functions as parameters, nor are they
    used directly in most expressions. (You can pass a struct that contains
    an array as a field, but that's a struct, not an array.) It is a major inconsistency in the type system of C, but one that works well in practice.

    Do you know the difference between :

    int ar1[10];

    and

    std::array<int, 10> ar2;

    and how these may be passed to functions?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From JohnnyCameLater@whatsthetime.net@21:1/5 to Juha Nieminen on Wed Oct 27 14:29:08 2021
    On Wed, 27 Oct 2021 08:47:42 -0000 (UTC)
    Juha Nieminen <nospam@thanks.invalid> wrote:
    RacingRabbit@watershipdown.co.uk wrote:
    On Tue, 26 Oct 2021 12:23:37 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/26/21 11:30 AM, RacingRabbit@watershipdown.co.uk wrote:
    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."

    There is no difference. As I said, what the compiler does with it under the >> hood is not the point. The syntax declares an array.

    No, it doesn't. It declares a pointer. What do you think this will print?

    Which parts of "syntax" and "under the hood" is confusing you. Perhaps in your own language?

    "syntaksi"
    "konepellin alla"

    Any clearer?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From JohnnyCameLater@whatsthetime.net@21:1/5 to David Brown on Wed Oct 27 14:30:39 2021
    On Wed, 27 Oct 2021 11:07:03 +0200
    David Brown <david.brown@hesbynett.no> wrote:
    On 27/10/2021 09:57, RacingRabbit@watershipdown.co.uk wrote:
    On Tue, 26 Oct 2021 12:23:37 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/26/21 11:30 AM, RacingRabbit@watershipdown.co.uk wrote:
    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."

    There is no difference. As I said, what the compiler does with it under the >> hood is not the point. The syntax declares an array.

    When I run that program, it says "pointer". What do you get when you run it?


    Same. Not the point, its declared as an array.


    Do you have trouble understanding the difference between an array, and a >pointer to the first element of the array?

    Do you have trouble understanding plain English? The syntax declares an array, the fact the compiler converts that into a pointer isn't the point. Pun intended.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to JohnnyCameLater@whatsthetime.net on Wed Oct 27 17:30:43 2021
    On 27/10/2021 16:30, JohnnyCameLater@whatsthetime.net wrote:
    On Wed, 27 Oct 2021 11:07:03 +0200
    David Brown <david.brown@hesbynett.no> wrote:
    On 27/10/2021 09:57, RacingRabbit@watershipdown.co.uk wrote:
    On Tue, 26 Oct 2021 12:23:37 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/26/21 11:30 AM, RacingRabbit@watershipdown.co.uk wrote:
    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."

    There is no difference. As I said, what the compiler does with it under the >>> hood is not the point. The syntax declares an array.

    When I run that program, it says "pointer". What do you get when you run it?


    Same. Not the point, its declared as an array.


    Do you have trouble understanding the difference between an array, and a
    pointer to the first element of the array?

    Do you have trouble understanding plain English? The syntax declares an array,
    the fact the compiler converts that into a pointer isn't the point. Pun intended.


    I don't know whether you are trolling, or if you are simply so arrogant
    that you would rather stay ignorant and insult people than admit to
    making a mistake and appreciate the help and advice you are given.
    Either way, feel free to come back with questions once you have grown up
    a little, and feel free to stay away until that day.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to RacingRabbit@watershipdown.co.uk on Wed Oct 27 11:38:54 2021
    On 10/27/21 3:57 AM, RacingRabbit@watershipdown.co.uk wrote:
    On Tue, 26 Oct 2021 12:23:37 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/26/21 11:30 AM, RacingRabbit@watershipdown.co.uk wrote:
    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."

    There is no difference. As I said, what the compiler does with it under the hood is not the point. The syntax declares an array.

    The syntax uses [] rather than *. The semantics are those of a pointer,
    not an array.

    The semantics are NOT "under the hood" - they restrict what you can do
    with the function without violating a constraint. They determine what
    result you get when you apply the sizeof() or unary & operators to the parameter. Most importantly, they determine what happens if you write expressions such as "parameter[1] = 5".

    Getting back to my original statement, declaring a parameter with
    pointer semantics using the [] syntax does not allow you to pass an
    array as the corresponding argument in the function call. All you can
    pass is a pointer to the first element of the array - which is what any
    lvalue of array type will be converted into if passed as an argument to
    such a function.

    Since C uses "pass by value" exclusively, if you were able to pass an
    array as an argument, then the corresponding parameter would be a
    separate array object with a different address, initialized by copying
    from the array argument. Evaluation the expression "parameter[1] = 5"
    would only affect the parameter, it would have no effect on the array
    argument.
    You can get some idea of how C would behave if such declarations were
    possible, by declaring a struct type containing an array. Such a struct
    could be passed into or returned by a function by value. It's
    syntactically different from, but semantically equivalent to, passing
    around the array itself by value.

    The actual C semantics of such a function call are that the
    corresponding parameter is a separate pointer object filled in by
    copying the value of the pointer that the lvalue was converted to.
    Evaluating the expression "parameter[1] = 5" would set the second
    element of the array to 5.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From JohnnyCameLater@whatsthetime.net@21:1/5 to David Brown on Wed Oct 27 15:46:20 2021
    On Wed, 27 Oct 2021 17:30:43 +0200
    David Brown <david.brown@hesbynett.no> wrote:
    On 27/10/2021 16:30, JohnnyCameLater@whatsthetime.net wrote:
    On Wed, 27 Oct 2021 11:07:03 +0200
    David Brown <david.brown@hesbynett.no> wrote:
    On 27/10/2021 09:57, RacingRabbit@watershipdown.co.uk wrote:
    On Tue, 26 Oct 2021 12:23:37 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/26/21 11:30 AM, RacingRabbit@watershipdown.co.uk wrote:
    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."

    There is no difference. As I said, what the compiler does with it under the

    hood is not the point. The syntax declares an array.

    When I run that program, it says "pointer". What do you get when you run >it?


    Same. Not the point, its declared as an array.


    Do you have trouble understanding the difference between an array, and a >>> pointer to the first element of the array?

    Do you have trouble understanding plain English? The syntax declares an >array,
    the fact the compiler converts that into a pointer isn't the point. Pun
    intended.


    I don't know whether you are trolling, or if you are simply so arrogant

    Pot -> kettle.

    that you would rather stay ignorant and insult people than admit to
    making a mistake and appreciate the help and advice you are given.
    Either way, feel free to come back with questions once you have grown up
    a little, and feel free to stay away until that day.

    Go fuck yourself you patronising jerk.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to JohnnyCameLater@whatsthetime.net on Wed Oct 27 11:39:16 2021
    On 10/27/21 10:30 AM, JohnnyCameLater@whatsthetime.net wrote:
    On Wed, 27 Oct 2021 11:07:03 +0200
    David Brown <david.brown@hesbynett.no> wrote:
    ...
    Do you have trouble understanding the difference between an array, and a
    pointer to the first element of the array?

    Do you have trouble understanding plain English? The syntax declares an array,
    the fact the compiler converts that into a pointer isn't the point. Pun intended.

    "array" vs. "pointer is a matter of semantics. [] vs * is a matter of
    syntax. All you can say about the syntax is that it contains []. In most contexts, [] in the syntax of a declaration corresponds to array
    semantics. However, the standard is quite explicit about the fact that,
    in this context, what [] means is pointer semantics.

    This is not just quibbling. It determine which arguments can be passed
    to the function without violating a constraint. Two function prototypes
    are compatible only if the type that one of them declares for a given
    parameter is compatible with the type declared by the other for that
    same parameter. If one function prototype uses "int array[]" for one
    parameter, and the other uses "* int pointer" for the same parameter,
    those two prototypes are compatible.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From JohnnyCameLater@whatsthetime.net@21:1/5 to James Kuyper on Wed Oct 27 15:49:17 2021
    On Wed, 27 Oct 2021 11:38:54 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/27/21 3:57 AM, RacingRabbit@watershipdown.co.uk wrote:
    On Tue, 26 Oct 2021 12:23:37 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/26/21 11:30 AM, RacingRabbit@watershipdown.co.uk wrote:
    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."

    There is no difference. As I said, what the compiler does with it under the >> hood is not the point. The syntax declares an array.

    The syntax uses [] rather than *. The semantics are those of a pointer,
    not an array.

    The syntax defines an array. Why do you think you get compiler warnings if
    you go OOB with an index value? Try it.

    The semantics are NOT "under the hood" - they restrict what you can do

    Yes, they are.

    tl;dr

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From JohnnyCameLater@whatsthetime.net@21:1/5 to James Kuyper on Wed Oct 27 15:51:31 2021
    On Wed, 27 Oct 2021 11:39:16 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/27/21 10:30 AM, JohnnyCameLater@whatsthetime.net wrote:
    On Wed, 27 Oct 2021 11:07:03 +0200
    David Brown <david.brown@hesbynett.no> wrote:
    ....
    Do you have trouble understanding the difference between an array, and a >>> pointer to the first element of the array?

    Do you have trouble understanding plain English? The syntax declares an >array,
    the fact the compiler converts that into a pointer isn't the point. Pun
    intended.

    "array" vs. "pointer is a matter of semantics. [] vs * is a matter of
    syntax. All you can say about the syntax is that it contains []. In most >contexts, [] in the syntax of a declaration corresponds to array
    semantics. However, the standard is quite explicit about the fact that,
    in this context, what [] means is pointer semantics.

    Oh really?

    fenris$ cat t.c
    #include <stdio.h>

    void func(int a[2][3])
    {
    a[10][50] = 123;
    }


    int main()
    {
    int a[2][3];
    func(a);
    return 0;
    }
    fenris$ cc t.c
    t.c:5:2: warning: array index 50 is past the end of the array (which contains 3
    elements) [-Warray-bounds]
    a[10][50] = 123;
    ^ ~~
    t.c:3:11: note: array 'a' declared here
    void func(int a[2][3])
    ^
    1 warning generated.

    Nuff said.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to David Brown on Wed Oct 27 16:13:40 2021
    David Brown <david.brown@hesbynett.no> writes:
    On 27/10/2021 16:30, JohnnyCameLater@whatsthetime.net wrote:

    On 27/10/2021 09:57, RacingRabbit@watershipdown.co.uk wrote:

    I don't know whether you are trolling,

    Given the choice of nyms,and the name changes, my assumption has
    been trolling. Best to simply ignore such posts.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to JohnnyCameLater@whatsthetime.net on Wed Oct 27 12:26:06 2021
    On 10/27/21 11:51 AM, JohnnyCameLater@whatsthetime.net wrote:
    On Wed, 27 Oct 2021 11:39:16 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    ...
    "array" vs. "pointer is a matter of semantics. [] vs * is a matter of
    syntax. All you can say about the syntax is that it contains []. In most
    contexts, [] in the syntax of a declaration corresponds to array
    semantics. However, the standard is quite explicit about the fact that,
    in this context, what [] means is pointer semantics.

    Oh really?

    Yes, really.

    fenris$ cat t.c
    #include <stdio.h>

    void func(int a[2][3])
    {
    a[10][50] = 123;
    }


    int main()
    {
    int a[2][3];
    func(a);
    return 0;
    }
    fenris$ cc t.c
    t.c:5:2: warning: array index 50 is past the end of the array (which contains 3
    elements) [-Warray-bounds]
    a[10][50] = 123;> ^ ~~
    t.c:3:11: note: array 'a' declared here
    void func(int a[2][3])
    ^
    1 warning generated.

    The standard imposes no requirements on diagnostics - in particular, it
    does not require them to correctly describe the problem - many
    implementations occasionally generate incorrectly worded diagnostics,
    even when describing legitimate errors, and this warning is one example.
    The actual type of a is int(*)[3], not int[2][3], a fact that you can
    confirm using sizeof(a) or &a (the difference between those types
    disappears in all other contexts, due to the implicit array=>pointer conversion).

    This message is posted to C++. While C++ has many differences from C,
    this isn't one of them, and in C++ a more convenient way to check the
    type of a is to use typeid(a).name(). Unfortunately, the resulting
    string is implementation-defined - on my machine it is a coded version
    of the type, rather than a proper C++ type specification. However, if
    you declare

    int b(*)[3];
    int c[2][3];

    then you should find that typeid(a)==typeid(b), and typeid(a) !=
    typeid(c). typeid(a).name() should be the same as typeid(b).name(), and
    both should be different from the typeid(c).name(). Let me know what you
    find when you try that.

    There is an array bounds violation in your code, but it's not in a
    itself, which is not an array. a is a pointer that can be used to point
    at any unqualified array with an element type of int[3], regardless of
    how large. However, whatever array you point a at, if a[10] refers to an
    actual element of that array, then a[10][50] violates the bounds of the 3-element array that a[10] points at, not the bounds of a itself, which
    has none.

    You'll get the same error if you change a[10][50] to a[1][50], and there wouldn't be any problem in func() itself if you changed it to a[10][2].

    If you pass a pointer to func() which will still point at a different
    element of the same array after 10 is added to it, then a[10][2] inside
    of func() would also have defined behavior - which would be to change
    the corresponding element of the pointed-at array, rather than the corresponding element of a, which has none.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ben Bacarisse@21:1/5 to JohnnyCameLater@whatsthetime.net on Wed Oct 27 17:21:34 2021
    JohnnyCameLater@whatsthetime.net writes:

    On Wed, 27 Oct 2021 11:39:16 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/27/21 10:30 AM, JohnnyCameLater@whatsthetime.net wrote:
    On Wed, 27 Oct 2021 11:07:03 +0200
    David Brown <david.brown@hesbynett.no> wrote:
    ....
    Do you have trouble understanding the difference between an array, and a >>>> pointer to the first element of the array?

    Do you have trouble understanding plain English? The syntax declares an >>array,
    the fact the compiler converts that into a pointer isn't the point. Pun
    intended.

    "array" vs. "pointer is a matter of semantics. [] vs * is a matter of >>syntax. All you can say about the syntax is that it contains []. In most >>contexts, [] in the syntax of a declaration corresponds to array
    semantics. However, the standard is quite explicit about the fact that,
    in this context, what [] means is pointer semantics.

    Oh really?

    Yes.

    fenris$ cat t.c
    #include <stdio.h>

    void func(int a[2][3])
    {
    a[10][50] = 123;
    }


    int main()
    {
    int a[2][3];
    func(a);
    return 0;
    }
    fenris$ cc t.c
    t.c:5:2: warning: array index 50 is past the end of the array (which contains 3
    elements) [-Warray-bounds]
    a[10][50] = 123;
    ^ ~~
    t.c:3:11: note: array 'a' declared here
    void func(int a[2][3])
    ^
    1 warning generated.

    Nuff said.

    Not really. Note that the compiler does not complain about the first
    index (10) being out of bounds. That's because (despite the badly
    worded note) it's only the three-element objects pointed to by 'a' that
    are in fact arrays with a known bound. Some compilers explain the issue
    more clearly.

    Here's gcc 11.2 giving much better, but identical, notes about these two functions:

    void func(int a[2][3])
    {
    a[10][50] = 123;
    }

    void func2(int (*a)[3])
    {
    a[10][50] = 123;
    }

    x.c: In function ‘func’:
    x.c:3:11: warning: array subscript 50 is above array bounds of ‘int[3]’ [-Warray-bounds]
    3 | a[10][50] = 123;
    | ~~~~~^~~~
    x.c:1:15: note: while referencing ‘a’
    1 | void func(int a[2][3])
    | ~~~~^~~~~~~
    x.c: In function ‘func2’:
    x.c:3:11: warning: array subscript 50 is above array bounds of ‘int[3]’ [-Warray-bounds]
    3 | a[10][50] = 123;
    | ~~~~~^~~~
    x.c:6:18: note: while referencing ‘a’
    6 | void func2(int (*a)[3])
    | ~~~~~~^~~~~

    --
    Ben.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to JohnnyCameLater@whatsthetime.net on Wed Oct 27 12:36:36 2021
    On 10/27/21 11:49 AM, JohnnyCameLater@whatsthetime.net wrote:
    On Wed, 27 Oct 2021 11:38:54 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/27/21 3:57 AM, RacingRabbit@watershipdown.co.uk wrote:
    ...
    There is no difference. As I said, what the compiler does with it under the >>> hood is not the point. The syntax declares an array.

    The syntax uses [] rather than *. The semantics are those of a pointer,
    not an array.

    The syntax defines an array. Why do you think you get compiler warnings if you go OOB with an index value? Try it.

    I did try it, and I don't get a compiler warning if I violate the
    supposed leading dimension of that array, which is the dimension that
    gets "adjusted" into a pointer:


    static void func (int a[2][3])
    {
    a[4][2] = 123;
    }

    int main(void)
    {
    int b[8][3] = 0;
    func(b);
    return b[4][2];
    }

    Subscripting with a[i][j] with j>2 would be just as much of an array
    violation if the type of a is int(*)[3] as it would be if the type of a
    were int[2][3].

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Chris Vine on Wed Oct 27 19:02:46 2021
    On 27/10/2021 18:41, Chris Vine wrote:
    On Wed, 27 Oct 2021 11:07:03 +0200
    David Brown <david.brown@hesbynett.no> wrote:
    On 27/10/2021 09:57, RacingRabbit@watershipdown.co.uk wrote:
    On Tue, 26 Oct 2021 12:23:37 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/26/21 11:30 AM, RacingRabbit@watershipdown.co.uk wrote:
    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."

    There is no difference. As I said, what the compiler does with it under the >>> hood is not the point. The syntax declares an array.

    When I run that program, it says "pointer". What do you get when you run it?

    Same. Not the point, its declared as an array.


    Do you have trouble understanding the difference between an array, and a
    pointer to the first element of the array?

    C arrays are /never/ passed to functions as parameters, nor are they
    used directly in most expressions. (You can pass a struct that contains
    an array as a field, but that's a struct, not an array.) It is a major
    inconsistency in the type system of C, but one that works well in practice.

    I know what you mean - they are never passed by value as parameters -
    but they can be passed by reference as an array rather than as a pointer
    to their first element. In C++ you have this available: there is no
    pointer decay and it will print 10:

    You can do that in C++, not in C. References in C++ are basically the
    same concept as const pointers (as distinct from pointers to const),
    just with a slightly different syntax. Your "func" here is not taking
    an array as a value parameter (like C, you can't do that in C++ with
    plain arrays), nor is it using array syntax as a means of specifying a
    pointer to the first element. It is a pointer to an /array/.


    void func (char (&arr)[10]) {
    std::cout << sizeof(arr) << std::endl;
    }

    int main() {
    char arr9[9];
    char arr10[10];
    // func(arr9); // won't compile
    func(arr10); // OK
    }

    In C and C++ you have this, which will also print 10:

    void func (char (*arr)[10]) {
    std::cout << sizeof(*arr) << std::endl;
    }


    Yes, that is much the same as a reference in C++ (though you'd be
    slightly closer with "char (*arr)[10]" ).


    (I think that references are often better than pointers - they can be
    safer and clearer. But they are not fundamentally different, as can be
    seen by looking at implementations - they are convenient syntactic sugar.)


    None of this detracts from the fact that your nym-shifting correspondent
    is not only clueless but also unable to learn.


    Indeed.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris Vine@21:1/5 to David Brown on Wed Oct 27 17:41:52 2021
    On Wed, 27 Oct 2021 11:07:03 +0200
    David Brown <david.brown@hesbynett.no> wrote:
    On 27/10/2021 09:57, RacingRabbit@watershipdown.co.uk wrote:
    On Tue, 26 Oct 2021 12:23:37 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/26/21 11:30 AM, RacingRabbit@watershipdown.co.uk wrote:
    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."

    There is no difference. As I said, what the compiler does with it under the hood is not the point. The syntax declares an array.

    When I run that program, it says "pointer". What do you get when you run it?

    Same. Not the point, its declared as an array.


    Do you have trouble understanding the difference between an array, and a pointer to the first element of the array?

    C arrays are /never/ passed to functions as parameters, nor are they
    used directly in most expressions. (You can pass a struct that contains
    an array as a field, but that's a struct, not an array.) It is a major inconsistency in the type system of C, but one that works well in practice.

    I know what you mean - they are never passed by value as parameters -
    but they can be passed by reference as an array rather than as a pointer
    to their first element. In C++ you have this available: there is no
    pointer decay and it will print 10:

    void func (char (&arr)[10]) {
    std::cout << sizeof(arr) << std::endl;
    }

    int main() {
    char arr9[9];
    char arr10[10];
    // func(arr9); // won't compile
    func(arr10); // OK
    }

    In C and C++ you have this, which will also print 10:

    void func (char (*arr)[10]) {
    std::cout << sizeof(*arr) << std::endl;
    }

    int main() {
    char arr9[9];
    char arr10[10];
    // func(&arr9); // won't compile
    func(&arr10); // OK
    }

    None of this detracts from the fact that your nym-shifting correspondent
    is not only clueless but also unable to learn.

    Do you know the difference between :

    int ar1[10];

    and

    std::array<int, 10> ar2;

    and how these may be passed to functions?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris Vine@21:1/5 to David Brown on Wed Oct 27 18:21:18 2021
    On Wed, 27 Oct 2021 19:02:46 +0200
    David Brown <david.brown@hesbynett.no> wrote:
    On 27/10/2021 18:41, Chris Vine wrote:
    On Wed, 27 Oct 2021 11:07:03 +0200
    David Brown <david.brown@hesbynett.no> wrote:
    [snip]
    C arrays are /never/ passed to functions as parameters, nor are they
    used directly in most expressions. (You can pass a struct that contains >> an array as a field, but that's a struct, not an array.) It is a major
    inconsistency in the type system of C, but one that works well in practice.

    I know what you mean - they are never passed by value as parameters -
    but they can be passed by reference as an array rather than as a pointer
    to their first element. In C++ you have this available: there is no pointer decay and it will print 10:

    You can do that in C++, not in C. References in C++ are basically the
    same concept as const pointers (as distinct from pointers to const),
    just with a slightly different syntax. Your "func" here is not taking
    an array as a value parameter (like C, you can't do that in C++ with
    plain arrays), nor is it using array syntax as a means of specifying a pointer to the first element. It is a pointer to an /array/.


    void func (char (&arr)[10]) {
    std::cout << sizeof(arr) << std::endl;
    }

    int main() {
    char arr9[9];
    char arr10[10];
    // func(arr9); // won't compile
    func(arr10); // OK
    }

    In C and C++ you have this, which will also print 10:

    void func (char (*arr)[10]) {
    std::cout << sizeof(*arr) << std::endl;
    }

    Yes, that is much the same as a reference in C++ (though you'd be
    slightly closer with "char (*arr)[10]" ).

    You are agreeing with me: I think you understand that although I am not entirely sure.

    The overarching point is that in the signature:

    void func (char arr[10])

    'arr' is not the name of an array at all but is the name of a pointer to
    char. You can pass func the address of any char object, whether or not
    a member of an array. This is the fundamental misunderstanding by the
    person in question.

    With:

    void func (char (&arr)[10])

    'arr' is a reference to an char array of size 10 and only that.

    With:

    void func (char (*arr)[10])

    'arr' is a pointer to an char array of size 10 and only that.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to JohnnyCameLater@whatsthetime.net on Thu Oct 28 05:17:54 2021
    JohnnyCameLater@whatsthetime.net wrote:
    On Wed, 27 Oct 2021 08:47:42 -0000 (UTC)
    Juha Nieminen <nospam@thanks.invalid> wrote:
    RacingRabbit@watershipdown.co.uk wrote:
    On Tue, 26 Oct 2021 12:23:37 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/26/21 11:30 AM, RacingRabbit@watershipdown.co.uk wrote:
    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."

    There is no difference. As I said, what the compiler does with it under the >>> hood is not the point. The syntax declares an array.

    No, it doesn't. It declares a pointer. What do you think this will print?

    Which parts of "syntax" and "under the hood" is confusing you. Perhaps in your
    own language?

    And which part of "an array and a pointer are different things" do you not understand?

    The function is *not* taking an array. It's taking a pointer. This is not just "it's taking a pointer under the hood". It's taking a pointer with respect to
    C syntax. Just because there are several alternative syntaxes to specify the type of pointer doesn't matter. It's still a pointer. Not just in its underlying representation, but at the language syntax level. These three declarations are identical and mean the same thing, they are merely using alternative syntaxes to express the same thing:

    void foo(int *ptr) {}
    void foo(int ptr[]) {}
    void foo(int ptr[10]) {}

    C++ supports function overloading. However, if you try to do the above in C++ you'll get redefinition errors, because all three are identical.

    Contrast that with, for example:

    void foo(int *ptr) {}
    void foo(int &ptr) {}

    Those two might result in identical binaries under the hood, but they are syntactically different, and will not cause a redefinition error.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Juha Nieminen on Wed Oct 27 23:10:54 2021
    On 10/27/2021 10:20 PM, Juha Nieminen wrote:
    JohnnyCameLater@whatsthetime.net wrote:
    void func(int a[2][3])
    {
    a[10][50] = 123;
    }

    If 'a' there is an array, what will sizeof(a) be?

    It can be equal to sizeof(void*)...



    int main()
    {
    int a[2][3];

    Contrast that to what sizeof(a) will be here.


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to JohnnyCameLater@whatsthetime.net on Thu Oct 28 05:20:52 2021
    JohnnyCameLater@whatsthetime.net wrote:
    void func(int a[2][3])
    {
    a[10][50] = 123;
    }

    If 'a' there is an array, what will sizeof(a) be?

    int main()
    {
    int a[2][3];

    Contrast that to what sizeof(a) will be here.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From JohnnyCameLater@whatsthetime.net@21:1/5 to Juha Nieminen on Thu Oct 28 09:30:00 2021
    On Thu, 28 Oct 2021 05:17:54 -0000 (UTC)
    Juha Nieminen <nospam@thanks.invalid> wrote:
    JohnnyCameLater@whatsthetime.net wrote:
    On Wed, 27 Oct 2021 08:47:42 -0000 (UTC)
    Juha Nieminen <nospam@thanks.invalid> wrote: >>>RacingRabbit@watershipdown.co.uk wrote:
    On Tue, 26 Oct 2021 12:23:37 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 10/26/21 11:30 AM, RacingRabbit@watershipdown.co.uk wrote:
    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."

    There is no difference. As I said, what the compiler does with it under the

    hood is not the point. The syntax declares an array.

    No, it doesn't. It declares a pointer. What do you think this will print?

    Which parts of "syntax" and "under the hood" is confusing you. Perhaps in >your
    own language?

    And which part of "an array and a pointer are different things" do you not >understand?

    Ok, you're either just plain thick or you're trolling now.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From JohnnyCameLater@whatsthetime.net@21:1/5 to Ben Bacarisse on Thu Oct 28 09:29:26 2021
    On Wed, 27 Oct 2021 17:21:34 +0100
    Ben Bacarisse <ben.usenet@bsb.me.uk> wrote:
    JohnnyCameLater@whatsthetime.net writes:
    "array" vs. "pointer is a matter of semantics. [] vs * is a matter of >>>syntax. All you can say about the syntax is that it contains []. In most >>>contexts, [] in the syntax of a declaration corresponds to array >>>semantics. However, the standard is quite explicit about the fact that, >>>in this context, what [] means is pointer semantics.

    Oh really?

    Yes.

    No.

    fenris$ cc t.c
    t.c:5:2: warning: array index 50 is past the end of the array (which >contains 3
    elements) [-Warray-bounds]
    a[10][50] = 123;
    ^ ~~
    t.c:3:11: note: array 'a' declared here
    void func(int a[2][3])
    ^
    1 warning generated.

    Nuff said.

    Not really. Note that the compiler does not complain about the first

    Yes really. The parser is treating it as an array and doing bounds checking. The fact it gets implicitely converted into a pointer later is irrelevant.

    index (10) being out of bounds. That's because (despite the badly

    So what? Clang compilation generally stops at the first error it finds.

    x.c:3:11: warning: array subscript 50 is above array bounds of ‘int[3]’ >[-Warray-bounds]
    3 | a[10][50] = 123;
    | ~~~~~^~~~
    x.c:6:18: note: while referencing ‘a’
    6 | void func2(int (*a)[3])
    | ~~~~~~^~~~~


    Thank you for proving my point that the gcc parser also treats it as an array.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to JohnnyCameLater@whatsthetime.net on Thu Oct 28 11:20:15 2021
    On 28/10/2021 10:29, JohnnyCameLater@whatsthetime.net wrote:
    On Wed, 27 Oct 2021 17:21:34 +0100
    Ben Bacarisse <ben.usenet@bsb.me.uk> wrote:
    JohnnyCameLater@whatsthetime.net writes:
    "array" vs. "pointer is a matter of semantics. [] vs * is a matter of
    syntax. All you can say about the syntax is that it contains []. In most >>>> contexts, [] in the syntax of a declaration corresponds to array
    semantics. However, the standard is quite explicit about the fact that, >>>> in this context, what [] means is pointer semantics.

    Oh really?

    Yes.

    No.

    fenris$ cc t.c
    t.c:5:2: warning: array index 50 is past the end of the array (which
    contains 3
    elements) [-Warray-bounds]
    a[10][50] = 123;
    ^ ~~
    t.c:3:11: note: array 'a' declared here
    void func(int a[2][3])
    ^
    1 warning generated.

    Nuff said.

    Not really. Note that the compiler does not complain about the first

    Yes really. The parser is treating it as an array and doing bounds checking. The fact it gets implicitely converted into a pointer later is irrelevant.

    index (10) being out of bounds. That's because (despite the badly

    So what? Clang compilation generally stops at the first error it finds.

    (Does it?)

    x.c:3:11: warning: array subscript 50 is above array bounds of ‘int[3]’ >> [-Warray-bounds]
    3 | a[10][50] = 123;
    | ~~~~~^~~~
    x.c:6:18: note: while referencing ‘a’
    6 | void func2(int (*a)[3])
    | ~~~~~~^~~~~


    Thank you for proving my point that the gcc parser also treats it as an array.

    In:

    void func(int a[2][3]) ...

    the type of 'a' is:

    'pointer to array 3 of int'.

    For parameters, the first level of a type starting T[] is reduced to T*,
    a pointer: T[][] becomes T(*)[].

    So arrays exist in the lower dimensions, but never in the primary one,
    which is where it would be necessary for arrays to be passed by value.

    But apart from that quibble, you're spot on. I can see now that you were
    pretty much right about everything, and all the long-standing experts
    here have no idea what they're talking about.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to JohnnyCameLater@whatsthetime.net on Fri Oct 29 05:11:53 2021
    JohnnyCameLater@whatsthetime.net wrote:
    And which part of "an array and a pointer are different things" do you not >>understand?

    Ok, you're either just plain thick or you're trolling now.

    Btw, I would warmly recommend that you do some self reflection.

    Ask yourself: *Why* are you just dodging the questions presented to you,
    and instead just throwing accusations and belittlement instead? If you were right, you wouldn't have to, and instead you could present arguments to
    what's actually said to you. Instead, you just dodge and throw insults.

    I think you know that you messed up, and now just stubborningly refuse
    to acknowledge it, or even to simply stop. Ask yourself why that's so,
    and whether that's actually something you really want to do.

    (Of course you will not do any of that. At least not right now. I'm
    just hoping that at some point in the future you'll start doing so.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to JohnnyCameLater@whatsthetime.net on Fri Oct 29 04:51:57 2021
    JohnnyCameLater@whatsthetime.net wrote:
    On Thu, 28 Oct 2021 05:20:52 -0000 (UTC)
    Juha Nieminen <nospam@thanks.invalid> wrote:
    JohnnyCameLater@whatsthetime.net wrote:
    void func(int a[2][3])
    {
    a[10][50] = 123;
    }

    If 'a' there is an array, what will sizeof(a) be?

    int main()
    {
    int a[2][3];

    Contrast that to what sizeof(a) will be here.

    FFS man, do you even understand whats being discussed here?

    Yes. You claim that in func(int a[2][3]) that 'a' there is
    declaring an array. According to you, it's declaring an array,
    and it doesn't matter what's happening "under the hood".

    If that were the case, if 'a' there were indeed an array, then
    it would behave like an array, and be the size of an array,
    just like that 'a' in the main() function.

    Yet, it doesn't. It actually behaves exactly like a pointer,
    not an array. Its size is that of a pointer, and its type is
    that of a pointer, and the declaration is exactly that of a
    pointer, at the language syntax level.

    The fact that you refuse to even answer my question, ie.
    what sizeof(a) is in those two cases, is telling.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to JohnnyCameLater@whatsthetime.net on Fri Oct 29 04:47:51 2021
    JohnnyCameLater@whatsthetime.net wrote:
    And which part of "an array and a pointer are different things" do you not >>understand?

    Ok, you're either just plain thick or you're trolling now.

    I'll take that as a concession. You know you are wrong and have no response.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From JohnnyCameLater@whatsthetime.net@21:1/5 to Juha Nieminen on Fri Oct 29 08:40:14 2021
    On Fri, 29 Oct 2021 05:11:53 -0000 (UTC)
    Juha Nieminen <nospam@thanks.invalid> wrote:
    JohnnyCameLater@whatsthetime.net wrote:
    And which part of "an array and a pointer are different things" do you not >>>understand?

    Ok, you're either just plain thick or you're trolling now.

    Btw, I would warmly recommend that you do some self reflection.

    Says Mr Depressive Aspie. Irony, much?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From JohnnyCameLater@whatsthetime.net@21:1/5 to Juha Nieminen on Fri Oct 29 08:39:50 2021
    On Fri, 29 Oct 2021 04:51:57 -0000 (UTC)
    Juha Nieminen <nospam@thanks.invalid> wrote:
    JohnnyCameLater@whatsthetime.net wrote:
    On Thu, 28 Oct 2021 05:20:52 -0000 (UTC)
    Juha Nieminen <nospam@thanks.invalid> wrote: >>>JohnnyCameLater@whatsthetime.net wrote:
    void func(int a[2][3])
    {
    a[10][50] = 123;
    }

    If 'a' there is an array, what will sizeof(a) be?

    int main()
    {
    int a[2][3];

    Contrast that to what sizeof(a) will be here.

    FFS man, do you even understand whats being discussed here?

    Yes. You claim that in func(int a[2][3]) that 'a' there is
    declaring an array. According to you, it's declaring an array,
    and it doesn't matter what's happening "under the hood".

    Do you understand the different stages of compilation? Have you heard of lexical analysis vs parsing vs code generation? I'm guessing not.

    If that were the case, if 'a' there were indeed an array, then
    it would behave like an array, and be the size of an array,
    just like that 'a' in the main() function.

    Honestly, I just give up.

    Its amusing how you see yourself as some kind of expert :)

    The fact that you refuse to even answer my question, ie.
    what sizeof(a) is in those two cases, is telling.

    I already answered it. Yes, it'll be the size of a pointer because BY THAT POINT it'll be treated as a pointer.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Juha Nieminen on Fri Oct 29 06:32:56 2021
    Juha Nieminen <nospam@thanks.invalid> writes:

    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:

    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.

    If gcc (or whichever compiler this is) doesn't give an outright error from trying to assign a const pointer to a non-const one without an explicit
    cast, then it's non-standard-conforming [...]

    I belive that statement is not correct. Can you cite a passage (or
    passages) in the C++ standard that supports this assertion?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Juha Nieminen on Fri Oct 29 06:34:36 2021
    Juha Nieminen <nospam@thanks.invalid> writes:

    There's literally zero reason not to use 'const' for pointers that
    are not intended to be used to modify the values they are pointing to.

    There is literally zero chance that this statement is not hyperbole.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Manfred on Fri Oct 29 06:41:33 2021
    Manfred <noname@add.invalid> writes:

    On 10/25/2021 8:11 PM, Keith Thompson wrote:

    Manfred <noname@add.invalid> writes:

    On 10/25/2021 4:59 PM, James Kuyper wrote:

    On 10/25/21 10:19 AM, RacingRabbit@watershipdown.co.uk wrote:

    <snip>

    No idea what UB means, but what'll happen is it'll crash
    immediately so you'll soon find out.

    UB means "Undefined Behavior", a technical term from the C
    standard which does NOT mean "behavior for which there is no
    definition". It means "behavior, upon use of a nonportable or
    erroneous program construct or of erroneous data, for which this
    document imposes no requirements" (3.4.3). Note that "this
    document" refers to the C standard; other documents (such as
    compiler documentation or ABI standards) might define the
    behavior, without changing the fact that is qualifies as
    "undefined behavior" as far as the C standard is concerned.

    Thanks for the quote, it made me compare it with the definition of
    UB in the C++ standard, which simply states "behavior for which
    this International Standard imposes no requirements".

    The lack of the sentence "upon use of a nonportable or erroneous
    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.

    It depends on the reader: whether it is some sort of text
    processing machine, a language lawyer or a human being.

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

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

    [...]

    Thank you for injecting a note of sanity into a discussion of
    what text in the C and C++ standards means.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Keith Thompson on Fri Oct 29 06:45:24 2021
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    RacingRabbit@watershipdown.co.uk writes:

    On Tue, 26 Oct 2021 10:42:24 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:

    On 10/26/21 4:21 AM, RacingRabbit@watershipdown.co.uk wrote:

    On Mon, 25 Oct 2021 10:48:57 -0700
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:

    RacingRabbit@watershipdown.co.uk writes:

    Any attempt to write to a read only program text area will
    result in a crash regardless of the language. It is implicit
    that its read only in C because C also provides the following
    initialisation which places the string (presumably) on the
    heap:

    char str[] = "hello world";

    I suggest that you would benefit more here from asking questions
    than from making assertions.

    I suggest you ease up on being patronising.

    You'll get less patronizing responses when you cease displaying
    such an 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.

    To be fair, sometimes he is one, and sometimes the other.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Keith Thompson@21:1/5 to Tim Rentsch on Fri Oct 29 11:11:19 2021
    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    RacingRabbit@watershipdown.co.uk writes:
    On Tue, 26 Oct 2021 10:42:24 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:

    On 10/26/21 4:21 AM, RacingRabbit@watershipdown.co.uk wrote:

    On Mon, 25 Oct 2021 10:48:57 -0700
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:

    RacingRabbit@watershipdown.co.uk writes:

    Any attempt to write to a read only program text area will
    result in a crash regardless of the language. It is implicit
    that its read only in C because C also provides the following
    initialisation which places the string (presumably) on the
    heap:

    char str[] = "hello world";

    I suggest that you would benefit more here from asking questions
    than from making assertions.

    I suggest you ease up on being patronising.

    You'll get less patronizing responses when you cease displaying
    such an 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.

    To be fair, sometimes he is one, and sometimes the other.

    To be fair, Tim, shut up.

    --
    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 */

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anand Hariharan@21:1/5 to james...@alumni.caltech.edu on Sun Oct 31 09:22:28 2021
    On Wednesday, October 27, 2021 at 11:26:23 AM UTC-5, james...@alumni.caltech.edu wrote:
    On 10/27/21 11:51 AM, JohnnyC...@whatsthetime.net wrote:
    On Wed, 27 Oct 2021 11:39:16 -0400

    void func(int a[2][3])
    {
    a[10][50] = 123;
    }


    int main()
    {
    int a[2][3];
    func(a);
    return 0;
    }
    fenris$ cc t.c
    t.c:5:2: warning: array index 50 is past the end of the array (which contains 3
    elements) [-Warray-bounds]
    a[10][50] = 123;> ^ ~~
    t.c:3:11: note: array 'a' declared here
    void func(int a[2][3])
    ^
    1 warning generated.
    The standard imposes no requirements on diagnostics - in particular, it
    does not require them to correctly describe the problem - many implementations occasionally generate incorrectly worded diagnostics,
    even when describing legitimate errors, and this warning is one example.
    The actual type of a is int(*)[3], not int[2][3], a fact that you can
    confirm using sizeof(a) or &a (the difference between those types
    disappears in all other contexts, due to the implicit array=>pointer conversion).

    This message is posted to C++. While C++ has many differences from C,
    this isn't one of them, and in C++ a more convenient way to check the
    type of a is to use typeid(a).name(). Unfortunately, the resulting
    string is implementation-defined - on my machine it is a coded version
    of the type, rather than a proper C++ type specification. However, if
    you declare

    int b(*)[3];
    int c[2][3];

    then you should find that typeid(a)==typeid(b), and typeid(a) !=
    typeid(c). typeid(a).name() should be the same as typeid(b).name(), and
    both should be different from the typeid(c).name(). Let me know what you
    find when you try that.


    I may have missed a reply (either from someone else or even yourself) but the declaration of 'b' is a syntax error. Looks like you meant

    int (*b)[3];

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Anand Hariharan on Mon Nov 1 00:13:17 2021
    On 10/31/21 12:22 PM, Anand Hariharan wrote:
    On Wednesday, October 27, 2021 at 11:26:23 AM UTC-5, james...@alumni.caltech.edu wrote:
    ...
    of the type, rather than a proper C++ type specification. However, if
    you declare

    int b(*)[3];
    ...
    I may have missed a reply (either from someone else or even yourself) but the declaration of 'b' is a syntax error. Looks like you meant

    int (*b)[3];

    Correct - the fact that no one else brought it up implies that very few
    people are bothering to pay attention to this thread. I'd guess that a
    lot of them have him killfiled by now.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to JohnnyCameLater@whatsthetime.net on Mon Nov 1 06:15:16 2021
    JohnnyCameLater@whatsthetime.net wrote:
    On Fri, 29 Oct 2021 05:11:53 -0000 (UTC)
    Juha Nieminen <nospam@thanks.invalid> wrote:
    JohnnyCameLater@whatsthetime.net wrote:
    And which part of "an array and a pointer are different things" do you not >>>>understand?

    Ok, you're either just plain thick or you're trolling now.

    Btw, I would warmly recommend that you do some self reflection.

    Says Mr Depressive Aspie. Irony, much?

    I still recommend that you think a bit about why you feel the need to insult people.

    When someone challenges your assertion, and you just respond with an insult, then you should think about why you are doing that.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to Tim Rentsch on Mon Nov 1 06:12:57 2021
    Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
    Juha Nieminen <nospam@thanks.invalid> writes:

    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:

    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.

    If gcc (or whichever compiler this is) doesn't give an outright error from >> trying to assign a const pointer to a non-const one without an explicit
    cast, then it's non-standard-conforming [...]

    I belive that statement is not correct. Can you cite a passage (or
    passages) in the C++ standard that supports this assertion?

    No. I made an assumption. I don't know if it's a true assumption.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to JohnnyCameLater@whatsthetime.net on Mon Nov 1 06:34:29 2021
    JohnnyCameLater@whatsthetime.net wrote:
    FFS man, do you even understand whats being discussed here?

    Yes. You claim that in func(int a[2][3]) that 'a' there is
    declaring an array. According to you, it's declaring an array,
    and it doesn't matter what's happening "under the hood".

    Do you understand the different stages of compilation? Have you heard of lexical analysis vs parsing vs code generation? I'm guessing not.

    No, it's you who is being confused by two alternative syntaxes to express
    the same thing.

    void foo(int a[10])

    and

    void foo(int *a)

    are declaring the exact same function. There is zero difference between
    them, other than just two alternative syntaxes. It's no different than,
    for example:

    int i = 5;

    and:

    signed int i = 5;

    The characters in the two lines may differ, but they are doing the exact
    same thing. In the function declaration the "10" within the brackets
    is completely superfluous and does absolutely nothing. It has no syntactic
    nor semantic meaning. You could just as well write it as "/*10*/" and it
    would have the same effect. And the brackets themselves are just an
    alternative way of expressing the same thing as the asterisk.

    So no, in "void foo(int a[10])" the 'a' there is not declaring an array,
    at any level of syntax or parsing. It's declaring a pointer.

    You can declare a "pointer to an array of 10 elements", which is *slightly* closer to what you want. Even here there are likewise two alternative ways
    to specify it:

    void foo(int (*a)[10]);
    void foo(int a[][10]);
    void foo(int a[124567][10]);

    Again, all three of the above are completely identical, and the number
    inside the first pair of brackets has no meaning and is superfluous.

    One way to discern that they are identical is that in C++ those are
    not overloads. They all declare the same function. (If they were
    function definitions, you would get an error about duplicate
    definitions.)

    Also here 'a' is not an array. It's a pointer. Just because it
    happens to point to an array of 10 ints doesn't make it an array.
    It's a pointer in every possible sense, at every possible stage
    of parsing, at every possible level of syntax.

    (The difference between an "int *a" and an "int (*a)[10]" is
    that the "unit" for the element pointed to by that 'a' is one
    single 'int' in the first case, and 10 'ints' in the second case.
    Thus, for example, 'a+1' would give you (typically) a pointer
    that's increased by 4 bytes in the first case, and 40 bytes in
    the second case.)

    Honestly, I just give up.

    I wish.

    Its amusing how you see yourself as some kind of expert :)

    No, I think it's *you* who considers yourself some kind of "expert",
    invoking the Dunning-Kruger effect quite hard.

    The fact that you refuse to even answer my question, ie.
    what sizeof(a) is in those two cases, is telling.

    I already answered it. Yes, it'll be the size of a pointer because BY THAT POINT it'll be treated as a pointer.

    There's no point at which it isn't considered a pointer.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Juha Nieminen on Mon Nov 1 08:53:44 2021
    On 01/11/2021 07:12, Juha Nieminen wrote:
    Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
    Juha Nieminen <nospam@thanks.invalid> writes:

    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:

    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.

    If gcc (or whichever compiler this is) doesn't give an outright error from >>> trying to assign a const pointer to a non-const one without an explicit
    cast, then it's non-standard-conforming [...]

    I belive that statement is not correct. Can you cite a passage (or
    passages) in the C++ standard that supports this assertion?

    No. I made an assumption. I don't know if it's a true assumption.


    Surely you mean you /didn't/ know if it was a true assumption. Now you
    know it is not true.

    If you think it should be true - that compilers should, by default, give
    fatal errors on this kind of thing, then you'll probably find many agree
    with you. A fatal error here would also have been conforming to the
    standards.

    Compiler writers always have a balance act here, especially in their
    default configurations without controlling flags - do they prioritise
    continued compilation of old code that worked with old tool versions
    despite flaws, or do they prioritise reducing the risk of flaws in
    current and future code by being stricter? There's no easy answer, and
    I expect for any such decision there will be people who believe they
    have made the wrong one. Language standards writers face similar dilemmas.

    All we can do is be careful about the compiler flags we choose
    ourselves, and encourage their use for others. And I suppose anyone who
    uses rextester.com could file a bug report for their site.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Juha Nieminen on Mon Nov 1 11:26:30 2021
    On 11/1/21 2:12 AM, Juha Nieminen wrote:
    Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
    Juha Nieminen <nospam@thanks.invalid> writes:
    ...
    If gcc (or whichever compiler this is) doesn't give an outright error from >>> trying to assign a const pointer to a non-const one without an explicit
    cast, then it's non-standard-conforming [...]

    I belive that statement is not correct. Can you cite a passage (or
    passages) in the C++ standard that supports this assertion?

    No. I made an assumption. I don't know if it's a true assumption.

    The C standard says:

    "The implementation shall not successfully translate a preprocessing translation unit containing a #error preprocessing directive unless it
    is part of a group skipped by conditional inclusion." (4p4).

    Ironically, any undefined behavior that a translation unit might have
    due to problems coming up during translation phases 1-4 might relieve an implementation of it's obligation to reject it.

    There's no other situation where it's prohibited for an implementation
    to successfully translate a program, no matter how many defects it
    contains, no matter how severe they are.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Alf P. Steinbach@21:1/5 to James Kuyper on Tue Nov 2 16:19:52 2021
    On 1 Nov 2021 16:26, James Kuyper wrote:
    On 11/1/21 2:12 AM, Juha Nieminen wrote:
    Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
    Juha Nieminen <nospam@thanks.invalid> writes:
    ...
    If gcc (or whichever compiler this is) doesn't give an outright error from >>>> trying to assign a const pointer to a non-const one without an explicit >>>> cast, then it's non-standard-conforming [...]

    I belive that statement is not correct. Can you cite a passage (or
    passages) in the C++ standard that supports this assertion?

    No. I made an assumption. I don't know if it's a true assumption.

    The C standard says:

    "The implementation shall not successfully translate a preprocessing translation unit containing a #error preprocessing directive unless it
    is part of a group skipped by conditional inclusion." (4p4).

    Ironically, any undefined behavior that a translation unit might have
    due to problems coming up during translation phases 1-4 might relieve an implementation of it's obligation to reject it.

    There's no other situation where it's prohibited for an implementation
    to successfully translate a program, no matter how many defects it
    contains, no matter how severe they are.

    A bit of thread-warping, but these last years I've ended up doing like

    #ifdef UNICODE
    # error "UNICODE must not be defined for an UTF-8 based program."
    # include <stop-compilation>
    #endif

    If only the C++ standard had some common means of stopping a compilation!

    And if only it also had some common means of stating that "the execution
    can and should never get here", apart from `for(;;){}`, which is not
    idiomatic.

    And, some way of saying "this parameter is intentionally unused and any
    use should be diagnosed", other than an awkward block comment around the
    name (yes I'm aware of C++17 `[[maybe_unused]]`, that thing sucks^1000).

    Uhm, come to think of it, it only /C++ compilers/ were designed to stop
    at first error, like I believe (think I remember) old Turbo C++ did.

    They could be lightning fast -- like, Blaisingly fast!


    - Alf

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Alf P. Steinbach on Tue Nov 2 12:54:25 2021
    On 11/2/21 11:19 AM, Alf P. Steinbach wrote:
    On 1 Nov 2021 16:26, James Kuyper wrote:
    On 11/1/21 2:12 AM, Juha Nieminen wrote:
    Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
    Juha Nieminen <nospam@thanks.invalid> writes:
    ...
    If gcc (or whichever compiler this is) doesn't give an outright error from
    trying to assign a const pointer to a non-const one without an explicit >>>>> cast, then it's non-standard-conforming [...]

    I belive that statement is not correct. Can you cite a passage (or
    passages) in the C++ standard that supports this assertion?

    No. I made an assumption. I don't know if it's a true assumption.

    The C standard says:

    "The implementation shall not successfully translate a preprocessing
    translation unit containing a #error preprocessing directive unless it
    is part of a group skipped by conditional inclusion." (4p4).

    Ironically, any undefined behavior that a translation unit might have
    due to problems coming up during translation phases 1-4 might relieve an
    implementation of it's obligation to reject it.

    There's no other situation where it's prohibited for an implementation
    to successfully translate a program, no matter how many defects it
    contains, no matter how severe they are.

    A bit of thread-warping, but these last years I've ended up doing like

    #ifdef UNICODE
    # error "UNICODE must not be defined for an UTF-8 based program."
    # include <stop-compilation>
    #endif

    If only the C++ standard had some common means of stopping a compilation!

    Sorry, ever since Bart's message with "Date: Thu, 21 Oct 2021 15:04:19
    +0100", included a comment about const in C, most of my messages on this
    thread were in response to messages about C (even though most of my
    messages could have said much the same thing about C++, with
    correspondingly different citations). I failed to notice that Keith's
    message with "Date: Tue, 26 Oct 2021 09:49:34 -0700" had returned the
    topic of this sub-thread back to C++. Therefore, my response to Juha's
    comment was not relevant.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paavo Helde@21:1/5 to All on Tue Nov 2 23:49:37 2021
    02.11.2021 17:19 Alf P. Steinbach kirjutas:

    Uhm, come to think of it, it only /C++ compilers/ were designed to stop
    at first error, like I believe (think I remember) old Turbo C++ did.

    Hmm, something like this?

    g++ -fmax-errors=1

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Alf P. Steinbach@21:1/5 to Paavo Helde on Wed Nov 3 00:38:34 2021
    On 2 Nov 2021 22:49, Paavo Helde wrote:
    02.11.2021 17:19 Alf P. Steinbach kirjutas:

    Uhm, come to think of it, it only /C++ compilers/ were designed to
    stop at first error, like I believe (think I remember) old Turbo C++ did.

    Hmm, something like this?

    g++ -fmax-errors=1

    At least some years ago, telling g++ to stop on error did not speed up
    things (e.g. it still prepares itself for re-synchronizing with the
    source and continuing spewing out diagnostics), and when the error
    message has supporting notes those subsequent lines are not shown.

    About same story with Visual C++, but I don't remember any details.

    The whole batch compilation idea is 1950-ish, as I see it.


    - Alf

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Keith Thompson on Mon Apr 25 07:39:56 2022
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:

    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    RacingRabbit@watershipdown.co.uk writes:

    On Tue, 26 Oct 2021 10:42:24 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:

    On 10/26/21 4:21 AM, RacingRabbit@watershipdown.co.uk wrote:

    On Mon, 25 Oct 2021 10:48:57 -0700
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:

    RacingRabbit@watershipdown.co.uk writes:

    Any attempt to write to a read only program text area will
    result in a crash regardless of the language. It is implicit
    that its read only in C because C also provides the following
    initialisation which places the string (presumably) on the
    heap:

    char str[] = "hello world";

    I suggest that you would benefit more here from asking
    questions than from making assertions.

    I suggest you ease up on being patronising.

    You'll get less patronizing responses when you cease displaying
    such an 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.

    To be fair, sometimes he is one, and sometimes the other.

    To be fair, Tim, shut up.

    I don't know why you find my comment so objectionable. I was
    only reporting some observations of past events. I think the
    same statement applies, to a greater or lesser degree, to most
    people who post here regularly and who present themselves as
    speaking authoritatively. It's no big deal.

    Furthermore, on a number of occasions James has posted comments
    about me, and not just about my writing but about my character.
    (Note that my comment was only about his writing, not about
    him personally, in case that was not evident.) So if you're
    going to be even handed, it seems like you should also tell
    him to shut up.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Keith Thompson@21:1/5 to Tim Rentsch on Mon Apr 25 10:59:14 2022
    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    RacingRabbit@watershipdown.co.uk writes:
    On Tue, 26 Oct 2021 10:42:24 -0400
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    [...]
    You'll get less patronizing responses when you cease displaying
    such an 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.

    To be fair, sometimes he is one, and sometimes the other.

    To be fair, Tim, shut up.

    I don't know why you find my comment so objectionable. I was
    only reporting some observations of past events. I think the
    same statement applies, to a greater or lesser degree, to most
    people who post here regularly and who present themselves as
    speaking authoritatively. It's no big deal.

    Furthermore, on a number of occasions James has posted comments
    about me, and not just about my writing but about my character.
    (Note that my comment was only about his writing, not about
    him personally, in case that was not evident.) So if you're
    going to be even handed, it seems like you should also tell
    him to shut up.

    Do you *seriously* not see how that was offensive?

    Re-reading the thread, I now see that it was not you who originally
    called James a "preening fool". It was another participant who chose to
    insult James while James was making correct and reasonable points. You
    just decided to jump in and endorse the insult. (I didn't remember the
    details because it all happened 6 months ago.)

    You did not comment on his writing. It was a personal insult directed
    at him, and it was entirely unconstructive. If you *meant* to comment
    on his writing, you could have done so; I believe your mastery of the
    English language is sufficient to make the distinction.

    James may or may not have posted comments about you; I don't recall.
    If he has, it's entirely possible that I didn't call him out because
    I didn't disagree. If I see any such comments in the future,
    I may or may not reply, but I feel no obligation to be even handed.

    Don't expect me to reply further, especially if you wait another 6
    months to post your next followup.

    --
    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 */

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)