• So You Think You Can Const?

    From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Tue Jan 7 20:32:50 2025
    From Newsgroup: comp.lang.c

    Hi everybody,

    I am back to programming in C after many years:
    indeed I have forgotten so many things, including
    how much I love this language. :)

    In particular, I am using C90, and compiling with
    `gcc ... -ansi -pedantic -Wall -Wextra` (as I have
    the requirement to ideally support any device).

    To the question, I was reading this, but I am not
    sure what the quoted passage means:

    Matt Stancliff, "So You Think You Can Const?",
    <https://matt.sh/sytycc>
    << Your compiler, at its discretion, may also choose
    to place any const declarations in read-only storage,
    so if you attempt to hack around the const blocks,
    you could get undefined behavior. >>

    I do not understand if just declaring that a pointer
    is to constant data may incur in that problem even
    if the pointed data was in fact allocated with malloc.
    I would say of course not, but I am not sure.

    E.g. consider this little internal helper of mine
    (which implements an interface that is public to
    do an internal thing...), where I am casting to
    pointer to non-constant data in order to free the
    pointed data (i.e. without warning):

    ```c
    static int MyStruct_free_(MyStruct_t const *pT) {
    assert(pT);

    free((MyStruct_t *)pT);

    return 0;
    }
    ```

    Assuming, as said, that the data was originally
    allocated with malloc, is that code safe or
    something can go wrong even in that case?

    Thank in advance for any help/insight,

    Julio
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Kaz Kylheku@643-408-1753@kylheku.com to comp.lang.c on Tue Jan 7 22:11:42 2025
    From Newsgroup: comp.lang.c

    On 2025-01-07, Julio Di Egidio <julio@diegidio.name> wrote:
    Hi everybody,

    I am back to programming in C after many years:
    indeed I have forgotten so many things, including
    how much I love this language. :)

    In particular, I am using C90, and compiling with
    `gcc ... -ansi -pedantic -Wall -Wextra` (as I have
    the requirement to ideally support any device).

    To the question, I was reading this, but I am not
    sure what the quoted passage means:

    Matt Stancliff, "So You Think You Can Const?",
    <https://matt.sh/sytycc>
    << Your compiler, at its discretion, may also choose
    to place any const declarations in read-only storage,
    so if you attempt to hack around the const blocks,
    you could get undefined behavior. >>

    An object defined with a type that is const-qualified
    could be put into write-protected storage.

    I do not understand if just declaring that a pointer
    is to constant data may incur in that problem even
    if the pointed data was in fact allocated with malloc.
    I would say of course not, but I am not sure.

    A pointer whose referenced type is const does not define an object of
    that type. A const-qualified object may point to data which is not const qualified. It may be converted to a pointer from whose type the
    qualifier is removed, and then the converted pointer can be used to
    modify the data.


    E.g. consider this little internal helper of mine
    (which implements an interface that is public to
    do an internal thing...), where I am casting to
    pointer to non-constant data in order to free the
    pointed data (i.e. without warning):

    ```c
    static int MyStruct_free_(MyStruct_t const *pT) {
    assert(pT);

    free((MyStruct_t *)pT);

    The prototype of free is

    void free(void *ptr);

    when it comes to pointers, the C language permits implicit conversions
    from "pointer to T" to "pointer to const T". Implicit meaning that
    no cast is required: you simply pass the "T *" value as a "const T *"
    function argument, or assign it to a "const T *" variable, etc.

    If yuo have some malloced storage which you are referencing with a
    "const T *" type, then you have a constraint violation if you free
    that pointer; hence the cast is required.

    Objects coming from malloc are not defined by a declaration.

    ISO C defines the term /effective type/ (// indicates italics)
    for the purposes of stating some rules regarding expressions accessing
    objects. "The /effective type/ of an object that is not a byte array,
    for an access to its stored value, is the declared type of the object"
    says the N3301 draft of C23 in section 6.5.1 Expressions/General.
    A footnote to this sentence clarifies that "allocated objects have no
    declared type", almost certainly meaning dynamically allocated by
    the malloc family.

    A chunk of memory from malloc is a kind of byte array, so the
    subsequents words apply to it:

    "If a value is stored into a byte array through an lvalue having a type
    that is not a byte type, then the type of the lvalue becomes the
    effective type of the object for that access and for subsequent accesses
    that do not modify the stored value."

    When we write values into the bytes of a malloced object, it takes on
    that type for subsequent reads.

    In the same section, rules are given regarding what type an expression
    may have which is accessing an object, in relation to that object's
    effective type.

    Indeed, the rules prohibit an object whose effective type is some "const
    T" from being accessed as a plain "T".

    However: it is not possible for a dynamically allocated object to
    have an effective type of "const T"!!!

    The reason is simple: the effective type of an allocated is established
    when an object is written, and then holds for subsequent reads. An
    object cannot be written through a "const T" lvalue.

    How you got the "const MyStruct_t *" pointer is that you first
    treated the object as "MyStruct_t *", and filled in its members.
    Then you cast the pointer to "const MyStruct_t *".

    Casting a pointer doesn't do anything to the referenced object's
    effective type; it is not a write operation on the object.

    Assuming, as said, that the data was originally
    allocated with malloc, is that code safe or
    something can go wrong even in that case?

    So yes, it is safe to treat malloced objects as const and then remove
    the const qualifier (as inescapably required by the API) when freeing.
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Wed Jan 8 09:46:46 2025
    From Newsgroup: comp.lang.c

    On 07/01/2025 20:32, Julio Di Egidio wrote:
    Hi everybody,

    I am back to programming in C after many years:
    indeed I have forgotten so many things, including
    how much I love this language. :)

    In particular, I am using C90, and compiling with
    `gcc ... -ansi -pedantic -Wall -Wextra` (as I have
    the requirement to ideally support any device).


    What devices do not have at least C99 compilers available - and yet /do/
    have standard C90 compilers available? What sort of code are you
    writing that should ideally run on an AVR Tiny with 2K of flash and 64
    bytes of ram, a DSP with 24-bit chars, and a TOP100 supercomputer? Have
    you thought about this in more detail?

    People who say they want their code to run on anything are invariably
    wildly exaggerating. People who say they want to write strictly standards-conforming code, especially C90, so that it will run
    everywhere, misunderstand the relationship between the C standards and real-world tools.

    I would say that the most portable language standard to use would be a
    subset of C99. Avoid complex numbers, VLAs, and wide/multibyte
    characters, and it will be compilable on all but the most obscure
    compilers. The use of <stdint.h> types make it far easier to write
    clear portable code while keeping good efficiency, and many C99 features
    let you write clearer, safer, and more efficient code. C90 was probably
    a good choice for highly portable code 15-20 years ago, but not now.
    (Your use of "malloc" eliminates far more potential devices for the code
    than choosing C99 ever could.)


    To the question, I was reading this, but I am not
    sure what the quoted passage means:

    Matt Stancliff, "So You Think You Can Const?",
    <https://matt.sh/sytycc>
    << Your compiler, at its discretion, may also choose
       to place any const declarations in read-only storage,
       so if you attempt to hack around the const blocks,
       you could get undefined behavior. >>

    I do not understand if just declaring that a pointer
    is to constant data may incur in that problem even
    if the pointed data was in fact allocated with malloc.
    I would say of course not, but I am not sure.


    You are mixing up declarations and definitions. It's not surprising -
    the article you reference is full of mistakes and bad advice.

    If you write "const uint32_t hello = 3;", you are /defining/ the object "hello", so it is a const object. It's value may not be changed in any
    way during its lifetime - attempting to do so is undefined behaviour,
    and the compiler will complain if it sees a direct attempt to change it.
    If the const object has program lifetime - it is a file-scope
    variable, or a static variable - the compiler can put it in read-only
    memory of some sort. On a microcontroller, that might mean flash memory
    - on a PC, it might mean a write-protected read-only memory page. For a
    local variable, it's much more likely that it will go in a register, on
    the stack, or be eliminated by optimisation - but if the initialiser is
    always the same, it could hypothetically also be placed in read-only memory.

    Suppose you have :

    int v = 123; // Non-const object definition
    const int * cp = &v; // Const pointer to non-const data
    int * p = (int *) cp; // Cast to non-const pointer
    *p = 456; // Change the target data

    This is allowed, because the original object definition was not a const definition.

    However, with this:

    int v = 123; // Const object definition
    const int * cp = &v; // Const pointer to const data
    int * p = (int *) cp; // Cast to non-const pointer
    *p = 456; // Undefined behaviour

    You can make the pointer to non-const, but trying to change an object
    that was /defined/ as const is undefined behaviour (even if it was not
    placed in read-only memory).

    When you use dynamic memory, however, you are not defining an object in
    the same way. If you write :

    const int * cp = malloc(sizeof(int));

    you are defining the object "p" as a pointer to type "const int" - but
    you are not defining a const int. You can cast "cp" to "int *" and use
    that new pointer to change the value.


    E.g. consider this little internal helper of mine
    (which implements an interface that is public to
    do an internal thing...), where I am casting to
    pointer to non-constant data in order to free the
    pointed data (i.e. without warning):

    ```c
    static int MyStruct_free_(MyStruct_t const *pT) {
        assert(pT);

        free((MyStruct_t *)pT);

        return 0;
    }
    ```

    Assuming, as said, that the data was originally
    allocated with malloc, is that code safe or
    something can go wrong even in that case?


    The code is safe in that it is not undefined behaviour - data allocated
    with malloc is never defined const. However, it is /unsafe/ in that it
    is doing something completely unexpected, given the function signature.

    When you have a function with a parameter of type "const T * p", this
    tells people reading it that the function will only read data via "p",
    and will never use "p" to change the data. The compiler will enforce
    this unless you specifically use casts to tell the compiler "I'm doing something that looks wrong - but I know it is right".

    Don't lie to your compiler. Don't lie to your fellow programmers (or yourself). Use const for things that you won't change - it is extremely
    rare that it is appropriate to cast away constness.



    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Ben Bacarisse@ben@bsb.me.uk to comp.lang.c on Wed Jan 8 11:25:53 2025
    From Newsgroup: comp.lang.c

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

    Suppose you have :

    int v = 123; // Non-const object definition
    const int * cp = &v; // Const pointer to non-const data
    int * p = (int *) cp; // Cast to non-const pointer
    *p = 456; // Change the target data

    This is allowed, because the original object definition was not a const definition.

    However, with this:

    int v = 123; // Const object definition
    const int * cp = &v; // Const pointer to const data
    int * p = (int *) cp; // Cast to non-const pointer
    *p = 456; // Undefined behaviour

    I think missed out the crucial "const" on the first line of the second
    example! It's always the way.

    You can make the pointer to non-const, but trying to change an object that was /defined/ as const is undefined behaviour (even if it was not placed in read-only memory).

    When you use dynamic memory, however, you are not defining an object in the same way. If you write :

    const int * cp = malloc(sizeof(int));

    I prefer

    const int *cp = malloc(sizeof *cp);

    you are defining the object "p" as a pointer to type "const int" - but you are not defining a const int. You can cast "cp" to "int *" and use that
    new pointer to change the value.
    --
    Ben.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Wed Jan 8 13:25:12 2025
    From Newsgroup: comp.lang.c

    On 08/01/2025 12:25, Ben Bacarisse wrote:
    David Brown <david.brown@hesbynett.no> writes:

    Suppose you have :

    int v = 123; // Non-const object definition
    const int * cp = &v; // Const pointer to non-const data
    int * p = (int *) cp; // Cast to non-const pointer
    *p = 456; // Change the target data

    This is allowed, because the original object definition was not a const
    definition.

    However, with this:

    int v = 123; // Const object definition

    Correction:
    const int v = 123;

    const int * cp = &v; // Const pointer to const data
    int * p = (int *) cp; // Cast to non-const pointer
    *p = 456; // Undefined behaviour

    I think missed out the crucial "const" on the first line of the second example! It's always the way.

    Fortunately, Usenet is a self-correcting medium :-) Thanks for pointing
    out that mistake, and I hope the OP sees your correction before getting confused by my copy-pasta error.


    You can make the pointer to non-const, but trying to change an object that >> was /defined/ as const is undefined behaviour (even if it was not placed in >> read-only memory).

    When you use dynamic memory, however, you are not defining an object in the >> same way. If you write :

    const int * cp = malloc(sizeof(int));

    I prefer

    const int *cp = malloc(sizeof *cp);


    That's a common preference. Personally, I prefer the former - I think
    it makes it clearer that we are allocating space for an int. Hopefully
    the OP will hang around this group and we'll get a chance to give advice
    and suggestions on many different aspects of C programming.

    you are defining the object "p" as a pointer to type "const int" - but you >> are not defining a const int. You can cast "cp" to "int *" and use that
    new pointer to change the value.


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Wed Jan 8 15:02:23 2025
    From Newsgroup: comp.lang.c

    On 07/01/2025 23:11, Kaz Kylheku wrote:
    On 2025-01-07, Julio Di Egidio <julio@diegidio.name> wrote:
    <snipped>
    In particular, I am using C90, and compiling with
    `gcc ... -ansi -pedantic -Wall -Wextra` (as I have
    the requirement to ideally support any device).

    To the question, I was reading this, but I am not
    sure what the quoted passage means:

    Matt Stancliff, "So You Think You Can Const?",
    <https://matt.sh/sytycc>
    << Your compiler, at its discretion, may also choose
    to place any const declarations in read-only storage,
    so if you attempt to hack around the const blocks,
    you could get undefined behavior. >>

    An object defined with a type that is const-qualified
    could be put into write-protected storage.

    What do you/we mean by "object" in this context? (Sorry, I do have
    forgotten, the glossary to begin with.)

    Overall, I am surmising this and only this might go write-protected:

    MyStruct_t const T = {...};

    While this one allocates a "byte-array", i.e. irrespective of how the
    pointer we are assigning it is declared:

    MyStruct_t const *pT = malloc(...);

    Is my understanding (to that point) correct?

    Objects coming from malloc are not defined by a declaration.

    I vaguely remember the use of "declaration" vs "definition" I used to
    find confusing at the time, but some three decades have passed, maybe
    now I can do better...

    ISO C defines the term /effective type/ (// indicates italics)
    for the purposes of stating some rules regarding expressions accessing objects. "The /effective type/ of an object that is not a byte array,
    for an access to its stored value, is the declared type of the object"
    says the N3301 draft of C23 in section 6.5.1 Expressions/General.
    A footnote to this sentence clarifies that "allocated objects have no declared type", almost certainly meaning dynamically allocated by
    the malloc family.

    Thank you so much overall, for the explanations and the final
    assessment: I couldn't hope for a better answer.

    How you got the "const MyStruct_t *" pointer is that you first
    treated the object as "MyStruct_t *", and filled in its members.
    Then you cast the pointer to "const MyStruct_t *".

    I was actually going out of my way trying to const-ify everything while
    trying not to cast anywhere: I totally misremembered what these things actually mean in C, and adding const everywhere possible is one of the
    habits I have meanwhile acquired from other languages. But it's coming back... I *will* do as you say. :)

    Thanks again very much,

    Julio

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Wed Jan 8 15:05:43 2025
    From Newsgroup: comp.lang.c

    On 08/01/2025 15:02, Julio Di Egidio wrote:
    irrespective of how the pointer we are assigning it is declared:

    Sorry, should read: how the pointer we are assigning it to is...

    -Julio

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Wed Jan 8 15:42:56 2025
    From Newsgroup: comp.lang.c

    On 08/01/2025 09:46, David Brown wrote:
    On 07/01/2025 20:32, Julio Di Egidio wrote:
    <snipped>
    In particular, I am using C90, and compiling with
    `gcc ... -ansi -pedantic -Wall -Wextra` (as I have
    the requirement to ideally support any device).

    People who say they want their code to run on anything are invariably
    wildly exaggerating.

    :) I do have embedded, and FPGAs, and even transpiling to e.g. Wasm,
    etc. in mind, my overall idea for now simply being: as long as the
    device comes with a C compiler that is not too broken. (I am also
    planning to distribute source files only: it also makes my life and
    coding so much easier, at the cost of not being able to
    "micro-optimize": where I am rather hoping that optimization can still
    come down the line if needed as an added pre or post processing step.)

    So, you might very well be right that "C90" isn't the best possible
    choice not even for my requirement, anyway I am at a pre-alpha stage, I
    am sure I will be tightening it up.

    People who say they want to write strictly
    standards-conforming code, especially C90, so that it will run
    everywhere, misunderstand the relationship between the C standards and real-world tools.

    So, now that I have qualified it with "any device coming with a C
    compiler (that is not too broken)", would you think coding it in "ANSI
    C" makes some sense?

    I would say that the most portable language standard to use would be a subset of C99.  Avoid complex numbers, VLAs, and wide/multibyte
    characters, and it will be compilable on all but the most obscure compilers.  The use of <stdint.h> types make it far easier to write
    clear portable code while keeping good efficiency, and many C99 features
    let you write clearer, safer, and more efficient code.  C90 was probably
    a good choice for highly portable code 15-20 years ago, but not now.
    (Your use of "malloc" eliminates far more potential devices for the code than choosing C99 ever could.)

    Assuming I don't in fact care if and how well a compiler does its job
    (in fact my policy for now is: as long as it compiles with GCC with
    those flags), what is wrong with "malloc"?

    When you have a function with a parameter of type "const T * p", this
    tells people reading it that the function will only read data via "p",

    Never mind, it's a private (static) method, so I am not "lying" to
    anybody: rather const and cast and almost everything in C is altogether something else...

    -Julio

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Ben Bacarisse@ben@bsb.me.uk to comp.lang.c on Wed Jan 8 15:16:03 2025
    From Newsgroup: comp.lang.c

    Julio Di Egidio <julio@diegidio.name> writes:

    On 07/01/2025 23:11, Kaz Kylheku wrote:
    On 2025-01-07, Julio Di Egidio <julio@diegidio.name> wrote:
    <snipped>
    In particular, I am using C90, and compiling with
    `gcc ... -ansi -pedantic -Wall -Wextra` (as I have
    the requirement to ideally support any device).

    To the question, I was reading this, but I am not
    sure what the quoted passage means:

    Matt Stancliff, "So You Think You Can Const?",
    <https://matt.sh/sytycc>
    << Your compiler, at its discretion, may also choose
    to place any const declarations in read-only storage,
    so if you attempt to hack around the const blocks,
    you could get undefined behavior. >>
    An object defined with a type that is const-qualified
    could be put into write-protected storage.

    What do you/we mean by "object" in this context? (Sorry, I do have forgotten, the glossary to begin with.)

    An object (in C) is a contiguous region of storage, the contents of
    which can represent values.

    Overall, I am surmising this and only this might go write-protected:

    MyStruct_t const T = {...};

    Yes, though you should extend your concern beyond what might be write-protected. Modifying an object whose type is const qualified is undefined, even if the object is in writable storage. A compiler may
    assume that such an object has not changed because in a program that has undefined behaviour, all bets are off. For example, under gcc with
    almost any optimisation this program prints 42:

    #include <stdio.h>

    void f(const int *ip)
    {
    *(int *)ip = 0;
    }

    int main(void)
    {
    const int a = 42;
    f(&a);
    printf("%d\n", a);
    }

    While this one allocates a "byte-array", i.e. irrespective of how the
    pointer we are assigning it is declared:

    MyStruct_t const *pT = malloc(...);

    Is my understanding (to that point) correct?

    Technically you get an object with no effective type. David's reply
    included some references to find out more about the effective type of an object, but it is safe to say that these only come into play if you are
    messing about with the way you access the allocated storage (for example accessing it as a MyStruct but then later as a floating point object).

    More relevant to a discussion of const is to ask what you plan to do
    with pT since you can't (without a cast) assign any useful value to the allocated object.

    It is generally better to use a non const-qualified pointer for the
    allocation but, when using the pointer, to pass it to functions that use
    the right type depending on whether they modify the pointed-to object or
    not. For example:

    MyStack *sp = malloc(*sp);
    ...
    stack_push(sp, 99);
    ...
    if (stack_empty(sp)) ...
    ...
    stack_free(sp);

    we would have

    void stack_push(MyStack *sp, int v) { ... }
    bool stack_empty(MyStack const *sp) { ... }
    void stack_free(MyStack *sp) { ... }
    --
    Ben.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Wed Jan 8 16:53:44 2025
    From Newsgroup: comp.lang.c

    On 08/01/2025 16:16, Ben Bacarisse wrote:

    Technically you get an object with no effective type. David's reply
    included some references to find out more about the effective type of an object, but it is safe to say that these only come into play if you are messing about with the way you access the allocated storage (for example accessing it as a MyStruct but then later as a floating point object).


    My turn for the little correction - it was Kaz that gave the helpful references, not me :-)

    But I can give the OP a useful reference - the site cppreference.com has
    a lot of accurate reference information about C (and C++, for those that
    want it). It is usually a little easier to read than the C standards,
    while still being very accurate.

    <https://en.cppreference.com/w/c/language> <https://en.cppreference.com/w/c/language/object> <https://en.cppreference.com/w/c/language/const> <https://en.cppreference.com/w/c/language/declarations>

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Wed Jan 8 17:05:24 2025
    From Newsgroup: comp.lang.c

    On 08/01/2025 16:16, Ben Bacarisse wrote:
    Julio Di Egidio <julio@diegidio.name> writes:
    On 07/01/2025 23:11, Kaz Kylheku wrote:
    On 2025-01-07, Julio Di Egidio <julio@diegidio.name> wrote:
    <snipped>
    To the question, I was reading this, but I am not
    sure what the quoted passage means:

    Matt Stancliff, "So You Think You Can Const?",
    <https://matt.sh/sytycc>
    << Your compiler, at its discretion, may also choose
    to place any const declarations in read-only storage,
    so if you attempt to hack around the const blocks,
    you could get undefined behavior. >>

    An object defined with a type that is const-qualified
    could be put into write-protected storage.

    What do you/we mean by "object" in this context? (Sorry, I do have
    forgotten, the glossary to begin with.)

    An object (in C) is a contiguous region of storage, the contents of
    which can represent values.

    Is that regardless of the stack/heap distinction, or is an "object"
    about heap-allocated/dynamic memory only? -- Anyway, I should in fact re-acquaint myself with the language reference instead of asking this question.)

    Overall, I am surmising this and only this might go write-protected:

    MyStruct_t const T = {...};

    Yes, though you should extend your concern beyond what might be write-protected. Modifying an object whose type is const qualified is undefined, even if the object is in writable storage.

    Yes, I am being a bit quick, but I definitely agree with that and indeed
    the priority of "defined behaviour" as a concern.

    While this one allocates a "byte-array", i.e. irrespective of how the
    pointer we are assigning it is declared:

    MyStruct_t const *pT = malloc(...);

    Is my understanding (to that point) correct?

    Technically you get an object with no effective type.

    OK.

    More relevant to a discussion of const is to ask what you plan to do
    with pT since you can't (without a cast) assign any useful value to the allocated object.

    Say my program unit implements AVL trees, with (conceptually speaking) constructors/destructors, navigation and retrieval, and of course
    manipulation (inserting, deleting, etc.).

    My idea (but I would think this is pretty "canonical" and, if it isn't,
    I am missing the mark) is: my public functions take/give "sealed"
    instances (with const members to const data), as the user is not
    supposed to directly manipulate/edit the data, OTOH of course my implementation is all about in-place editing...

    -Julio

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Wed Jan 8 17:18:21 2025
    From Newsgroup: comp.lang.c

    On 08/01/2025 15:42, Julio Di Egidio wrote:
    On 08/01/2025 09:46, David Brown wrote:
    On 07/01/2025 20:32, Julio Di Egidio wrote:
    <snipped>
    In particular, I am using C90, and compiling with
    `gcc ... -ansi -pedantic -Wall -Wextra` (as I have
    the requirement to ideally support any device).

    People who say they want their code to run on anything are invariably
    wildly exaggerating.

    :)  I do have embedded, and FPGAs, and even transpiling to e.g. Wasm,
    etc. in mind, my overall idea for now simply being: as long as the
    device comes with a C compiler that is not too broken.  (I am also
    planning to distribute source files only: it also makes my life and
    coding so much easier, at the cost of not being able to
    "micro-optimize": where I am rather hoping that optimization can still
    come down the line if needed as an added pre or post processing step.)


    Do you have experience with embedded programming (if so, what kind of devices)?

    So, you might very well be right that "C90" isn't the best possible
    choice not even for my requirement, anyway I am at a pre-alpha stage, I
    am sure I will be tightening it up.

    People who say they want to write strictly standards-conforming code,
    especially C90, so that it will run everywhere, misunderstand the
    relationship between the C standards and real-world tools.

    So, now that I have qualified it with "any device coming with a C
    compiler (that is not too broken)", would you think coding it in "ANSI
    C" makes some sense?


    No.

    There are basically two classes of small embedded devices - those that
    are usually programmed with gcc (and sometimes clang, and occasionally
    vastly expensive commercial tools), and those that are programmed using non-standard, limited and often expensive sort-of-C compilers. For
    people using gcc, clang, Green Hills, Code Warrior, or other quality
    tools on a 16-bit or 32-bit microcontroller, C99 is not a problem. C23
    is not a problem for the most popular toolchain for the most popular microcontroller core.

    For people using 8051, COP8, 68HC05, PIC16 or other long outdated
    brain-dead microcontrollers, you don't get standard C support at all.
    You program these in a device-specific variant of C full of extensions
    and extra restrictions - and the support is as close to the subset of
    C99 that I described as it is to standard C90.

    Those kinds of microcontrollers are now pretty much only used in legacy hardware or where companies have too much code investment that cannot reasonably be ported to something modern. (There are also a small
    number of niche use-cases.) So you can be confident that almost anyone
    using your software in embedded systems will be using a 32-bit core -
    most likely an ARM Cortex-M, but possibly RISC-V. And they will
    probably be using a toolchain that supports at least C17 (some people
    are still on older toolchains), whether it is gcc, clang, or commercial.
    Certainly solid C99 support is guaranteed. Everything else is niche,
    and no one will be using your software on niche systems.

    I would say that the most portable language standard to use would be a
    subset of C99.  Avoid complex numbers, VLAs, and wide/multibyte
    characters, and it will be compilable on all but the most obscure
    compilers.  The use of <stdint.h> types make it far easier to write
    clear portable code while keeping good efficiency, and many C99
    features let you write clearer, safer, and more efficient code.  C90
    was probably a good choice for highly portable code 15-20 years ago,
    but not now. (Your use of "malloc" eliminates far more potential
    devices for the code than choosing C99 ever could.)

    Assuming I don't in fact care if and how well a compiler does its job
    (in fact my policy for now is: as long as it compiles with GCC with
    those flags), what is wrong with "malloc"?


    If that's your starting assumption, then can we also assume that you
    don't expect anyone to use your code - certainly not on embedded
    systems? People often place too much emphasis on code efficiency, but
    in small embedded systems, efficient code means smaller, cheaper and
    lower power microcontrollers which is almost always relevant.

    The problem with malloc, however, is nothing to do with code efficiency.
    Dynamic memory is generally banned, or at least highly restricted, in serious embedded programming as it is a huge reliability risk. Most
    code on PC's can tolerate leaks - programs run for a bit, then stop and
    any leaked memory is recovered by the OS. Embedded programs usually
    never stop. PC programs can usually assume unlimited memory - embedded systems have very limited memory. PC OS's have memory managers to
    re-arrange memory and see few issues with fragmentation - embedded
    systems are easily killed by heap fragmentation. PC programs expect to
    have wildly varying timings - embedded systems are often real-time and
    do not do well with the non-deterministic timing you usually see with malloc/free implementations.

    Sometimes dynamic memory usage is unavoidable, but good code for
    embedded systems uses alternative solutions where possible, and usually
    uses specialised pools rather than a generic heap with malloc.

    When you have a function with a parameter of type "const T * p", this
    tells people reading it that the function will only read data via "p",

    Never mind, it's a private (static) method, so I am not "lying" to
    anybody: rather const and cast and almost everything in C is altogether something else...


    You are lying to yourself - and that is not a good start.

    Don't use const everywhere - use it where it is /helpful/. Don't use
    casts unless you have very good reason for it - get the types and
    qualifiers correct from the start. The article you referenced is rubbish.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Wed Jan 8 17:24:05 2025
    From Newsgroup: comp.lang.c

    On 08/01/2025 17:05, Julio Di Egidio wrote:
    On 08/01/2025 16:16, Ben Bacarisse wrote:
    <snip>
    More relevant to a discussion of const is to ask what you plan to do
    with pT since you can't (without a cast) assign any useful value to the
    allocated object.

    Say my program unit implements AVL trees, with (conceptually speaking) constructors/destructors, navigation and retrieval, and of course manipulation (inserting, deleting, etc.).

    My idea (but I would think this is pretty "canonical" and, if it isn't,
    I am missing the mark) is: my public functions take/give "sealed"
    instances (with const members to const data), as the user is not
    supposed to directly manipulate/edit the data, OTOH of course my implementation is all about in-place editing...

    P.S. To be clear, as I am still being a bit quick: I do not also mean
    "public destructors" should take a const pointer in input, i.e. apply as appropriate...

    And here is what my construction/destruction code is looking like at the moment, which should also make clear what I meant by "a private method implementing a public interface" and why:

    ```c
    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT;

    pT = malloc(sizeof(AvlTree_t));

    if (!pT) {
    return NULL;
    }

    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;

    return pT;
    }

    static int AvlTree_free_(AvlTree_t const *pT) {
    assert(pT);

    free((AvlTree_t *)pT);

    return 0;
    }

    AvlTree_t const *AvlTree_create(void const *pk) {
    return AvlTree_node(pk, NULL, NULL);
    }

    void AvlTree_destroy(AvlTree_t *pT) {
    AvlTree_visitPost(AvlTree_free_, pT);
    }
    ```

    -Julio

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Kaz Kylheku@643-408-1753@kylheku.com to comp.lang.c on Wed Jan 8 16:24:45 2025
    From Newsgroup: comp.lang.c

    On 2025-01-08, David Brown <david.brown@hesbynett.no> wrote:
    On 07/01/2025 20:32, Julio Di Egidio wrote:
    Hi everybody,

    I am back to programming in C after many years:
    indeed I have forgotten so many things, including
    how much I love this language. :)

    In particular, I am using C90, and compiling with
    `gcc ... -ansi -pedantic -Wall -Wextra` (as I have
    the requirement to ideally support any device).


    What devices do not have at least C99 compilers available - and yet /do/ have standard C90 compilers available? What sort of code are you
    writing that should ideally run on an AVR Tiny with 2K of flash and 64
    bytes of ram, a DSP with 24-bit chars, and a TOP100 supercomputer? Have
    you thought about this in more detail?

    I suspect that C90 is mainly related to retrocomputing now. Programs
    written in C90 will compile in installations of old operating systems.
    These maybe actual old installations on the original hardware, or
    historic installations re-created by retrocomputing enthusiasts on the
    original hardware or simulated hardware.

    There is probably a bit of legacy code out there requiring C90 support
    in a compiler, but maintenance on that code doesn't have to continue
    in C90.
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Wed Jan 8 17:35:42 2025
    From Newsgroup: comp.lang.c

    On 08/01/2025 17:18, David Brown wrote:
    On 08/01/2025 15:42, Julio Di Egidio wrote:
    On 08/01/2025 09:46, David Brown wrote:
    On 07/01/2025 20:32, Julio Di Egidio wrote:
    <snipped>
    In particular, I am using C90, and compiling with
    `gcc ... -ansi -pedantic -Wall -Wextra` (as I have
    the requirement to ideally support any device).

    People who say they want their code to run on anything are invariably
    wildly exaggerating.

    :)  I do have embedded, and FPGAs, and even transpiling to e.g. Wasm,
    etc. in mind, my overall idea for now simply being: as long as the
    device comes with a C compiler that is not too broken.
    <snip>

    Do you have experience with embedded programming (if so, what kind of devices)?

    TL;DR nearly zero. Siemens PLCs for small industrial automation, my own experimenting with Intel FPGAs (mainly for coprocessors), plus coding
    against device drivers for systems integration.

    So, now that I have qualified it with "any device coming with a C
    compiler (that is not too broken)", would you think coding it in "ANSI
    C" makes some sense?

    No.

    Cool. :) Please give me few hours, maybe less: I will be reading your
    reply with great interest.

    -Julio

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Phillip@nntp@fulltermprivacy.com to comp.lang.c on Wed Jan 8 11:45:01 2025
    From Newsgroup: comp.lang.c

    On 1/8/25 11:18 AM, David Brown wrote:
    For people using 8051, COP8, 68HC05, PIC16 or other long outdated brain- dead microcontrollers, you don't get standard C support at all. You
    program these in a device-specific variant of C full of extensions and
    extra restrictions - and the support is as close to the subset of C99
    that I described as it is to standard C90.

    Just a point of reference, there are still several "brain-dead" systems
    in modern use today that aren't old, some being invested as late as
    2019. That being said, your comment isn't completely accurate in that,
    there are some modern uses of things like the 6502 that can use standards-based C. In fact, you can use ANSI C89 and C90 with the 6502.
    I've done this for several modern pace makers as well as a smart
    prosthetic. So your statement is correct in 90% of cases but not all cases.

    (Also most car manufacturer's use the 6502 and other variants for their digital input analog gauges and warning light controls on their dashboards.)

    C89 and C90 are better for 8-bit systems then C99 and newer. Not that
    you can't do 8-bit on C99 but it's just not designed as well for it
    since C99 assumes you've moved on to at least 16-bit.

    But this is all based on the OP's specific use case for their
    application. I just wanted to chime in since I do primarily work on
    modern embedded systems that don't use "modern" microcontrollers and
    CPU's since they are still used in a wide range of modern devices that
    people don't even realize.
    --
    Phillip Frabott
    ----------
    - Adam: Is a void really a void if it returns?
    - Jack: No, it's just nullspace at that point.
    ----------
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Andrey Tarasevich@andreytarasevich@hotmail.com to comp.lang.c on Wed Jan 8 08:48:53 2025
    From Newsgroup: comp.lang.c

    On 01/07/25 11:32 AM, Julio Di Egidio wrote:

    To the question, I was reading this, but I am not
    sure what the quoted passage means:

    Matt Stancliff, "So You Think You Can Const?",
    <https://matt.sh/sytycc>
    << Your compiler, at its discretion, may also choose
       to place any const declarations in read-only storage,
       so if you attempt to hack around the const blocks,
       you could get undefined behavior. >>

    Strictly speaking, the passage is misleading. It dues not matter whether
    the compiler decided to place const data into read-only storage. If you
    "hack around" data constness (i.e. if you attempt to modify const data),
    you _always_ get undefined behavior, regardless of where the data is
    actually stored.

    I do not understand if just declaring that a pointer
    is to constant data may incur in that problem

    It can't. The original quoted passage is obviously meant to be about
    constant data, i.e. about _top-level_ constness.

    Meanwhile, pointer declared as "pointer to constant data" is simply a
    constant _access path_ to some data. Constness of an access path does
    not imply constness of the actual data that path leads to. Forcefully
    removing constness from the access path and subsequently modifying the
    pointed data is perfectly legal (if inelegant) and causes no undefined behavior. Of course, this is only valid when the data itself is not const.

    E.g. consider this little internal helper of mine
    (which implements an interface that is public to
    do an internal thing...), where I am casting to
    pointer to non-constant data in order to free the
    pointed data (i.e. without warning):

    ```c
    static int MyStruct_free_(MyStruct_t const *pT) {
        assert(pT);

        free((MyStruct_t *)pT);

        return 0;
    }
    ```

    Assuming, as said, that the data was originally
    allocated with malloc, is that code safe or
    something can go wrong even in that case?

    It is perfectly safe. One can even argue that standard declaration if
    `free` as `void free(void *)` is defective. It should have been `void free(const void *)` from the very beginning.
    --
    Best regards,
    Andrey
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Wed Jan 8 19:39:20 2025
    From Newsgroup: comp.lang.c

    On 08/01/2025 17:35, Julio Di Egidio wrote:
    On 08/01/2025 17:18, David Brown wrote:
    On 08/01/2025 15:42, Julio Di Egidio wrote:
    On 08/01/2025 09:46, David Brown wrote:
    On 07/01/2025 20:32, Julio Di Egidio wrote:
    <snipped>
    In particular, I am using C90, and compiling with
    `gcc ... -ansi -pedantic -Wall -Wextra` (as I have
    the requirement to ideally support any device).

    People who say they want their code to run on anything are
    invariably wildly exaggerating.

    :)  I do have embedded, and FPGAs, and even transpiling to e.g. Wasm,
    etc. in mind, my overall idea for now simply being: as long as the
    device comes with a C compiler that is not too broken.
    <snip>

    Do you have experience with embedded programming (if so, what kind of
    devices)?

    TL;DR nearly zero.  Siemens PLCs for small industrial automation, my own experimenting with Intel FPGAs (mainly for coprocessors), plus coding against device drivers for systems integration.


    OK. Small-systems embedded programming is quite a bit different from
    "big" systems programming.

    So, now that I have qualified it with "any device coming with a C
    compiler (that is not too broken)", would you think coding it in
    "ANSI C" makes some sense?

    No.

    Cool. :)  Please give me few hours, maybe less: I will be reading your reply with great interest.


    No problem. I'm glad you think it looks like it is worth reading!


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Wed Jan 8 14:10:27 2025
    From Newsgroup: comp.lang.c

    On 1/8/25 09:42, Julio Di Egidio wrote:
    On 08/01/2025 09:46, David Brown wrote:
    ...
    People who say they want to write strictly
    standards-conforming code, especially C90, so that it will run
    everywhere, misunderstand the relationship between the C standards and
    real-world tools.

    So, now that I have qualified it with "any device coming with a C
    compiler (that is not too broken)", would you think coding it in "ANSI
    C" makes some sense?

    I would agree with what you wrote, but probably not with what you meant.
    The first C standard, C89, was approved by ANSI. Later on, almost
    exactly the same standard was approved as C90 by ISO. They had to add
    three sections at the beginning to meet ISO requirements on how
    standards are organized. The result is that every section number from
    C89 corresponds to a section number 3 higher in C90.
    Since that time, every new version of the C standard has first been
    adopted by ISO, and then approved without changes by ANSI. Both
    organizations have a policy that the new version of a standard replaces
    the old one, which is no longer in effect. Therefore, ANSI C should,
    properly, refer to the current latest version of C that has been adopted
    by ANSI, which is C2023. I suspect that you were using "ANSI C" to refer
    to C89.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Wed Jan 8 14:10:54 2025
    From Newsgroup: comp.lang.c

    On 1/8/25 11:05, Julio Di Egidio wrote:
    On 08/01/2025 16:16, Ben Bacarisse wrote:
    Julio Di Egidio <julio@diegidio.name> writes:
    On 07/01/2025 23:11, Kaz Kylheku wrote:
    ...
    An object defined with a type that is const-qualified
    could be put into write-protected storage.

    What do you/we mean by "object" in this context? (Sorry, I do have
    forgotten, the glossary to begin with.)

    An object (in C) is a contiguous region of storage, the contents of
    which can represent values.

    Is that regardless of the stack/heap distinction, or is an "object"
    about heap-allocated/dynamic memory only? -- Anyway, I should in fact re-acquaint myself with the language reference instead of asking this question.)

    The standard makes no distinction between heap and stack memory. Those
    are merely implementation details, outside the scope of the standard. C
    can be implemented on hardware that provides no support for either a
    heap or a stack.
    Objects can have any one of four different storage durations: static,
    thread, automatic, and allocated. On implementations with a stack,
    objects with static or automatic storage can be implemented using the
    stack. On implementations with a heap, objects with allocated storage
    duration can be implemented using the heap. I'm not sufficiently
    familiar with multi-threaded programming to comment on how objects with
    thread storage duration may be implemented.

    The key issue that's connected to your questions is the fact that the
    relevant rule is about objects that are defined as being 'const'.
    Objects with automatic storage duration are never defined, so they
    cannot be defined to be 'const'. You can only obtain a void* value that
    points at such objects by calling one of the memory allocation functions (aligned_alloc, malloc, calloc, or realloc). You can convert that value
    into a pointer to non-const object type and then use that pointer to
    write an object of that type into that memory. Unless the object type is
    a character type, doing so will give that memory that type. You can also convert that value into a pointer to a const-qualified type, but that
    doesn't make the object it points at const.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Wed Jan 8 14:20:00 2025
    From Newsgroup: comp.lang.c

    On 1/8/25 14:10, James Kuyper wrote:
    ...
    Objects with automatic storage duration are never defined, so they
    cannot be defined to be 'const'.

    That was supposed to be "allocated", not "automatic".
    Objects with automatic storage duration can be defined as const:

    void func(void) {
    const int the_answer = 42;
    }

    On some implementations, they may even be stored in read-only memory.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Wed Jan 8 11:44:14 2025
    From Newsgroup: comp.lang.c

    Julio Di Egidio <julio@diegidio.name> writes:

    Hi everybody,

    I am back to programming in C after many years:
    indeed I have forgotten so many things, including
    how much I love this language. :)

    In particular, I am using C90, and compiling with
    `gcc ... -ansi -pedantic -Wall -Wextra` (as I have
    the requirement to ideally support any device).

    If it is absolutely necessary to have source that conforms to
    C90, you should at the very least compile it (on some machine)
    under both C90 rules and C99 rules. Frankly I doubt you will
    ever have a real need to target a C90-only environment, but if
    you do decide to go down that path then at least also compile the
    program as C99, to be sure the brokennesses of C90 don't bite
    you.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Wed Jan 8 11:52:12 2025
    From Newsgroup: comp.lang.c

    Phillip <nntp@fulltermprivacy.com> writes:

    C89 and C90 are better for 8-bit systems then C99 and newer. Not
    that you can't do 8-bit on C99 but it's just not designed as well
    for it since C99 assumes you've moved on to at least 16-bit.

    Which parts of the C99 standard support this assertion?
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Wed Jan 8 12:08:21 2025
    From Newsgroup: comp.lang.c

    David Brown <david.brown@hesbynett.no> writes:
    [...]
    int v = 123; // Non-const object definition
    const int * cp = &v; // Const pointer to non-const data

    cp isn't a const pointer, i.e., it's not a pointer object whose value
    cannot be changed. It's a pointer to const, specifically a pointer to
    const int. You could (and arguably should) make the pointer itself
    const by defining:

    const int *const cp = &v;

    v, as you point out, is a non-const object. *cp provides access to that
    object in a way that forbids changing the target via that pointer.

    int * p = (int *) cp; // Cast to non-const pointer
    *p = 456; // Change the target data

    You could also write:

    *(int*)p = 456;

    [...]
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Wed Jan 8 12:12:37 2025
    From Newsgroup: comp.lang.c

    Julio Di Egidio <julio@diegidio.name> writes:

    [...]

    Say my program unit implements AVL trees, with (conceptually
    speaking) constructors/destructors, navigation and retrieval, and
    of course manipulation (inserting, deleting, etc.).

    My idea (but I would think this is pretty "canonical" and, if it
    isn't, I am missing the mark) is: my public functions take/give
    "sealed" instances (with const members to const data), as the user
    is not supposed to directly manipulate/edit the data, OTOH of
    course my implementation is all about in-place editing...

    A better choice is to put the AVL code in a separate .c file,
    and give out only opaque types to clients. For example (disclaimer:
    not compiled):

    // in "avl.h"
    typedef struct avl_node_s *AVLTree;
    // note that the struct contents are not defined in the .h file

    ... declare interfaces that accept and return AVLTree values ...



    // in "avl.c"
    #include "avl.h"
    struct avl_node_s {
    // whatever members are needed
    };

    ... implementation of public interfaces and any supporting
    ... functions needed


    I might mention that some people don't like declaring a type name
    that includes the pointerness ('*') as part of the type. I think
    doing that is okay (and in fact more than just okay; better) in the
    specific case where the type name is being offered as an opaque
    type.

    Of course you could also make the opaque type be a pointer to a
    'const' struct type, if you wanted to, but the extra "protection" of
    const-ness doesn't add much, and might actually cost more than it
    buys you because of the additional casting that would be needed.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Wed Jan 8 20:14:40 2025
    From Newsgroup: comp.lang.c

    James Kuyper <jameskuyper@alumni.caltech.edu> writes:
    On 1/8/25 11:05, Julio Di Egidio wrote:

    <snip>

    I'm not sufficiently
    familiar with multi-threaded programming to comment on how objects with >thread storage duration may be implemented.

    Generally by reserving a register to point to the base of a
    per-thread region of memory allocated by the runtime. In x86,
    one of the otherwise useless segment registers (%gs) was used
    as the base address of the per-thread region (%fs is used by the kernel
    as a 'per-cpu' reg base address pointer). I believe Aarch64 uses
    x18.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Wed Jan 8 12:24:47 2025
    From Newsgroup: comp.lang.c

    Andrey Tarasevich <andreytarasevich@hotmail.com> writes:

    On 01/07/25 11:32 AM, Julio Di Egidio wrote:

    Assuming, as said, that the data was originally
    allocated with malloc, is [calling free on a pointer
    to const something] safe or something can go wrong
    even in that case?

    It is perfectly safe. One can even argue that standard declaration if
    free` as `void free(void *)` is defective. It should have been `void free(const void *)` from the very beginning.

    I think declaring the parameter as 'void *' rather than 'const void *'
    is a better choice. There is a fair chance that calling free() on a
    pointer to const anything is a programming error, and it would be good
    to catch that. If it isn't an error, then fixing the diagnostic is
    trivial. If it's a common pattern one could even define an inline
    function

    static inline void
    cfree( const void *p ){ free( (void*)p ); }

    and call that instead of free(). (Obviously the 'inline' should be
    taken out if compiling as C90.)
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Wed Jan 8 12:43:52 2025
    From Newsgroup: comp.lang.c

    Ben Bacarisse <ben@bsb.me.uk> writes:

    Julio Di Egidio <julio@diegidio.name> writes:

    On 07/01/2025 23:11, Kaz Kylheku wrote:

    On 2025-01-07, Julio Di Egidio <julio@diegidio.name> wrote:

    <snipped>

    In particular, I am using C90, and compiling with
    `gcc ... -ansi -pedantic -Wall -Wextra` (as I have
    the requirement to ideally support any device).

    To the question, I was reading this, but I am not
    sure what the quoted passage means:

    Matt Stancliff, "So You Think You Can Const?",
    <https://matt.sh/sytycc>
    << Your compiler, at its discretion, may also choose
    to place any const declarations in read-only storage,
    so if you attempt to hack around the const blocks,
    you could get undefined behavior. >>

    An object defined with a type that is const-qualified
    could be put into write-protected storage.

    What do you/we mean by "object" in this context? (Sorry, I do have
    forgotten, the glossary to begin with.)

    An object (in C) is a contiguous region of storage, the contents of
    which can represent values.

    Overall, I am surmising this and only this might go write-protected:

    MyStruct_t const T = {...};

    Yes, though you should extend your concern beyond what might be write-protected. Modifying an object whose type is const qualified
    is undefined, even if the object is in writable storage. A compiler
    may assume that such an object has not changed because in a program
    that has undefined behaviour, all bets are off. [...]

    We need to be careful about what is being asserted here. There
    are cases where a compiler may not assume that a const object
    has not changed, despite the rule that assigning to a const
    object is undefined behavior:

    #include <stdio.h>
    typedef union { const int foo; int bas; } Foobas;

    int
    main(){
    Foobas fb = { 0 };

    printf( " fb.foo is %d\n", fb.foo );
    fb.bas = 7;
    printf( " fb.foo is %d\n", fb.foo );
    return 0;
    }

    The object fb.foo is indeed a const object, but an access of
    fb.foo must not assume that it retains its original value after
    the assignment to fb.bas.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Wed Jan 8 13:01:15 2025
    From Newsgroup: comp.lang.c

    Andrey Tarasevich <andreytarasevich@hotmail.com> writes:
    On 01/07/25 11:32 AM, Julio Di Egidio wrote:
    To the question, I was reading this, but I am not
    sure what the quoted passage means:
    Matt Stancliff, "So You Think You Can Const?",
    <https://matt.sh/sytycc>
    << Your compiler, at its discretion, may also choose
       to place any const declarations in read-only storage,
       so if you attempt to hack around the const blocks,
       you could get undefined behavior. >>

    Strictly speaking, the passage is misleading. It dues not matter
    whether the compiler decided to place const data into read-only
    storage. If you "hack around" data constness (i.e. if you attempt to
    modify const data), you _always_ get undefined behavior, regardless of
    where the data is actually stored.

    And one possible result of undefined behavior (in some sense perhaps
    the worst) is that the code behaves just as you expected it to.

    The author of the article likely thought of "undefined behavior" as
    "the program crashes" or "something goes terribly wrong". In fact
    undefined behavior is simply behavior that is not defined; the C
    standard says nothing about what happens.

    And if the manifestation of that undefined behavior is that the
    code quietly does what you thought it would do, it could mean that
    you have a latent bug that's difficult to track down, and that will
    come back and bite you later.

    [...]
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From gazelle@gazelle@shell.xmission.com (Kenny McCormack) to comp.lang.c on Wed Jan 8 21:32:00 2025
    From Newsgroup: comp.lang.c

    In article <87tta9qauc.fsf@nosuchdomain.example.com>,
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    ...
    The author of the article likely thought of "undefined behavior" as
    "the program crashes" or "something goes terribly wrong". In fact
    undefined behavior is simply behavior that is not defined; the C
    standard says nothing about what happens.

    And if the manifestation of that undefined behavior is that the
    code quietly does what you thought it would do, it could mean that
    you have a latent bug that's difficult to track down, and that will
    come back and bite you later.

    Or it could mean that you are covered by some higher, more powerful
    standard, such as POSIX. Note that a lot of perfectly good,
    POSIX-compliant code is UB if you take the view that the C standard is your only coverage.

    Similarly, lots of perfectly good Linux code is UB if viewed through the
    lens of the POSIX standards. My point is that it is perfectly fine to rely
    on higher/better standards, provided, of course, that you correctly label
    your product (make it clear that you are covered by policies in addition to
    and superior to the ordinary C standards).
    --
    The randomly chosen signature file that would have appeared here is more than 4 lines long. As such, it violates one or more Usenet RFCs. In order to remain in compliance with said RFCs, the actual sig can be found at the following URL:
    http://user.xmission.com/~gazelle/Sigs/BestCLCPostEver
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Wed Jan 8 14:48:06 2025
    From Newsgroup: comp.lang.c

    On 1/8/2025 8:05 AM, Julio Di Egidio wrote:
    On 08/01/2025 16:16, Ben Bacarisse wrote:
    Julio Di Egidio <julio@diegidio.name> writes:
    On 07/01/2025 23:11, Kaz Kylheku wrote:
    On 2025-01-07, Julio Di Egidio <julio@diegidio.name> wrote:
    <snipped>
    To the question, I was reading this, but I am not
    sure what the quoted passage means:

    Matt Stancliff, "So You Think You Can Const?",
    <https://matt.sh/sytycc>
    << Your compiler, at its discretion, may also choose
          to place any const declarations in read-only storage,
          so if you attempt to hack around the const blocks,
          you could get undefined behavior. >>

    An object defined with a type that is const-qualified
    could be put into write-protected storage.

    What do you/we mean by "object" in this context?  (Sorry, I do have
    forgotten, the glossary to begin with.)

    An object (in C) is a contiguous region of storage, the contents of
    which can represent values.

    Is that regardless of the stack/heap distinction, or is an "object"
    about heap-allocated/dynamic memory only?  --  Anyway, I should in fact re-acquaint myself with the language reference instead of asking this question.)

    Overall, I am surmising this and only this might go write-protected:

       MyStruct_t const T = {...};

    Yes, though you should extend your concern beyond what might be
    write-protected.  Modifying an object whose type is const qualified is
    undefined, even if the object is in writable storage.

    Yes, I am being a bit quick, but I definitely agree with that and indeed
    the priority of "defined behaviour" as a concern.

    While this one allocates a "byte-array", i.e. irrespective of how the
    pointer we are assigning it is declared:

       MyStruct_t const *pT = malloc(...);

    Is my understanding (to that point) correct?

    Technically you get an object with no effective type.

    OK.

    More relevant to a discussion of const is to ask what you plan to do
    with pT since you can't (without a cast) assign any useful value to the
    allocated object.

    Say my program unit implements AVL trees, with (conceptually speaking) constructors/destructors, navigation and retrieval, and of course manipulation (inserting, deleting, etc.).

    My idea (but I would think this is pretty "canonical" and, if it isn't,
    I am missing the mark) is: my public functions take/give "sealed"
    instances (with const members to const data), as the user is not
    supposed to directly manipulate/edit the data, OTOH of course my implementation is all about in-place editing...

    Off topic, but for some reason you are making me think of the mutable
    keyword in C++:

    https://en.cppreference.com/w/cpp/language/cv

    ;^)
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Wed Jan 8 14:55:33 2025
    From Newsgroup: comp.lang.c

    On 1/8/2025 8:24 AM, Julio Di Egidio wrote:
    On 08/01/2025 17:05, Julio Di Egidio wrote:
    On 08/01/2025 16:16, Ben Bacarisse wrote:
    <snip>
    More relevant to a discussion of const is to ask what you plan to do
    with pT since you can't (without a cast) assign any useful value to the
    allocated object.

    Say my program unit implements AVL trees, with (conceptually speaking)
    constructors/destructors, navigation and retrieval, and of course
    manipulation (inserting, deleting, etc.).

    My idea (but I would think this is pretty "canonical" and, if it
    isn't, I am missing the mark) is: my public functions take/give
    "sealed" instances (with const members to const data), as the user is
    not supposed to directly manipulate/edit the data, OTOH of course my
    implementation is all about in-place editing...

    P.S.  To be clear, as I am still being a bit quick: I do not also mean "public destructors" should take a const pointer in input, i.e. apply as appropriate...

    And here is what my construction/destruction code is looking like at the moment, which should also make clear what I meant by "a private method implementing a public interface" and why:


    Sometimes I think the following can be kind of useful:

    struct foo
    {
    [...]
    };

    void foo_bar(struct foo const* const self, [...])
    {
    // self cannot change without a warning
    // self is const pointer to a const struct foo

    self = NULL; // zap!
    }

    ;^)



    ```c
    static AvlTree_t const *AvlTree_node(
        void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
        AvlTree_t *pT;

        pT = malloc(sizeof(AvlTree_t));

        if (!pT) {
            return NULL;
        }

        pT->pk = pk;
        pT->pL = pL;
        pT->pR = pR;

        return pT;
    }

    static int AvlTree_free_(AvlTree_t const *pT) {
        assert(pT);

        free((AvlTree_t *)pT);

        return 0;
    }

    AvlTree_t const *AvlTree_create(void const *pk) {
        return AvlTree_node(pk, NULL, NULL);
    }

    void AvlTree_destroy(AvlTree_t *pT) {
        AvlTree_visitPost(AvlTree_free_, pT);
    }
    ```

    -Julio


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Ben Bacarisse@ben@bsb.me.uk to comp.lang.c on Thu Jan 9 00:49:05 2025
    From Newsgroup: comp.lang.c

    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:

    Ben Bacarisse <ben@bsb.me.uk> writes:

    ... Modifying an object whose type is const qualified
    is undefined, even if the object is in writable storage. A compiler
    may assume that such an object has not changed because in a program
    that has undefined behaviour, all bets are off. [...]

    We need to be careful about what is being asserted here. There
    are cases where a compiler may not assume that a const object
    has not changed, despite the rule that assigning to a const
    object is undefined behavior:

    #include <stdio.h>
    typedef union { const int foo; int bas; } Foobas;

    int
    main(){
    Foobas fb = { 0 };

    printf( " fb.foo is %d\n", fb.foo );
    fb.bas = 7;
    printf( " fb.foo is %d\n", fb.foo );
    return 0;
    }

    The object fb.foo is indeed a const object, but an access of
    fb.foo must not assume that it retains its original value after
    the assignment to fb.bas.

    Yes, good point. There is also the case of

    volatile const int x;

    which the compiler can't assume won't change even though the program
    can't change it directly.
    --
    Ben.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Ben Bacarisse@ben@bsb.me.uk to comp.lang.c on Thu Jan 9 01:04:27 2025
    From Newsgroup: comp.lang.c

    Julio Di Egidio <julio@diegidio.name> writes:

    On 08/01/2025 16:16, Ben Bacarisse wrote:
    Julio Di Egidio <julio@diegidio.name> writes:
    On 07/01/2025 23:11, Kaz Kylheku wrote:
    On 2025-01-07, Julio Di Egidio <julio@diegidio.name> wrote:
    <snipped>
    To the question, I was reading this, but I am not
    sure what the quoted passage means:

    Matt Stancliff, "So You Think You Can Const?",
    <https://matt.sh/sytycc>
    << Your compiler, at its discretion, may also choose
    to place any const declarations in read-only storage,
    so if you attempt to hack around the const blocks,
    you could get undefined behavior. >>

    An object defined with a type that is const-qualified
    could be put into write-protected storage.

    What do you/we mean by "object" in this context? (Sorry, I do have
    forgotten, the glossary to begin with.)
    An object (in C) is a contiguous region of storage, the contents of
    which can represent values.

    Is that regardless of the stack/heap distinction, or is an "object" about heap-allocated/dynamic memory only?

    That's what an object is in all cases.

    -- Anyway, I should in fact
    re-acquaint myself with the language reference instead of asking this question.)

    Overall, I am surmising this and only this might go write-protected:

    MyStruct_t const T = {...};
    Yes, though you should extend your concern beyond what might be
    write-protected. Modifying an object whose type is const qualified is
    undefined, even if the object is in writable storage.

    Yes, I am being a bit quick, but I definitely agree with that and indeed
    the priority of "defined behaviour" as a concern.

    While this one allocates a "byte-array", i.e. irrespective of how the
    pointer we are assigning it is declared:

    MyStruct_t const *pT = malloc(...);

    Is my understanding (to that point) correct?
    Technically you get an object with no effective type.

    OK.

    More relevant to a discussion of const is to ask what you plan to do
    with pT since you can't (without a cast) assign any useful value to the
    allocated object.

    Say my program unit implements AVL trees, with (conceptually speaking) constructors/destructors, navigation and retrieval, and of course manipulation (inserting, deleting, etc.).

    My idea (but I would think this is pretty "canonical" and, if it isn't, I
    am missing the mark) is: my public functions take/give "sealed" instances (with const members to const data), as the user is not supposed to directly manipulate/edit the data, OTOH of course my implementation is all about in-place editing...

    See Tim's reply -- the best way to implement "sealed" instances is to
    use an opaque type where the "user code" simply can't see anything but a pointer to an otherwise unknown struct.

    A slight variation to what Tim was suggesting would be to take the
    pointer out of the typedef because that can allow you to define an
    interface with pointers to const and to non-const AVLtree objects:

    typedef struct AVLtree AVLtree;

    AVLtree *avl_create(void);
    void avl_add(AVLtree *tree, ...);
    void *avl_lookup(const AVLtree *tree, ...);

    and so on. Of course, if you use a more function style as Tim was
    suggesting there is no value in having this distinction.

    [I must say it's great to have a discussion about C programming for a
    change instead of endless threads about how awful C is and how this or
    that feature should be added to make it more like Rust/C++/Whatever.]
    --
    Ben.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Ben Bacarisse@ben@bsb.me.uk to comp.lang.c on Thu Jan 9 01:09:20 2025
    From Newsgroup: comp.lang.c

    Julio Di Egidio <julio@diegidio.name> writes:

    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT;

    pT = malloc(sizeof(AvlTree_t));

    if (!pT) {
    return NULL;
    }

    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;

    return pT;
    }

    Just on a side issue, I prefer to make tests like this positive so I'd
    write:

    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT = malloc(*pT);

    if (pT) {
    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;
    }
    return pT;
    }

    I'm not going to "make a case" for this (though I will if you want!) --
    I just think it helps to see lots of different styles.
    --
    Ben.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Kaz Kylheku@643-408-1753@kylheku.com to comp.lang.c on Thu Jan 9 04:24:56 2025
    From Newsgroup: comp.lang.c

    On 2025-01-09, Ben Bacarisse <ben@bsb.me.uk> wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT;

    pT = malloc(sizeof(AvlTree_t));

    if (!pT) {
    return NULL;
    }

    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;

    return pT;
    }

    Just on a side issue, I prefer to make tests like this positive so I'd
    write:

    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT = malloc(*pT);

    if (pT) {
    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;
    }
    return pT;
    }

    More generally:

    foo_handle *foo = foo_create();
    bar_handle *bar = foo ? bar_create(foo) : 0; // doesn't like null
    xyzzy_handle *xyz = xyzzy_create(42, bar, arg);
    container *con = malloc(sizeof *con);

    if (foo && bar && xyz && con) {
    // happy case: we have all three resources

    con->foo = foo;
    con->bar = bar;
    con->xyz = xyz;

    return con;
    }

    xyzzy_destroy(xyz);
    xyzzy_destroy(bar);
    if (foo)
    xyzzy_destroy(foo); // stupidly doesn't like null

    return 0;

    I'm not going to "make a case" for this (though I will if you want!) --
    I just think it helps to see lots of different styles.

    I might just have made the case. When more resources need to be
    acquired that might fail, it consolidates the happy case under one
    conjunctive test, and consolidates the cleanup in the unhappy case.
    Effectively it's almost if we have only two cases.

    A minor disadvantage is that in the unhappy flow, we may allocate
    resources past the point where it is obvious they are not going to be
    needed: if foo_create() failed, we are pointlessly calling
    xyzzy_create() and malloc for the container. It's possible that these
    succeed, and we are just going to turn around and free them.

    It's a form of consolidated error checking, like when we make
    several system calls and check them for errors as a batch;
    e.g. call fprintf several times and check for disk full (etc)
    just once.
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Thu Jan 9 07:49:47 2025
    From Newsgroup: comp.lang.c

    On 09/01/2025 02:09, Ben Bacarisse wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT;

    pT = malloc(sizeof(AvlTree_t));

    if (!pT) {
    return NULL;
    }

    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;

    return pT;
    }

    Just on a side issue, I prefer to make tests like this positive so I'd
    write:

    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT = malloc(*pT);

    if (pT) {
    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;
    }
    return pT;
    }

    I'm not going to "make a case" for this (though I will if you want!) --
    I just think it helps to see lots of different styles.

    That is *more* error prone, all the more so if it's not a 5 liner...

    -Julio

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Thu Jan 9 07:56:42 2025
    From Newsgroup: comp.lang.c

    On 09/01/2025 02:04, Ben Bacarisse wrote:
    Julio Di Egidio <julio@diegidio.name> writes:
    <snip>
    My idea (but I would think this is pretty "canonical" and, if it isn't, I
    am missing the mark) is: my public functions take/give "sealed" instances
    (with const members to const data), as the user is not supposed to directly >> manipulate/edit the data, OTOH of course my implementation is all about
    in-place editing...

    See Tim's reply -- the best way to implement "sealed" instances is to
    use an opaque type where the "user code" simply can't see anything but a pointer to an otherwise unknown struct.

    You say "best", I'd prefer "canonical" meaning what the people do who
    know what they are doing. :) But OK, that sounds indeed reasonable:
    I'll see what I get...

    -Julio

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Thu Jan 9 08:12:58 2025
    From Newsgroup: comp.lang.c

    On 08/01/2025 21:12, Tim Rentsch wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    [...]

    Say my program unit implements AVL trees, with (conceptually
    speaking) constructors/destructors, navigation and retrieval, and
    of course manipulation (inserting, deleting, etc.).

    My idea (but I would think this is pretty "canonical" and, if it
    isn't, I am missing the mark) is: my public functions take/give
    "sealed" instances (with const members to const data), as the user
    is not supposed to directly manipulate/edit the data, OTOH of
    course my implementation is all about in-place editing...

    A better choice is to put the AVL code in a separate .c file,
    and give out only opaque types to clients. For example (disclaimer:
    not compiled):

    // in "avl.h"
    typedef struct avl_node_s *AVLTree;
    // note that the struct contents are not defined in the .h file

    ... declare interfaces that accept and return AVLTree values ...

    // in "avl.c"
    #include "avl.h"
    struct avl_node_s {
    // whatever members are needed
    };

    ... implementation of public interfaces and any supporting
    ... functions needed


    I might mention that some people don't like declaring a type name
    that includes the pointerness ('*') as part of the type. I think
    doing that is okay (and in fact more than just okay; better) in the
    specific case where the type name is being offered as an opaque
    type.

    Of course you could also make the opaque type be a pointer to a
    'const' struct type, if you wanted to, but the extra "protection" of const-ness doesn't add much, and might actually cost more than it
    buys you because of the additional casting that would be needed.

    Thank you, I like that...

    -Julio

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Thu Jan 9 09:07:18 2025
    From Newsgroup: comp.lang.c

    On 08/01/2025 17:18, David Brown wrote:
    On 08/01/2025 15:42, Julio Di Egidio wrote:
    <snip>
      So you can be confident that almost anyone
    using your software in embedded systems will be using a 32-bit core -
    most likely an ARM Cortex-M, but possibly RISC-V.  And they will
    probably be using a toolchain that supports at least C17 (some people
    are still on older toolchains), whether it is gcc, clang, or commercial.
     Certainly solid C99 support is guaranteed.  Everything else is niche, and no one will be using your software on niche systems.

    Even my fridge should be able to run it... I am writing a Prolog
    compiler, but more generally I'd be mostly writing algorithms-data
    structures things.

    That said, one thing nobody has been explaining is why C99 is superior
    to C89/C90, except for some coding conveniences as far as I have read
    online: and I must say here that I do prefer the good old ways,
    including style-wise, in most cases...

    But, more concretely, what I do not understand of your reply is, OK the plethora of architectures and compilers and languages, but I cannot even
    begin to cope with that, can I, and why should I when I can e.g. just
    have a "config" header (up to even a pre-preprocessor or whatever pre or post-transformations are needed) where I re-define "malloc" or even
    "int" as I like for a specific target?

    The underlying idea being I won't care at all, I just pick a reasonable
    and reasonably standard variant of the C language as base, and I just distribute source code, the user must compile it.

    -Julio

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Thu Jan 9 09:12:06 2025
    From Newsgroup: comp.lang.c

    On 08/01/2025 17:48, Andrey Tarasevich wrote:
    On 01/07/25 11:32 AM, Julio Di Egidio wrote:
    <snip>
    E.g. consider this little internal helper of mine
    (which implements an interface that is public to
    do an internal thing...), where I am casting to
    pointer to non-constant data in order to free the
    pointed data (i.e. without warning):

    ```c
    static int MyStruct_free_(MyStruct_t const *pT) {
         assert(pT);

         free((MyStruct_t *)pT);

         return 0;
    }
    ```

    Assuming, as said, that the data was originally
    allocated with malloc, is that code safe or
    something can go wrong even in that case?

    It is perfectly safe. One can even argue that standard declaration if
    `free` as `void free(void *)` is defective. It should have been `void free(const void *)` from the very beginning.

    I do not understand that: `free` is changing the pointed data, so how
    can `const void *` even be "correct"?

    -Julio

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Thu Jan 9 10:07:11 2025
    From Newsgroup: comp.lang.c

    On 09/01/2025 09:07, Julio Di Egidio wrote:
    On 08/01/2025 17:18, David Brown wrote:
    On 08/01/2025 15:42, Julio Di Egidio wrote:
    <snip>
      So you can be confident that almost anyone using your software in
    embedded systems will be using a 32-bit core - most likely an ARM
    Cortex-M, but possibly RISC-V.  And they will probably be using a
    toolchain that supports at least C17 (some people are still on older
    toolchains), whether it is gcc, clang, or commercial.   Certainly
    solid C99 support is guaranteed.  Everything else is niche, and no one
    will be using your software on niche systems.

    Even my fridge should be able to run it...  I am writing a Prolog
    compiler, but more generally I'd be mostly writing algorithms-data structures things.

    That said, one thing nobody has been explaining is why C99 is superior
    to C89/C90, except for some coding conveniences as far as I have read online: and I must say here that I do prefer the good old ways,
    including style-wise, in most cases...

    But, more concretely, what I do not understand of your reply is, OK the plethora of architectures and compilers and languages, but I cannot even begin to cope with that, can I, and why should I when I can e.g. just
    have a "config" header (up to even a pre-preprocessor or whatever pre or post-transformations are needed) where I re-define "malloc" or even
    "int" as I like for a specific target?

    The underlying idea being I won't care at all, I just pick a reasonable
    and reasonably standard variant of the C language as base, and I just distribute source code, the user must compile it.

    P.S. Of course, I do not expect my code to be super-optimized either, I
    only strive for the best I can get by "generic programming", again the
    idea being that at least micro-optimization (very local, very low-level) remains possible by ad-hoc program transformations: or e.g. I'd code
    against a "generic" math library that is a thin wrapper by default
    around a standard library, but can then be "relinked/redirected" to
    anything at compile time... Of course, assuming the code is written to
    be conducive of such "parametricity", I do not expect it to come for free.

    Does it make sense? Does it work?

    -Julio

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Thu Jan 9 10:35:52 2025
    From Newsgroup: comp.lang.c

    On 09/01/2025 05:24, Kaz Kylheku wrote:
    On 2025-01-09, Ben Bacarisse <ben@bsb.me.uk> wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT;

    pT = malloc(sizeof(AvlTree_t));

    if (!pT) {
    return NULL;
    }

    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;

    return pT;
    }

    Just on a side issue, I prefer to make tests like this positive so I'd
    write:

    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT = malloc(*pT);

    if (pT) {
    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;
    }
    return pT;
    }

    More generally:

    foo_handle *foo = foo_create();
    bar_handle *bar = foo ? bar_create(foo) : 0; // doesn't like null
    xyzzy_handle *xyz = xyzzy_create(42, bar, arg);
    container *con = malloc(sizeof *con);

    if (foo && bar && xyz && con) {
    // happy case: we have all three resources

    con->foo = foo;
    con->bar = bar;
    con->xyz = xyz;

    return con;
    }

    xyzzy_destroy(xyz);
    xyzzy_destroy(bar);
    if (foo)
    xyzzy_destroy(foo); // stupidly doesn't like null

    return 0;

    I'm not going to "make a case" for this (though I will if you want!) --
    I just think it helps to see lots of different styles.

    I might just have made the case. When more resources need to be
    acquired that might fail, it consolidates the happy case under one conjunctive test, and consolidates the cleanup in the unhappy case. Effectively it's almost if we have only two cases.

    A minor disadvantage is that in the unhappy flow, we may allocate
    resources past the point where it is obvious they are not going to be
    needed: if foo_create() failed, we are pointlessly calling
    xyzzy_create() and malloc for the container. It's possible that these succeed, and we are just going to turn around and free them.


    How about taking the idea slightly further and making the later
    allocations conditional too?

    foo_handle *foo = foo_create();
    bar_handle *bar = foo ? bar_create(foo) : 0; // doesn't like null
    xyzzy_handle *xyz = bar ? xyzzy_create(42, bar, arg) : 0;
    container *con = xyz ? malloc(sizeof *con) : 0;

    if (con) {
    // happy case: we have all three resources

    ...

    If you are going to use that style (and I not arguing for or against
    it), go all in!


    It's a form of consolidated error checking, like when we make
    several system calls and check them for errors as a batch;
    e.g. call fprintf several times and check for disk full (etc)
    just once.




    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Thu Jan 9 03:21:23 2025
    From Newsgroup: comp.lang.c

    Julio Di Egidio <julio@diegidio.name> writes:
    On 08/01/2025 17:48, Andrey Tarasevich wrote:
    [...]
    It is perfectly safe. One can even argue that standard declaration
    if `free` as `void free(void *)` is defective. It should have been
    `void free(const void *)` from the very beginning.

    I do not understand that: `free` is changing the pointed data, so how
    can `const void *` even be "correct"?

    No, `free` doesn't (necessarily) change the pointed-to data.
    Any attempt to access the allocated data after free() has undefined
    behavior, so it might be modified, but all free() needs to do is
    make it available for further allocation. It might do so without
    touching the data itself.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Thu Jan 9 12:26:24 2025
    From Newsgroup: comp.lang.c

    On 09/01/2025 12:21, Keith Thompson wrote:
    Julio Di Egidio <julio@diegidio.name> writes:
    On 08/01/2025 17:48, Andrey Tarasevich wrote:
    [...]
    It is perfectly safe. One can even argue that standard declaration
    if `free` as `void free(void *)` is defective. It should have been
    `void free(const void *)` from the very beginning.

    I do not understand that: `free` is changing the pointed data, so how
    can `const void *` even be "correct"?

    No, `free` doesn't (necessarily) change the pointed-to data.
    Any attempt to access the allocated data after free() has undefined
    behavior,

    I would indeed call that a change!

    Anyway I see the point, thanks for explaining.

    -Julio

    so it might be modified, but all free() needs to do is
    make it available for further allocation. It might do so without
    touching the data itself.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Thu Jan 9 13:15:01 2025
    From Newsgroup: comp.lang.c

    On 08/01/2025 17:45, Phillip wrote:
    On 1/8/25 11:18 AM, David Brown wrote:
    For people using 8051, COP8, 68HC05, PIC16 or other long outdated
    brain- dead microcontrollers, you don't get standard C support at all.
    You program these in a device-specific variant of C full of extensions
    and extra restrictions - and the support is as close to the subset of
    C99 that I described as it is to standard C90.

    Just a point of reference, there are still several "brain-dead" systems
    in modern use today that aren't old, some being invested as late as
    2019.

    There are very few new versions of 8-bit CISC microcontrollers being
    developed - the rate has been dropping steadily since the Cortex M
    family rose to dominance. The decay is probably approximately
    exponential, in terms of development of new devices, production and sale
    of existing devices, and development of new products using these
    devices. They are not /all/ gone - but you need /very/ good reason for considering them for anything new, whether it is software or hardware.

    The only 8-bit core that can be viewed as "alive and well", is the AVR.
    It is a RISC design and a good deal more C and general software friendly
    than these other devices. The most common toolchain for the AVR is gcc,
    and you can use normal standard C with it. You do have to consider some
    of its idiosyncrasies if you want efficient results, but you /can/ write normal C code for it.

    That being said, your comment isn't completely accurate in that,
    there are some modern uses of things like the 6502 that can use standards-based C. In fact, you can use ANSI C89 and C90 with the 6502.

    The 6502 is one of the better 8-bit cpu cores - you can get reasonable
    code with mostly standard C (C99 if you like) using a compiler like
    SDCC. You don't /need/ as many extra target-specific keywords and
    extensions to deal with multiple memory banks, independent address
    spaces for ram and constant data, long and short pointers, non-recursive functions, and so on, as you need for useable results on an 8051 or PIC.
    But you probably still use some of these to get efficient results,
    such as choosing which variables go in the fast zero page area.

    Also note that there is a huge difference between being able to write significant parts of the code for a microcontroller using only standard
    C, and being able to use a significant fraction of the standard C
    language and library (at least the "freestanding" part) in your code.
    For example, I've used a compiler for the PIC16 that supported structs
    and arrays, but not arrays of structs or structs containing arrays. If
    the code does not use these features, you can write it in standard C -
    but plenty of perfectly ordinary standard C code would not work with
    that compiler.

    There is also a huge difference between being /able/ to write the code
    in only standard C, and pure standard C being an appropriate way to
    write the code for the microcontroller. Using the target-specific
    features of these kinds of devices and their tools makes a massive
    difference to code efficiency. Basically, if you are trying to stick to
    pure standard C for an 8051 or 68HC05, you are doing it wrong.

    I've done this for several modern pace makers as well as a smart
    prosthetic. So your statement is correct in 90% of cases but not all cases.

    I believe that goes under the category of "niche" :-) For some types of application, you stick to what you have tested - no one wants to have
    the first pacemaker with a new microcontroller!

    Of course in this field there are always exceptions - no generalisation
    is going to be correct in absolutely all cases. But I'd guess my
    statement was accurate in 99.9% or more cases.


    (Also most car manufacturer's use the 6502 and other variants for their digital input analog gauges and warning light controls on their
    dashboards.)

    Have you a reference for that claim? I am very confident that it is not
    the case. The 6502 was primarily developed as a microprocessor core,
    not a microcontroller core - it was found in early microcomputers and
    games consoles. It was also used in early embedded systems, but once microcontrollers became common, they dominated quickly in numbers. (Microprocessors were used for high-end embedded systems, like embedded
    PC's with x86 cpus and network equipment with m68k processors.) I don't
    know that the 6502 was ever common in the automotive industry - but I
    can't believe it was ever used by "most" car manufacturers.


    C89 and C90 are better for 8-bit systems then C99 and newer. Not that
    you can't do 8-bit on C99 but it's just not designed as well for it
    since C99 assumes you've moved on to at least 16-bit.


    My point is that almost no general-purpose software written now will
    ever be used on 8-bit devices, especially not 8-bit CISC cores. For
    most software written on these cores, standard C is not a practical
    option anyway. And the cores are used in very conservative situations,
    such as when there is a lot of legacy code or when many years or decades
    of field testing is important - new external software will not be used
    by such developers. So it makes no sense to me to restrict the code to
    an old standard because of the /tiny/ chance that there is someone who
    might want to use it on such devices.

    I also disagree completely that C90 is somehow a better fit for 8-bit
    devices than C99. It is not. C has /always/ expected at least 16-bit -
    it's not something new in C99. Apart from VLA's, there is nothing in
    C99 that would result in code generation that is a poorer fit for these devices than C90. There are some sort-of-C compilers for 8-bit microcontrollers that have 8-bit ints or do not follow the C rules for
    integer promotions - these are, of course, no more standard C90
    compilers than they are standard C99 compilers.


    But this is all based on the OP's specific use case for their
    application. I just wanted to chime in since I do primarily work on
    modern embedded systems that don't use "modern" microcontrollers and
    CPU's since they are still used in a wide range of modern devices that people don't even realize.



    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Thu Jan 9 15:11:28 2025
    From Newsgroup: comp.lang.c

    On 09/01/2025 09:07, Julio Di Egidio wrote:
    On 08/01/2025 17:18, David Brown wrote:
    On 08/01/2025 15:42, Julio Di Egidio wrote:
    <snip>
      So you can be confident that almost anyone using your software in
    embedded systems will be using a 32-bit core - most likely an ARM
    Cortex-M, but possibly RISC-V.  And they will probably be using a
    toolchain that supports at least C17 (some people are still on older
    toolchains), whether it is gcc, clang, or commercial.   Certainly
    solid C99 support is guaranteed.  Everything else is niche, and no one
    will be using your software on niche systems.

    Even my fridge should be able to run it...  I am writing a Prolog
    compiler, but more generally I'd be mostly writing algorithms-data structures things.

    That said, one thing nobody has been explaining is why C99 is superior
    to C89/C90, except for some coding conveniences as far as I have read online: and I must say here that I do prefer the good old ways,
    including style-wise, in most cases...


    The C99 features that I consider to make code easier to write, clearer,
    safer, more portable, more efficient, and generally better are:

    long long int (via <stdint.h> types)
    compound literals
    designated initialisers
    // comments
    <stdint.h> types
    mixing declaration and code
    declaring index variables inside a "for" statement
    variadic macros
    inline functions
    boolean type and <stdbool.h>

    (The ordering here is from the changelog in the C standards.)

    There are also a few cleanups, like removal of implicit int and implicit function declaration, but those can be enforced in C90 mode too by
    static analysis tools.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Thu Jan 9 15:18:55 2025
    From Newsgroup: comp.lang.c

    On 08/01/2025 17:48, Andrey Tarasevich wrote:


    It is perfectly safe. One can even argue that standard declaration if
    `free` as `void free(void *)` is defective. It should have been `void free(const void *)` from the very beginning.


    It is common in simple heap implementations for the allocated block to
    contain data about the block, such as allocation sizes and pointers to
    other blocks, in memory just below the address returned by malloc.
    free() then uses its parameter to access that data, and may change it.
    So "void free(const void *);" would be lying to the user.

    Even without that, since you are now giving away the memory for re-use
    by other code, it's reasonable to say that "free" might change the data pointed to. (And a security-paranoid "free" might zero out the memory
    before returning it to the heap for re-use.)

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From bart@bc@freeuk.com to comp.lang.c on Thu Jan 9 15:28:25 2025
    From Newsgroup: comp.lang.c

    On 09/01/2025 14:11, David Brown wrote:
    On 09/01/2025 09:07, Julio Di Egidio wrote:
    On 08/01/2025 17:18, David Brown wrote:
    On 08/01/2025 15:42, Julio Di Egidio wrote:
    <snip>
      So you can be confident that almost anyone using your software in
    embedded systems will be using a 32-bit core - most likely an ARM
    Cortex-M, but possibly RISC-V.  And they will probably be using a
    toolchain that supports at least C17 (some people are still on older
    toolchains), whether it is gcc, clang, or commercial.   Certainly
    solid C99 support is guaranteed.  Everything else is niche, and no
    one will be using your software on niche systems.

    Even my fridge should be able to run it...  I am writing a Prolog
    compiler, but more generally I'd be mostly writing algorithms-data
    structures things.

    That said, one thing nobody has been explaining is why C99 is superior
    to C89/C90, except for some coding conveniences as far as I have read
    online: and I must say here that I do prefer the good old ways,
    including style-wise, in most cases...


    The C99 features that I consider to make code easier to write, clearer, safer, more portable, more efficient, and generally better are:

        compound literals
        designated initialisers
        mixing declaration and code
        variadic macros

    Funny, I usually find code using such features less clear!

    They would also make programs a little less portable, since they now
    rely on an implementation that includes support.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Kaz Kylheku@643-408-1753@kylheku.com to comp.lang.c on Thu Jan 9 19:47:49 2025
    From Newsgroup: comp.lang.c

    On 2025-01-09, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    Julio Di Egidio <julio@diegidio.name> writes:
    On 08/01/2025 17:48, Andrey Tarasevich wrote:
    [...]
    It is perfectly safe. One can even argue that standard declaration
    if `free` as `void free(void *)` is defective. It should have been
    `void free(const void *)` from the very beginning.

    I do not understand that: `free` is changing the pointed data, so how
    can `const void *` even be "correct"?

    No, `free` doesn't (necessarily) change the pointed-to data.
    Any attempt to access the allocated data after free() has undefined
    behavior, so it might be modified, but all free() needs to do is
    make it available for further allocation. It might do so without
    touching the data itself.

    It doesn't matter because if free were to change the pointed-to
    data, that would be only wrong if the effective type were const.

    The only way an allocated block acquires an effective type is
    when its value is stored; it then inherits the type of lvalue
    expression.

    An expression of const type isn't a modifiable lvalue that could
    be used to store to the object. (If an implementation allows the
    modification, in spite of emitting the required diagnostic at
    translation time, the behavior is then no longer defined.)

    Therefore, an effective type for an allocated block cannot ever be const-qualified (in a program that has not already run into undefined
    behavior so far prior to the call to impending call to free).
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Thu Jan 9 21:39:30 2025
    From Newsgroup: comp.lang.c

    On 09/01/2025 16:28, bart wrote:
    On 09/01/2025 14:11, David Brown wrote:
    On 09/01/2025 09:07, Julio Di Egidio wrote:
    On 08/01/2025 17:18, David Brown wrote:
    On 08/01/2025 15:42, Julio Di Egidio wrote:
    <snip>
      So you can be confident that almost anyone using your software in
    embedded systems will be using a 32-bit core - most likely an ARM
    Cortex-M, but possibly RISC-V.  And they will probably be using a
    toolchain that supports at least C17 (some people are still on older
    toolchains), whether it is gcc, clang, or commercial.   Certainly
    solid C99 support is guaranteed.  Everything else is niche, and no
    one will be using your software on niche systems.

    Even my fridge should be able to run it...  I am writing a Prolog
    compiler, but more generally I'd be mostly writing algorithms-data
    structures things.

    That said, one thing nobody has been explaining is why C99 is
    superior to C89/C90, except for some coding conveniences as far as I
    have read online: and I must say here that I do prefer the good old
    ways, including style-wise, in most cases...


    The C99 features that I consider to make code easier to write,
    clearer, safer, more portable, more efficient, and generally better are:

         compound literals
         designated initialisers
         mixing declaration and code
         variadic macros

    Funny, I usually find code using such features less clear!

    Opinions on clarity vary - I was giving /my/ opinion.


    They would also make programs a little less portable, since they now
    rely on an implementation that includes support.

    I explained elsewhere how C99 is at least as portable as C90 in real
    life - no compiler of relevance can be used with standard C90 and not
    with standard C99 (perhaps excluding a few features not on my list -
    VLAs, complex numbers and wide character support). And even if you use nothing else from C99, use of <stdint.h> types improves portability significantly.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Thu Jan 9 13:37:09 2025
    From Newsgroup: comp.lang.c

    On 1/9/2025 1:35 AM, David Brown wrote:
    On 09/01/2025 05:24, Kaz Kylheku wrote:
    On 2025-01-09, Ben Bacarisse <ben@bsb.me.uk> wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    static AvlTree_t const *AvlTree_node(
         void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
         AvlTree_t *pT;

         pT = malloc(sizeof(AvlTree_t));

         if (!pT) {
             return NULL;
         }

         pT->pk = pk;
         pT->pL = pL;
         pT->pR = pR;

         return pT;
    }

    Just on a side issue, I prefer to make tests like this positive so I'd
    write:

      static AvlTree_t const *AvlTree_node(
          void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
      ) {
          AvlTree_t *pT = malloc(*pT);
          if (pT) {
              pT->pk = pk;
              pT->pL = pL;
              pT->pR = pR;
          }
          return pT;
      }

    More generally:

        foo_handle *foo = foo_create();
        bar_handle *bar = foo ? bar_create(foo) : 0; // doesn't like null
        xyzzy_handle *xyz = xyzzy_create(42, bar, arg);
        container *con = malloc(sizeof *con);

        if (foo && bar && xyz && con) {
          // happy case: we have all three resources

          con->foo = foo;
          con->bar = bar;
          con->xyz = xyz;

          return con;
        }

        xyzzy_destroy(xyz);
        xyzzy_destroy(bar);
        if (foo)
           xyzzy_destroy(foo); // stupidly doesn't like null

        return 0;

    I'm not going to "make a case" for this (though I will if you want!) --
    I just think it helps to see lots of different styles.

    I might just have made the case. When more resources need to be
    acquired that might fail, it consolidates the happy case under one
    conjunctive test, and consolidates the cleanup in the unhappy case.
    Effectively it's almost if we have only two cases.

    A minor disadvantage is that in the unhappy flow, we may allocate
    resources past the point where it is obvious they are not going to be
    needed: if foo_create() failed, we are pointlessly calling
    xyzzy_create() and malloc for the container. It's possible that these
    succeed, and we are just going to turn around and free them.


    How about taking the idea slightly further and making the later
    allocations conditional too?

       foo_handle *foo = foo_create();
       bar_handle *bar = foo ? bar_create(foo) : 0; // doesn't like null
       xyzzy_handle *xyz = bar ? xyzzy_create(42, bar, arg) : 0;
       container *con = xyz ? malloc(sizeof *con) : 0;

       if (con) {
         // happy case: we have all three resources

       ...

    If you are going to use that style (and I not arguing for or against
    it), go all in!

    Indeed! :^D




    It's a form of consolidated error checking, like when we make
    several system calls and check them for errors as a batch;
    e.g. call fprintf several times and check for disk full (etc)
    just once.





    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Ben Bacarisse@ben@bsb.me.uk to comp.lang.c on Thu Jan 9 23:23:33 2025
    From Newsgroup: comp.lang.c

    Julio Di Egidio <julio@diegidio.name> writes:

    On 09/01/2025 02:09, Ben Bacarisse wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT;

    pT = malloc(sizeof(AvlTree_t));

    if (!pT) {
    return NULL;
    }

    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;

    return pT;
    }
    Just on a side issue, I prefer to make tests like this positive so I'd
    write:
    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT = malloc(*pT);
    if (pT) {
    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;
    }
    return pT;
    }
    I'm not going to "make a case" for this (though I will if you want!) --
    I just think it helps to see lots of different styles.

    That is *more* error prone,

    I would be happy for you to expand on why you say that.

    all the more so if it's not a 5 liner...
    --
    Ben.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Fri Jan 10 00:37:56 2025
    From Newsgroup: comp.lang.c

    On 10/01/2025 00:23, Ben Bacarisse wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    On 09/01/2025 02:09, Ben Bacarisse wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT;

    pT = malloc(sizeof(AvlTree_t));

    if (!pT) {
    return NULL;
    }

    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;

    return pT;
    }
    Just on a side issue, I prefer to make tests like this positive so I'd
    write:
    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT = malloc(*pT);
    if (pT) {
    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;
    }
    return pT;
    }
    I'm not going to "make a case" for this (though I will if you want!) --
    I just think it helps to see lots of different styles.

    That is *more* error prone,

    I would be happy for you to expand on why you say that.

    all the more so if it's not a 5 liner...

    There is no such thing as expanding 40 years of professional experience
    in software engineering and programming and doing it properly since day
    one: just think about that code and what I said for what it's worth, in particular I haven't mentioned 5 liners by chance, things are quite more complicated not in vitro.

    And please do not hold a grudge about that: it's not me who was trying
    to say how to write code... ;)

    HTH,

    -Julio

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Fri Jan 10 00:45:44 2025
    From Newsgroup: comp.lang.c

    On 10/01/2025 00:37, Julio Di Egidio wrote:
    On 10/01/2025 00:23, Ben Bacarisse wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    On 09/01/2025 02:09, Ben Bacarisse wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    static AvlTree_t const *AvlTree_node(
          void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
          AvlTree_t *pT;

          pT = malloc(sizeof(AvlTree_t));

          if (!pT) {
              return NULL;
          }

          pT->pk = pk;
          pT->pL = pL;
          pT->pR = pR;

          return pT;
    }
    Just on a side issue, I prefer to make tests like this positive so I'd >>>> write:
       static AvlTree_t const *AvlTree_node(
           void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
       ) {
           AvlTree_t *pT = malloc(*pT);
             if (pT) {
               pT->pk = pk;
               pT->pL = pL;
               pT->pR = pR;
           }
           return pT;
       }
    I'm not going to "make a case" for this (though I will if you want!) -- >>>> I just think it helps to see lots of different styles.

    That is *more* error prone,

    I would be happy for you to expand on why you say that.

    all the more so if it's not a 5 liner...

    There is no such thing as expanding 40 years of professional experience
    in software engineering and programming and doing it properly since day
    one: just think about that code and what I said for what it's worth, in particular I haven't mentioned 5 liners by chance, things are quite more complicated not in vitro.

    And please do not hold a grudge about that: it's not me who was trying
    to say how to write code... ;)

    HTH,

    BTW, I hadn't mention it, but have you noticed the second one is
    misindented? Between me and you, I can tell how long a piece of code
    will take to break when in production by just looking at it... A lot of
    fun. :)

    -Julio

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Ben Bacarisse@ben@bsb.me.uk to comp.lang.c on Fri Jan 10 01:04:01 2025
    From Newsgroup: comp.lang.c

    Julio Di Egidio <julio@diegidio.name> writes:

    On 10/01/2025 00:23, Ben Bacarisse wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    On 09/01/2025 02:09, Ben Bacarisse wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT;

    pT = malloc(sizeof(AvlTree_t));

    if (!pT) {
    return NULL;
    }

    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;

    return pT;
    }
    Just on a side issue, I prefer to make tests like this positive so I'd >>>> write:
    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT = malloc(*pT);
    if (pT) {
    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;
    }
    return pT;
    }
    I'm not going to "make a case" for this (though I will if you want!) -- >>>> I just think it helps to see lots of different styles.

    That is *more* error prone,
    I would be happy for you to expand on why you say that.

    all the more so if it's not a 5 liner...

    There is no such thing as expanding 40 years of professional experience in software engineering and programming and doing it properly since day one: just think about that code and what I said for what it's worth, in
    particular I haven't mentioned 5 liners by chance, things are quite more complicated not in vitro.

    And please do not hold a grudge about that: it's not me who was trying to
    say how to write code... ;)

    I was not trying to tell anyone how to write code. You made a claim --
    not even a qualified one, a categorical one -- the justification for
    which I would have liked to know more about.

    HTH,

    Not really, but no one is under any obligation here. It's just a
    discussion.
    --
    Ben.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Thu Jan 9 17:43:50 2025
    From Newsgroup: comp.lang.c

    Julio Di Egidio <julio@diegidio.name> writes:

    On 10/01/2025 00:37, Julio Di Egidio wrote:

    On 10/01/2025 00:23, Ben Bacarisse wrote:

    Julio Di Egidio <julio@diegidio.name> writes:

    On 09/01/2025 02:09, Ben Bacarisse wrote:

    Julio Di Egidio <julio@diegidio.name> writes:

    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT;

    pT = malloc(sizeof(AvlTree_t));

    if (!pT) {
    return NULL;
    }

    pk = pk;
    pL = pL;
    pR = pR;

    return pT;
    }

    Just on a side issue, I prefer to make tests like this positive
    so I'd write:
    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT = malloc(*pT);
    if (pT) {
    pk = pk;
    pL = pL;
    pR = pR;
    }
    return pT;
    }
    I'm not going to "make a case" for this (though I will if you
    want!) -- I just think it helps to see lots of different styles.

    That is *more* error prone,

    I would be happy for you to expand on why you say that.

    all the more so if it's not a 5 liner...

    There is no such thing as expanding 40 years of professional
    experience in software engineering and programming and doing it
    properly since day one: just think about that code and what I said
    for what it's worth, in particular I haven't mentioned 5 liners by
    chance, things are quite more complicated not in vitro.

    And please do not hold a grudge about that: it's not me who was
    trying to say how to write code... ;)

    BTW, I hadn't mention it, but have you noticed the second one is
    misindented? Between me and you, I can tell how long a piece of
    code will take to break when in production by just looking at
    it... A lot of fun. :)

    The indentation was correct in Ben's original posting.

    The misindentation first appeared in your followup to that
    posting, where the quoted portion had been changed to remove a
    blank line and over-indent the if().
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Thu Jan 9 17:48:08 2025
    From Newsgroup: comp.lang.c

    Kaz Kylheku <643-408-1753@kylheku.com> writes:

    On 2025-01-09, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:

    Julio Di Egidio <julio@diegidio.name> writes:

    On 08/01/2025 17:48, Andrey Tarasevich wrote:

    [...]

    It is perfectly safe. One can even argue that standard declaration
    if `free` as `void free(void *)` is defective. It should have been
    `void free(const void *)` from the very beginning.

    I do not understand that: `free` is changing the pointed data, so
    how can `const void *` even be "correct"?

    No, `free` doesn't (necessarily) change the pointed-to data.
    Any attempt to access the allocated data after free() has undefined
    behavior, so it might be modified, but all free() needs to do is
    make it available for further allocation. It might do so without
    touching the data itself.

    It doesn't matter because if free were to change the pointed-to
    data, that would be only wrong if the effective type were const.
    [...]

    Effective type is irrelevant. This particular undefined behavior
    occurs only when an object _defined_ with a const-qualified type
    is changed. There are no objects defined in a block of memory
    allocated by malloc().
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Fri Jan 10 03:14:13 2025
    From Newsgroup: comp.lang.c

    On 10/01/2025 02:43, Tim Rentsch wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    On 10/01/2025 00:37, Julio Di Egidio wrote:

    On 10/01/2025 00:23, Ben Bacarisse wrote:

    Julio Di Egidio <julio@diegidio.name> writes:

    On 09/01/2025 02:09, Ben Bacarisse wrote:

    Julio Di Egidio <julio@diegidio.name> writes:

    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT;

    pT = malloc(sizeof(AvlTree_t));

    if (!pT) {
    return NULL;
    }

    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;

    return pT;
    }

    Just on a side issue, I prefer to make tests like this positive
    so I'd write:
    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT = malloc(*pT);
    if (pT) {
    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;
    }
    return pT;
    }
    I'm not going to "make a case" for this (though I will if you
    want!) -- I just think it helps to see lots of different styles.

    That is *more* error prone,

    I would be happy for you to expand on why you say that.

    all the more so if it's not a 5 liner...

    There is no such thing as expanding 40 years of professional
    experience in software engineering and programming and doing it
    properly since day one: just think about that code and what I said
    for what it's worth, in particular I haven't mentioned 5 liners by
    chance, things are quite more complicated not in vitro.

    And please do not hold a grudge about that: it's not me who was
    trying to say how to write code... ;)

    BTW, I hadn't mention it, but have you noticed the second one is
    misindented? Between me and you, I can tell how long a piece of
    code will take to break when in production by just looking at
    it... A lot of fun. :)

    The indentation was correct in Ben's original posting.

    The misindentation first appeared in your followup to that
    posting, where the quoted portion had been changed to remove a
    blank line and over-indent the if().

    But indeed the point is what happens in the long run: if you look above
    mine is still better indented... :) But of course it is not
    indentation per se the problem: for example check the return value as
    soon as the function returns a possibly null pointer or an error value
    is certainly more widely applicable, and quite less error prone,
    especially if it's not a 5 liner... Anyway, I also truly believe there
    is no point in belabouring the point: it's the overall picture that one
    must get to see.

    -Julio

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Fri Jan 10 03:51:16 2025
    From Newsgroup: comp.lang.c

    On 10/01/2025 03:14, Julio Di Egidio wrote:
    On 10/01/2025 02:43, Tim Rentsch wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    On 10/01/2025 00:37, Julio Di Egidio wrote:

    On 10/01/2025 00:23, Ben Bacarisse wrote:

    Julio Di Egidio <julio@diegidio.name> writes:

    On 09/01/2025 02:09, Ben Bacarisse wrote:

    Julio Di Egidio <julio@diegidio.name> writes:

    static AvlTree_t const *AvlTree_node(
      void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
      AvlTree_t *pT;

      pT = malloc(sizeof(AvlTree_t));

      if (!pT) {
      return NULL;
      }

      pT->pk = pk;
      pT->pL = pL;
      pT->pR = pR;

      return pT;
    }

    Just on a side issue, I prefer to make tests like this positive
    so I'd write:
      static AvlTree_t const *AvlTree_node(
      void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
      ) {
      AvlTree_t *pT = malloc(*pT);
      if (pT) {
      pT->pk = pk;
      pT->pL = pL;
      pT->pR = pR;
      }
      return pT;
      }
    I'm not going to "make a case" for this (though I will if you
    want!) -- I just think it helps to see lots of different styles.

    That is *more* error prone,

    I would be happy for you to expand on why you say that.

    all the more so if it's not a 5 liner...

    There is no such thing as expanding 40 years of professional
    experience in software engineering and programming and doing it
    properly since day one:  just think about that code and what I said
    for what it's worth, in particular I haven't mentioned 5 liners by
    chance, things are quite more complicated not in vitro.

    And please do not hold a grudge about that:  it's not me who was
    trying to say how to write code... ;)

    BTW, I hadn't mention it, but have you noticed the second one is
    misindented?  Between me and you, I can tell how long a piece of
    code will take to break when in production by just looking at
    it...  A lot of fun.  :)

    The indentation was correct in Ben's original posting.

    The misindentation first appeared in your followup to that
    posting, where the quoted portion had been changed to remove a
    blank line and over-indent the if().

    But indeed the point is what happens in the long run: if you look above
    mine is still better indented... :)  But of course it is not indentation per se the problem: for example check the return value as soon as the function returns a possibly null pointer or an error value is certainly
    more widely applicable, and quite less error prone, especially if it's

    I meant: immediately check the return value and bail out if needed. The
    other approach does not even simplify on the clean-up, by the way...

    not a 5 liner...  Anyway, I also truly believe there is no point in belabouring the point: it's the overall picture that one must get to see.

    -Julio

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Fri Jan 10 04:05:05 2025
    From Newsgroup: comp.lang.c

    On 10/01/2025 03:51, Julio Di Egidio wrote:
    On 10/01/2025 03:14, Julio Di Egidio wrote:
    On 10/01/2025 02:43, Tim Rentsch wrote:
    Julio Di Egidio <julio@diegidio.name> writes:
    On 10/01/2025 00:37, Julio Di Egidio wrote:
    On 10/01/2025 00:23, Ben Bacarisse wrote:
    Julio Di Egidio <julio@diegidio.name> writes:
    On 09/01/2025 02:09, Ben Bacarisse wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    static AvlTree_t const *AvlTree_node(
      void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
      AvlTree_t *pT;

      pT = malloc(sizeof(AvlTree_t));

      if (!pT) {
      return NULL;
      }

      pT->pk = pk;
      pT->pL = pL;
      pT->pR = pR;

      return pT;
    }

    Just on a side issue, I prefer to make tests like this positive >>>>>>>> so I'd write:
      static AvlTree_t const *AvlTree_node(
      void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
      ) {
      AvlTree_t *pT = malloc(*pT);
      if (pT) {
      pT->pk = pk;
      pT->pL = pL;
      pT->pR = pR;
      }
      return pT;
      }
    I'm not going to "make a case" for this (though I will if you
    want!) -- I just think it helps to see lots of different styles. >>>>>>>
    That is *more* error prone,

    I would be happy for you to expand on why you say that.

    all the more so if it's not a 5 liner...

    There is no such thing as expanding 40 years of professional
    experience in software engineering and programming and doing it
    properly since day one:  just think about that code and what I said >>>>> for what it's worth, in particular I haven't mentioned 5 liners by
    chance, things are quite more complicated not in vitro.

    And please do not hold a grudge about that:  it's not me who was
    trying to say how to write code... ;)

    BTW, I hadn't mention it, but have you noticed the second one is
    misindented?  Between me and you, I can tell how long a piece of
    code will take to break when in production by just looking at
    it...  A lot of fun.  :)

    The indentation was correct in Ben's original posting.

    The misindentation first appeared in your followup to that
    posting, where the quoted portion had been changed to remove a
    blank line and over-indent the if().

    But indeed the point is what happens in the long run: if you look
    above mine is still better indented... :)  But of course it is not
    indentation per se the problem: for example check the return value as
    soon as the function returns a possibly null pointer or an error value
    is certainly more widely applicable, and quite less error prone,
    especially if it's

    I meant: immediately check the return value and bail out if needed.  The other approach does not even simplify on the clean-up, by the way...

    not a 5 liner...  Anyway, I also truly believe there is no point in
    belabouring the point: it's the overall picture that one must get to see.

    A last one for the more theoretically inclined: every "if" is a code
    path, but not all code paths are "if"s...

    Cheers,

    -Julio

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Andrey Tarasevich@andreytarasevich@hotmail.com to comp.lang.c on Thu Jan 9 22:01:53 2025
    From Newsgroup: comp.lang.c

    On 01/09/25 12:12 AM, Julio Di Egidio wrote:
    I do not understand that: `free` is changing the pointed data, so how
    can `const void *` even be "correct"?

    `free` is destroying the pointed data.

    Every object in C object model has to be created (when its lifetime
    begins) and has to be eventually destroyed (when its lifetime ends).
    This applies to all objects, including `const` ones (!). Lifetime of a
    `const` objects also ends eventually, which means that `const` object
    has to be destroyable. No way around it.

    So, destruction is not really a "modifying" operation. Destruction of an object is a special case, it's a meta-operaton, which needs special
    treatment. We have to be able to destroy `const` objects as well,
    regardless of how they were created/allocated. And in C destruction of dynamically created objects is essentially embodied in `free`. So,
    `free` is a meta-operation, which has to be able to destroy `const` objects.

    This, for example, is mirrored in C++ as well, where `delete` is
    immediately applicable to pointers to `const` objects. No reason to make `free` behave differently in C.
    --
    Best regards,
    Andrey
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Andrey Tarasevich@andreytarasevich@hotmail.com to comp.lang.c on Thu Jan 9 22:04:39 2025
    From Newsgroup: comp.lang.c

    On 01/09/25 6:18 AM, David Brown wrote:

    It is common in simple heap implementations for the allocated block to contain data about the block, such as allocation sizes and pointers to
    other blocks, in memory just below the address returned by malloc.
    free() then uses its parameter to access that data, and may change it.
    So "void free(const void *);" would be lying to the user.

    No, it wouldn't be. A deallocated data no longer exists from the user's
    point of view. There's nothing to lie about.

    Even without that, since you are now giving away the memory for re-use
    by other code, it's reasonable to say that "free" might change the data pointed to.  (And a security-paranoid "free" might zero out the memory before returning it to the heap for re-use.)

    Such internal implementation details are completely irrelevant to the
    matter at hand, which is purely conceptual in essence. The rest I've
    explained above.
    --
    Best regards,
    Andrey

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Thu Jan 9 23:40:52 2025
    From Newsgroup: comp.lang.c

    Andrey Tarasevich <andreytarasevich@hotmail.com> writes:
    On 01/09/25 12:12 AM, Julio Di Egidio wrote:
    I do not understand that: `free` is changing the pointed data, so
    how can `const void *` even be "correct"?

    `free` is destroying the pointed data.

    Right. In other words, it causes the pointed-to data to reach the end
    of its lifetime. "Changing" the data generally means modifying its
    value (that's what "const" forbids).

    Given:

    int *ptr = malloc(sizeof *ptr);
    *ptr = 42;
    printf("*ptr = %d\n", *ptr);
    free(ptr);

    After the call to free(), the int object logically no longer exists.
    Also, the value of the pointer object ptr becomes indeterminate.
    Attempting to refer to the value of either ptr or *ptr has undefined
    behavior.

    Having said that, it's likely that such an attempt will not be
    diagnosed, and that the values of ptr and *ptr will be *appear* to be
    the same before and after calling free(). (Though the memory management
    system might update *ptr, depending on the implementation.) But this is outside the scope of what C defines, and there are no guarantees of
    *anything*.

    Every object in C object model has to be created (when its lifetime
    begins) and has to be eventually destroyed (when its lifetime
    ends). This applies to all objects, including `const` ones
    (!). Lifetime of a `const` objects also ends eventually, which means
    that `const` object has to be destroyable. No way around it.

    An object with static storage duration (either defined with the "static" keyword or defined at file scope) has a lifetime that ends when the
    program terminates. In a typical implementation, the destruction of
    such an object doesn't do anything other than deallocating its memory.

    [...]
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Fri Jan 10 12:23:53 2025
    From Newsgroup: comp.lang.c

    On Thu, 09 Jan 2025 23:40:52 -0800
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:

    Andrey Tarasevich <andreytarasevich@hotmail.com> writes:
    On 01/09/25 12:12 AM, Julio Di Egidio wrote:
    I do not understand that: `free` is changing the pointed data, so
    how can `const void *` even be "correct"?

    `free` is destroying the pointed data.

    Right. In other words, it causes the pointed-to data to reach the end
    of its lifetime. "Changing" the data generally means modifying its
    value (that's what "const" forbids).

    Given:

    int *ptr = malloc(sizeof *ptr);
    *ptr = 42;
    printf("*ptr = %d\n", *ptr);
    free(ptr);

    After the call to free(), the int object logically no longer exists.
    Also, the value of the pointer object ptr becomes indeterminate.
    Attempting to refer to the value of either ptr or *ptr has undefined behavior.


    I believe that the Standard really says that, but find the part about
    value of ptr variable ridiculous. It breaks natural hierarchy by which
    standard library is somewhat special, but it is not above rules of core language. free() is defined as function rather than macro. By rules of
    core language, a function call can not modify the value of local
    variable at caller's scope, unless pointers to the variable was passed
    to it explicitly.


    Having said that, it's likely that such an attempt will not be
    diagnosed, and that the values of ptr and *ptr will be *appear* to be
    the same before and after calling free(). (Though the memory
    management system might update *ptr, depending on the
    implementation.) But this is outside the scope of what C defines,
    and there are no guarantees of *anything*.

    Every object in C object model has to be created (when its lifetime
    begins) and has to be eventually destroyed (when its lifetime
    ends). This applies to all objects, including `const` ones
    (!). Lifetime of a `const` objects also ends eventually, which means
    that `const` object has to be destroyable. No way around it.

    An object with static storage duration (either defined with the
    "static" keyword or defined at file scope) has a lifetime that ends
    when the program terminates. In a typical implementation, the
    destruction of such an object doesn't do anything other than
    deallocating its memory.

    [...]



    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Fri Jan 10 11:31:07 2025
    From Newsgroup: comp.lang.c

    On 10/01/2025 07:04, Andrey Tarasevich wrote:
    On 01/09/25 6:18 AM, David Brown wrote:

    It is common in simple heap implementations for the allocated block to
    contain data about the block, such as allocation sizes and pointers to
    other blocks, in memory just below the address returned by malloc.
    free() then uses its parameter to access that data, and may change it.
    So "void free(const void *);" would be lying to the user.

    No, it wouldn't be. A deallocated data no longer exists from the user's point of view. There's nothing to lie about.

    Even without that, since you are now giving away the memory for re-use
    by other code, it's reasonable to say that "free" might change the
    data pointed to.  (And a security-paranoid "free" might zero out the
    memory before returning it to the heap for re-use.)

    Such internal implementation details are completely irrelevant to the
    matter at hand, which is purely conceptual in essence. The rest I've explained above.


    I appreciate your point of view. And certainly there is no way (without
    UB, or at least implementation-specific details) for the code that calls "free" to see that "free" has changed anything via the pointer parameter
    - thus to the calling code, it makes no difference if it is "void *" or
    "const void *".

    So perhaps it is wrong to say it would be /lying/ to the user. But it
    is still, I think, misleading. A pointer-to-const parameter in C is
    used primarily to say that the function will only read the pointed-to data.

    Another factor here is symmetry - malloc returns a void * pointer, and
    it would seem very strange that you should return that pointer to the
    heap as a const void * pointer.

    If you want a better signature for "free", then I would suggest "void free(void ** p)" - that (to me) more naturally shows that the function
    is freeing the pointer, while also greatly reducing the "use after free" errors in C code by turning them into "dereferencing a null pointer"
    errors which are more easily caught by many OS's.


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Fri Jan 10 08:50:07 2025
    From Newsgroup: comp.lang.c

    On 1/10/25 05:23, Michael S wrote:
    On Thu, 09 Jan 2025 23:40:52 -0800
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    ...
    After the call to free(), the int object logically no longer exists.
    Also, the value of the pointer object ptr becomes indeterminate.
    Attempting to refer to the value of either ptr or *ptr has undefined
    behavior.


    I believe that the Standard really says that, but find the part about
    value of ptr variable ridiculous. It breaks natural hierarchy by which standard library is somewhat special, but it is not above rules of core language. free() is defined as function rather than macro. By rules of
    core language, a function call can not modify the value of local
    variable at caller's scope, unless pointers to the variable was passed
    to it explicitly.

    And that applies to free() just as much as any user-defined function.

    Keep in mind that if p is a value returned by a call to a memory
    management function (alloc_aligned(), malloc(), calloc() or realloc()),
    the values of all pointers anywhere in the program that point at any
    location in the block of memory allocated by the call to that memory
    management function become indeterminate at the same time. This doesn't
    mean that free() has permission to change the bit pattern stored in any
    of those pointer objects. It doesn't. There's no legal way to access the
    values stored in those objects; the only thing you can do with those
    objects as a whole is to store a new value in them. It is, however,
    legal to access the individual bytes that make up the pointer's
    representation. Those bytes are themselves objects, and changing the
    values stored in those bytes would therefore violate 6.2.4p2: "An object exists, has a constant address36) , and retains its last-stored value throughout its lifetime."

    What the indeterminate value of those pointers does mean is that implementations have permission to remove that entire block of memory
    from the list of valid pointer locations. On a system which uses memory
    maps, that can be as simple as modifying one entry in the memory map table.

    By the way, this argument was controversial when I made it in a
    discussion on this newsgroup in 2003 in a thread titled "pointer after
    free indeterminate (your example James)". Yes, I am the "James" in that
    title. You can look up that thread using Google Groups, if you want to
    examine the arguments for and against. I don't believe that there's been
    any relevant changes in the standard, but it's been two decades and
    several revisions of the standard, so I could be wrong about that.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Fri Jan 10 15:30:39 2025
    From Newsgroup: comp.lang.c

    On 09/01/2025 21:39, David Brown wrote:
    On 09/01/2025 16:28, bart wrote:
    On 09/01/2025 14:11, David Brown wrote:
    On 09/01/2025 09:07, Julio Di Egidio wrote:
    On 08/01/2025 17:18, David Brown wrote:
    On 08/01/2025 15:42, Julio Di Egidio wrote:
    <snip>
      So you can be confident that almost anyone using your software in >>>>> embedded systems will be using a 32-bit core - most likely an ARM
    Cortex-M, but possibly RISC-V.  And they will probably be using a
    toolchain that supports at least C17 (some people are still on
    older toolchains), whether it is gcc, clang, or commercial.
      Certainly solid C99 support is guaranteed.  Everything else is >>>>> niche, and no one will be using your software on niche systems.

    Even my fridge should be able to run it...  I am writing a Prolog
    compiler, but more generally I'd be mostly writing algorithms-data
    structures things.

    That said, one thing nobody has been explaining is why C99 is
    superior to C89/C90, except for some coding conveniences as far as I
    have read online: and I must say here that I do prefer the good old
    ways, including style-wise, in most cases...


    The C99 features that I consider to make code easier to write,
    clearer, safer, more portable, more efficient, and generally better are: >>>
         compound literals
         designated initialisers
         mixing declaration and code
         variadic macros

    Funny, I usually find code using such features less clear!

    Opinions on clarity vary - I was giving /my/ opinion.


    They would also make programs a little less portable, since they now
    rely on an implementation that includes support.

    I explained elsewhere how C99 is at least as portable as C90 in real
    life - no compiler of relevance can be used with standard C90 and not
    with standard C99 (perhaps excluding a few features not on my list -
    VLAs, complex numbers and wide character support).  And even if you use nothing else from C99, use of <stdint.h> types improves portability significantly.

    OK, I start getting the picture: indeed, let it be C99... Thanks very
    much for that, to you in particular but also to those who have chimed
    in, greatly appreciated.

    -Julio

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Fri Jan 10 10:37:46 2025
    From Newsgroup: comp.lang.c

    Michael S <already5chosen@yahoo.com> writes:

    On Thu, 09 Jan 2025 23:40:52 -0800
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:

    Andrey Tarasevich <andreytarasevich@hotmail.com> writes:
    On 01/09/25 12:12 AM, Julio Di Egidio wrote:
    I do not understand that: `free` is changing the pointed data, so
    how can `const void *` even be "correct"?

    `free` is destroying the pointed data.

    Right. In other words, it causes the pointed-to data to reach the end
    of its lifetime. "Changing" the data generally means modifying its
    value (that's what "const" forbids).

    Given:

    int *ptr = malloc(sizeof *ptr);
    *ptr = 42;
    printf("*ptr = %d\n", *ptr);
    free(ptr);

    After the call to free(), the int object logically no longer exists.
    Also, the value of the pointer object ptr becomes indeterminate.
    Attempting to refer to the value of either ptr or *ptr has undefined
    behavior.

    I believe that the Standard really says that, but find the part about
    value of ptr variable ridiculous. It breaks natural hierarchy by which standard library is somewhat special, but it is not above rules of core language. free() is defined as function rather than macro. By rules of
    core language, a function call can not modify the value of local
    variable at caller's scope, unless pointers to the variable was passed
    to it explicitly.

    Right, the call to free() doesn't change the value of ptr.

    But that value, which is a valid pointer value before the call, becomes indeterminate after the call.

    You can even do something like this:

    int *ptr = malloc(sizeof *ptr); // assume malloc succeeded
    int *ptr1 = ptr + 1;
    free(ptr - 1);

    In most or all actual implementations, referring to the value of ptr
    after the free() for example with printf("%p", (void*)ptr), won't cause
    any problems (unless the compiler peforms some optimization based on the assumption that its value won't be accessed). But one can imagine a
    system that checks pointer values for validity every time they're
    accessed; on such a system, reading the value of ptr might cause a trap.

    [...]
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Kaz Kylheku@643-408-1753@kylheku.com to comp.lang.c on Fri Jan 10 18:37:49 2025
    From Newsgroup: comp.lang.c

    On 2025-01-10, Andrey Tarasevich <andreytarasevich@hotmail.com> wrote:
    On 01/09/25 12:12 AM, Julio Di Egidio wrote:
    I do not understand that: `free` is changing the pointed data, so how
    can `const void *` even be "correct"?

    `free` is destroying the pointed data.

    Every object in C object model has to be created (when its lifetime
    begins) and has to be eventually destroyed (when its lifetime ends).

    That is not so. Literals can be put into a ROM image.

    Well, sure, that is created in a factory, and destroyed when recycled.

    The point is that the data's lifetime can span over countless
    invocations of the program; the program can never observe a time which
    is outside of the lifetime of those objects.

    So, destruction is not really a "modifying" operation. Destruction of an

    Destruction by malloc is modifying in any system that recycles the
    memory for another allocation.
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Fri Jan 10 10:56:04 2025
    From Newsgroup: comp.lang.c

    David Brown <david.brown@hesbynett.no> writes:
    [...]
    If you want a better signature for "free", then I would suggest "void free(void ** p)" - that (to me) more naturally shows that the function
    is freeing the pointer, while also greatly reducing the "use after
    free" errors in C code by turning them into "dereferencing a null
    pointer" errors which are more easily caught by many OS's.

    I'm not sure that would work. A void** argument means you need to pass
    a pointer to a void* object. If you've assigned the converted result of malloc() to, say, an int* object, you don't have a void* object. (int*
    and void* might not even have the same representation).

    Some kind of generic function that takes a pointer to an object of any
    object pointer type could work, but the language doesn't support that.
    (C++ addressed this by making `new` and `delete` built-in operators
    rather than library functions.)
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Fri Jan 10 18:58:20 2025
    From Newsgroup: comp.lang.c

    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    Michael S <already5chosen@yahoo.com> writes:

    On Thu, 09 Jan 2025 23:40:52 -0800
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:


    I believe that the Standard really says that, but find the part about
    value of ptr variable ridiculous. It breaks natural hierarchy by which
    standard library is somewhat special, but it is not above rules of core
    language. free() is defined as function rather than macro. By rules of
    core language, a function call can not modify the value of local
    variable at caller's scope, unless pointers to the variable was passed
    to it explicitly.

    Right, the call to free() doesn't change the value of ptr.

    But that value, which is a valid pointer value before the call, becomes >indeterminate after the call.

    You can even do something like this:

    int *ptr = malloc(sizeof *ptr); // assume malloc succeeded
    int *ptr1 = ptr + 1;
    free(ptr - 1);

    Did you mean to write

    free(ptr1 - 1);

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Fri Jan 10 11:08:03 2025
    From Newsgroup: comp.lang.c

    Kaz Kylheku <643-408-1753@kylheku.com> writes:
    On 2025-01-10, Andrey Tarasevich <andreytarasevich@hotmail.com> wrote:
    On 01/09/25 12:12 AM, Julio Di Egidio wrote:
    I do not understand that: `free` is changing the pointed data, so how
    can `const void *` even be "correct"?

    `free` is destroying the pointed data.

    Every object in C object model has to be created (when its lifetime
    begins) and has to be eventually destroyed (when its lifetime ends).

    That is not so. Literals can be put into a ROM image.

    Well, sure, that is created in a factory, and destroyed when recycled.

    The point is that the data's lifetime can span over countless
    invocations of the program; the program can never observe a time which
    is outside of the lifetime of those objects.

    Any object with static storage duration has a lifetime that extends over
    the entire execution of the program, so the program can never see such
    an object outside its lifetime regardless of how it's stored.

    If an object happens to be stored in a ROM image that the program
    accesses directly, then sure, those bits can survive across multiple executions. But the C "lifetime" extends only across a single
    execution.

    Nothing in particular *has* to happen when an object reaches the end of
    its lifetime.

    So, destruction is not really a "modifying" operation. Destruction of an

    Destruction by malloc is modifying in any system that recycles the
    memory for another allocation.

    A call to malloc or free can modify the bits that make up an object, but
    they do so only before or after the object's C lifetime.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Fri Jan 10 11:08:56 2025
    From Newsgroup: comp.lang.c

    scott@slp53.sl.home (Scott Lurndal) writes:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    Michael S <already5chosen@yahoo.com> writes:
    [...]
    You can even do something like this:

    int *ptr = malloc(sizeof *ptr); // assume malloc succeeded
    int *ptr1 = ptr + 1;
    free(ptr - 1);

    Did you mean to write

    free(ptr1 - 1);

    Yes, thanks.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Kaz Kylheku@643-408-1753@kylheku.com to comp.lang.c on Fri Jan 10 19:11:05 2025
    From Newsgroup: comp.lang.c

    On 2025-01-10, Michael S <already5chosen@yahoo.com> wrote:
    On Thu, 09 Jan 2025 23:40:52 -0800
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:

    Andrey Tarasevich <andreytarasevich@hotmail.com> writes:
    On 01/09/25 12:12 AM, Julio Di Egidio wrote:
    I do not understand that: `free` is changing the pointed data, so
    how can `const void *` even be "correct"?

    `free` is destroying the pointed data.

    Right. In other words, it causes the pointed-to data to reach the end
    of its lifetime. "Changing" the data generally means modifying its
    value (that's what "const" forbids).

    Given:

    int *ptr = malloc(sizeof *ptr);
    *ptr = 42;
    printf("*ptr = %d\n", *ptr);
    free(ptr);

    After the call to free(), the int object logically no longer exists.
    Also, the value of the pointer object ptr becomes indeterminate.
    Attempting to refer to the value of either ptr or *ptr has undefined
    behavior.


    I believe that the Standard really says that, but find the part about
    value of ptr variable ridiculous. It breaks natural hierarchy by which standard library is somewhat special, but it is not above rules of core language.

    The library is above the rules because it is not required to be
    written in strictly conforming ISO C.

    The free function can communicate with the hardware (obviously in
    a completely nonportable way that is outside of the C language) to put a pointer value into a blacklist, such that any operation doing anything
    with that pointer will trap.

    Whether a value is valid or is a non-value* representation doesn't
    necessarily depend in a static way on the value's bit pattern; the
    machine could have a dynamically changing function which determines
    whether a bit pattern is a value or non-value.

    More practically speaking, suppose we have a situation like this:

    free(p);
    helper(p);

    The helper(p) call can be diagnosed at translation time, because C implementations can implement arbitrary diagnostics.

    If p is an indeterminate value, then the call erroneous regardless of
    what the function does with p, so that adds legitimacy to diagnosing it,
    and even terminating translation.

    In other words, a conforming ISO C implementation can reject a program
    which, if it were not for the use of p, would otherwise be strictly
    conforming in every other regard, and which stays within all
    implementation limits.

    ---
    *. Formerly "trap representations" in the C standard are now called
    non-value representations. Use of a non-value representation may
    generate a trap.
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Kaz Kylheku@643-408-1753@kylheku.com to comp.lang.c on Fri Jan 10 19:28:22 2025
    From Newsgroup: comp.lang.c

    On 2025-01-09, David Brown <david.brown@hesbynett.no> wrote:
    On 08/01/2025 17:48, Andrey Tarasevich wrote:


    It is perfectly safe. One can even argue that standard declaration if
    `free` as `void free(void *)` is defective. It should have been `void
    free(const void *)` from the very beginning.

    It is common in simple heap implementations for the allocated block to contain data about the block, such as allocation sizes and pointers to
    other blocks, in memory just below the address returned by malloc.
    free() then uses its parameter to access that data, and may change it.
    So "void free(const void *);" would be lying to the user.

    If the pointer came from the allocator, as required, there is no lie.
    The object is writable, and so free may strip away the qualifier
    and do whatever it wants, like cover the object with 0xFE bytes.

    Quite contrary, if the program calls free(p), yet still somehow cares
    about what happens to the contents referenced by p, then the program is
    lying to the implementation!

    The prototype of free being: void free(const void *) would be helpful.
    It would mean that programs which choose to initialize dynamically
    allocated objects and then pass them around via pointers to const could
    then free the objects without having to use a cast.

    Code like this can be free of casts:

    const obj *obj_create(int param)
    {
    obj *o = malloc(sizeof *po); // null check omitted for topic focus
    o->param = param;
    return o; // after this, objs treated as immutable
    }

    void obj_destroy(const obj *o)
    {
    free(o);
    }

    Free of casts is good!

    Might it already be that the features of the current ISO C standard
    allow for the possibility of free being generic betwen const and
    unqualified? Though I don't suspect you can select on a pointer's
    referenced type's qualification, regardless of type.

    What are the backward compatibilty considerations of just making
    the prototype void free(const void *)?

    One is this problem:

    void (*pfree)(void *) = free;

    but in fact, the type rules should be such that the assignment
    is allowed even if free is void(const void *).

    The type compatibility rule should be that a function pointer type with
    more strictly qualified parameters should be implicitly convertible to a function pointer type with less strictly qualified parameters.
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Fri Jan 10 20:58:55 2025
    From Newsgroup: comp.lang.c

    On 1/10/25 14:11, Kaz Kylheku wrote:
    On 2025-01-10, Michael S <already5chosen@yahoo.com> wrote:
    On Thu, 09 Jan 2025 23:40:52 -0800
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    ...
    int *ptr = malloc(sizeof *ptr);
    *ptr = 42;
    printf("*ptr = %d\n", *ptr);
    free(ptr);

    After the call to free(), the int object logically no longer exists.
    Also, the value of the pointer object ptr becomes indeterminate.
    Attempting to refer to the value of either ptr or *ptr has undefined
    behavior.


    I believe that the Standard really says that, but find the part about
    value of ptr variable ridiculous. It breaks natural hierarchy by which
    standard library is somewhat special, but it is not above rules of core
    language.

    The library is above the rules because it is not required to be
    written in strictly conforming ISO C.

    True, but not as relevant as you think. The key point is that
    interactions between the library and with user-written C code needs to
    behave according to the rules of C, whether or not the function is
    written in C. In particular, since free() is defined as receiving a
    pointer value, rather than a pointer to that pointer, it should have no
    way of changing the original pointer. You've described how it can render
    that pointer's value invalid, without any change to the pointer itself.
    And that is true whether or not malloc() is written in strictly conforming C --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Fri Jan 10 20:57:34 2025
    From Newsgroup: comp.lang.c

    On 1/9/2025 11:40 PM, Keith Thompson wrote:
    Andrey Tarasevich <andreytarasevich@hotmail.com> writes:
    On 01/09/25 12:12 AM, Julio Di Egidio wrote:
    I do not understand that: `free` is changing the pointed data, so
    how can `const void *` even be "correct"?

    `free` is destroying the pointed data.

    Right. In other words, it causes the pointed-to data to reach the end
    of its lifetime. "Changing" the data generally means modifying its
    value (that's what "const" forbids).

    Given:

    int *ptr = malloc(sizeof *ptr);
    *ptr = 42;
    printf("*ptr = %d\n", *ptr);
    free(ptr);

    After the call to free(), the int object logically no longer exists.
    Also, the value of the pointer object ptr becomes indeterminate.
    Attempting to refer to the value of either ptr or *ptr has undefined behavior.

    I must be missing something here. Humm... I thought is was okay to do something like this:
    _____________________________
    #include <stdio.h>
    #include <stdlib.h>

    int main() {
    int* a = malloc(sizeof(*a));

    if (a)
    {
    *a = 42;

    printf("a = %p\n", (void*)a);
    printf("*a = %d\n", *a);

    free(a);

    printf("a = %p was just freed! do not deref\n", (void*)a);
    }

    return 0;
    }
    _____________________________

    Is that okay?

    [...]
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Sat Jan 11 12:14:20 2025
    From Newsgroup: comp.lang.c

    On 10/01/2025 19:56, Keith Thompson wrote:
    David Brown <david.brown@hesbynett.no> writes:
    [...]
    If you want a better signature for "free", then I would suggest "void
    free(void ** p)" - that (to me) more naturally shows that the function
    is freeing the pointer, while also greatly reducing the "use after
    free" errors in C code by turning them into "dereferencing a null
    pointer" errors which are more easily caught by many OS's.

    I'm not sure that would work. A void** argument means you need to pass
    a pointer to a void* object. If you've assigned the converted result of malloc() to, say, an int* object, you don't have a void* object. (int*
    and void* might not even have the same representation).


    Yes, you are right - while "free(void ** p)" might often be feasible in practice (since on most implementations, pointers are the same size and representation) it would at a minimum rely on compilers being somewhat
    lax about accepting these conversions. Certainly it is not something
    that could be part of the standard.

    The idea was to place the emphasis on "free" changing the pointer,
    rather than the data pointed to. But it could not be done as simply as
    I had suggested.

    Some kind of generic function that takes a pointer to an object of any
    object pointer type could work, but the language doesn't support that.
    (C++ addressed this by making `new` and `delete` built-in operators
    rather than library functions.)



    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Richard Damon@richard@damon-family.org to comp.lang.c on Sat Jan 11 09:16:16 2025
    From Newsgroup: comp.lang.c

    On 1/10/25 11:57 PM, Chris M. Thomasson wrote:
    On 1/9/2025 11:40 PM, Keith Thompson wrote:
    Andrey Tarasevich <andreytarasevich@hotmail.com> writes:
    On 01/09/25 12:12 AM, Julio Di Egidio wrote:
    I do not understand that: `free` is changing the pointed data, so
    how can `const void *` even be "correct"?

    `free` is destroying the pointed data.

    Right.  In other words, it causes the pointed-to data to reach the end
    of its lifetime.  "Changing" the data generally means modifying its
    value (that's what "const" forbids).

    Given:

         int *ptr = malloc(sizeof *ptr);
         *ptr = 42;
         printf("*ptr = %d\n", *ptr);
         free(ptr);

    After the call to free(), the int object logically no longer exists.
    Also, the value of the pointer object ptr becomes indeterminate.
    Attempting to refer to the value of either ptr or *ptr has undefined
    behavior.

    I must be missing something here. Humm... I thought is was okay to do something like this:
    _____________________________
    #include <stdio.h>
    #include <stdlib.h>

    int main() {
        int* a = malloc(sizeof(*a));

        if (a)
        {
            *a = 42;

            printf("a = %p\n", (void*)a);
            printf("*a = %d\n", *a);

            free(a);

            printf("a = %p was just freed! do not deref\n", (void*)a);
        }

        return 0;
    }
    _____________________________

    Is that okay?

    [...]

    No, because the value of a has become indeterminate, and operating on
    it, even to just look at its value, can trap.

    you could save a representation of it either in a char array or as a
    uintptr_t value, and work with that (but not try to recreate a pointer
    with it, as that pointer "value" has become indeterminate).

    This issue CAN occur if the implementation is using segment_tag + offset pointers, and free invalidates the segment_tag of that the pointer used,
    and the implementation will perhaps validate the segment_tag when
    looking at the pointer value. (perhaps pointers are loaded into
    registers that automatically validate the segment_tag in them).
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Sat Jan 11 10:58:44 2025
    From Newsgroup: comp.lang.c

    On 1/10/25 13:56, Keith Thompson wrote:
    David Brown <david.brown@hesbynett.no> writes:
    [...]
    If you want a better signature for "free", then I would suggest "void
    free(void ** p)" - that (to me) more naturally shows that the function
    is freeing the pointer, while also greatly reducing the "use after
    free" errors in C code by turning them into "dereferencing a null
    pointer" errors which are more easily caught by many OS's.

    I'm not sure that would work. A void** argument means you need to pass
    a pointer to a void* object. If you've assigned the converted result of malloc() to, say, an int* object, you don't have a void* object. (int*
    and void* might not even have the same representation).

    Correct. As a result, that interface would, in principle, require
    storing the pointer in a void* object so that it's address could be
    passed to free(). In many contexts, that would encourage saving the
    original void* value returned by malloc() for passing to free(), while
    creating a second pointer for actually using the allocated memory.


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Sat Jan 11 17:07:36 2025
    From Newsgroup: comp.lang.c

    On 11/01/2025 12:14, David Brown wrote:
    On 10/01/2025 19:56, Keith Thompson wrote:
    <snip>
    The idea was to place the emphasis on "free" changing the pointer,
    rather than the data pointed to.

    I feel I am still altogether missing the point.

    Is my understanding correct that when freeing a pointer: 1) the pointer
    value, i.e. the address it holds, does not change; OTOH, 2) the
    pointed-to object does change, in the sense that it is marked unusable
    (and, supposedly, made available to re-allocation)?

    Moreover, while the pointer value has not changed, it is in fact changed
    in the sense that it has become invalid, namely the pointer cannot be
    used (validly dereferenced) anymore. Not just that, but *every* pointer
    to the same object, i.e. holding the same address, has become invalid.

    All that considered, how isn't `void free(void *p)`, i.e. with no const qualifiers anywhere, the only reasonable signature?

    -Julio

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Sat Jan 11 17:21:25 2025
    From Newsgroup: comp.lang.c

    On 11/01/2025 17:07, Julio Di Egidio wrote:
    On 11/01/2025 12:14, David Brown wrote:
    On 10/01/2025 19:56, Keith Thompson wrote:
    <snip>
    The idea was to place the emphasis on "free" changing the pointer,
    rather than the data pointed to.

    I feel I am still altogether missing the point.

    Is my understanding correct that when freeing a pointer: 1) the pointer value, i.e. the address it holds, does not change; OTOH, 2) the
    pointed-to object does change, in the sense that it is marked unusable
    (and, supposedly, made available to re-allocation)?

    Moreover, while the pointer value has not changed, it is in fact changed
    in the sense that it has become invalid, namely the pointer cannot be
    used (validly dereferenced) anymore.  Not just that, but *every* pointer
    to the same object, i.e. holding the same address, has become invalid.

    All that considered, how isn't `void free(void *p)`, i.e. with no const qualifiers anywhere, the only reasonable signature?

    In fact, along that line, I could see one might insist that "strictly speaking, it should be `void free(void *const p)` because the pointer
    value is not changed" (my considerations above are indeed more
    "semantic"), OTOH, I just cannot see a case for `void free(void const
    *p)`, not even strictly technically.

    -Julio

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Sat Jan 11 17:33:46 2025
    From Newsgroup: comp.lang.c

    On 11/01/2025 17:21, Julio Di Egidio wrote:
    On 11/01/2025 17:07, Julio Di Egidio wrote:
    On 11/01/2025 12:14, David Brown wrote:
    On 10/01/2025 19:56, Keith Thompson wrote:
    <snip>
    The idea was to place the emphasis on "free" changing the pointer,
    rather than the data pointed to.

    I feel I am still altogether missing the point.

    Is my understanding correct that when freeing a pointer: 1) the
    pointer value, i.e. the address it holds, does not change; OTOH, 2)
    the pointed-to object does change, in the sense that it is marked
    unusable (and, supposedly, made available to re-allocation)?

    Moreover, while the pointer value has not changed, it is in fact
    changed in the sense that it has become invalid, namely the pointer
    cannot be used (validly dereferenced) anymore.  Not just that, but
    *every* pointer to the same object, i.e. holding the same address, has
    become invalid.

    All that considered, how isn't `void free(void *p)`, i.e. with no
    const qualifiers anywhere, the only reasonable signature?

    In fact, along that line, I could see one might insist that "strictly speaking, it should be `void free(void *const p)` because the pointer
    value is not changed" (my considerations above are indeed more
    "semantic"), OTOH, I just cannot see a case for `void free(void const
    *p)`, not even strictly technically.

    P.S. Sorry, I might have snipped too much: to be clear, David Brown is actually referring to a `void free(void ** p)` in his statement above,
    so his "changing the pointer" is to be read in that context: I am back
    to the question of is there a problem with the standard signature of `free`.

    -Julio

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Andrey Tarasevich@andreytarasevich@hotmail.com to comp.lang.c on Sat Jan 11 08:38:40 2025
    From Newsgroup: comp.lang.c

    On 01/10/25 10:37 AM, Kaz Kylheku wrote:

    Destruction by malloc is modifying in any system that recycles the
    memory for another allocation.


    From such a radically "physical" point of view, nothing is and nothing
    will ever be `const`... Sorry, this is not even close to what
    "constness" in C is about.

    In C and C++ (as well in virtually all higher level languages)
    "constness" is not a physical concept. It is a purely high-level
    logic-level concept, designed, implemented and enforced entirely by the
    author of the code (of the public interface of the module) in accordance
    with their intent.

    It has absolutely no relation to any physical modifications that might
    occur anywhere in the execution environment. Nobody cares whether
    something somewhere gets "modified". It is always a question of whether
    _I_ want to recognize such modifications as part of the public interface designed by me. I'm the one who says whether the operation is "constant"
    or not, based purely on my idea of "logical constness".

    That's the reason `const` exists in C (and C++).

    However (returning to the more narrowly focused matter at hand), two
    things - creation and deletion of objects - will always indisputably
    stand apart as operations that transcend/defeat/ignore the idea of
    "constness" with relation to the object itself. Creation/deletion might logically be seen as "non-constant" wrt to the surrounding environment
    (e.g. memory manager), but wrt to the object itself they shall not (and, obviously, cannot) care about its "constness" at all.

    An object begins being `const` only after the process of its creation (construction) is complete. An object stops being `const` the moment the process of its destruction begins. That's how it works in C and C++.
    (Again, using C++ as an example, you all know that `const` objects are
    not seen as `const` inside their constructors and destructors.) In C we
    don't have constructors and destructors, but we still naturally follow
    the same ideas in hand-written code. It would've been nice to have
    `free` to play along with this.
    --
    Best regards,
    Andrey

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Sat Jan 11 14:50:38 2025
    From Newsgroup: comp.lang.c

    Julio Di Egidio <julio@diegidio.name> writes:
    On 11/01/2025 12:14, David Brown wrote:
    On 10/01/2025 19:56, Keith Thompson wrote:
    <snip>
    The idea was to place the emphasis on "free" changing the pointer,
    rather than the data pointed to.

    I feel I am still altogether missing the point.

    Is my understanding correct that when freeing a pointer: 1) the
    pointer value, i.e. the address it holds, does not change; OTOH, 2)
    the pointed-to object does change, in the sense that it is marked
    unusable (and, supposedly, made available to re-allocation)?

    Moreover, while the pointer value has not changed, it is in fact
    changed in the sense that it has become invalid, namely the pointer
    cannot be used (validly dereferenced) anymore. Not just that, but
    *every* pointer to the same object, i.e. holding the same address, has
    become invalid.

    All that considered, how isn't `void free(void *p)`, i.e. with no
    const qualifiers anywhere, the only reasonable signature?

    It is.

    The current declaration `void free(void *p)` implies that the free
    function does not promise to modify the data pointed to by p. In fact
    it may or may not do so. (No program can tell whether the data is
    modified without undefined behavior.)

    `void free(const void *p)` would imply a promise by free that it will
    not modify that data. Since it does so in some implementations and
    since any attempt to access that data would have undefined behavior,
    that would not be useful.

    In `void free(void *const p)`, the "const" would be nearly meaningless.
    p is a parameter, a local object in the implementation of free. The
    "const", if it appears in the definition (assuming free() is implemented
    as a C function), would be a promise not to modify that local object -- something that should be of no relevance to callers. In the
    declaration, it means nothing.

    The visible declaration of free does not, and cannot, communicate all
    the relevant information about it. The allocated data reaches the end
    of its lifetime, and the pointer value passed to free therefore becomes indeterminate. The same thing happens with a pointer to a local object
    when that object reaches the end of its lifetime:

    {
    int *p;
    {
    int n = 42;
    p = &n;
    }
    printf("p = %p\n", (void*)p); // undefined behavior
    }

    C's declaration syntax can't express those effects, which is why they're
    stated explicitly in the standard.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Sat Jan 11 19:29:27 2025
    From Newsgroup: comp.lang.c

    On 1/11/25 11:07, Julio Di Egidio wrote:
    ...
    ... Not just that, but *every* pointer to the same object, i.e.
    holding the same address, has become invalid.

    It's even stronger than that: every pointer at any location within or
    one past the end of the allocated memory also becomes invalid.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Sat Jan 11 19:29:55 2025
    From Newsgroup: comp.lang.c

    On 1/11/25 11:21, Julio Di Egidio wrote:
    ...
    In fact, along that line, I could see one might insist that "strictly speaking, it should be `void free(void *const p)

    You should keep in mind that the 'const' in that declaration is
    meaningless. Normally,

    "If the function is defined with a type that is not compatible with the
    type (of the expression) pointed to by the expression that denotes the
    called function, the behavior is undefined." (6.5.2.2p7)

    However,

    "For two function types to be compatible ... In the determination of
    type compatibility and of a composite type, ... each parameter declared
    with qualified type is taken as having the unqualified version of its
    declared type." (6.7.6.3p14)

    Therefore, it's entirely permissible for a function's definition to have parameters with different qualifiers than the ones specified in the
    function declaration. And the ones specified in the function declaration
    have no effect on the validity or meaning of any expression using that declaration. Such qualifiers only have meaning within the function's
    definition itself.



    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Sat Jan 11 18:23:48 2025
    From Newsgroup: comp.lang.c

    On 1/11/2025 6:16 AM, Richard Damon wrote:
    On 1/10/25 11:57 PM, Chris M. Thomasson wrote:
    On 1/9/2025 11:40 PM, Keith Thompson wrote:
    Andrey Tarasevich <andreytarasevich@hotmail.com> writes:
    On 01/09/25 12:12 AM, Julio Di Egidio wrote:
    I do not understand that: `free` is changing the pointed data, so
    how can `const void *` even be "correct"?

    `free` is destroying the pointed data.

    Right.  In other words, it causes the pointed-to data to reach the end
    of its lifetime.  "Changing" the data generally means modifying its
    value (that's what "const" forbids).

    Given:

         int *ptr = malloc(sizeof *ptr);
         *ptr = 42;
         printf("*ptr = %d\n", *ptr);
         free(ptr);

    After the call to free(), the int object logically no longer exists.
    Also, the value of the pointer object ptr becomes indeterminate.
    Attempting to refer to the value of either ptr or *ptr has undefined
    behavior.

    I must be missing something here. Humm... I thought is was okay to do
    something like this:
    _____________________________
    #include <stdio.h>
    #include <stdlib.h>

    int main() {
         int* a = malloc(sizeof(*a));

         if (a)
         {
             *a = 42;

             printf("a = %p\n", (void*)a);
             printf("*a = %d\n", *a);

             free(a);

             printf("a = %p was just freed! do not deref\n", (void*)a); >>      }

         return 0;
    }
    _____________________________

    Is that okay?

    [...]

    No, because the value of a has become indeterminate, and operating on
    it, even to just look at its value, can trap.

    Argh! Shit. Thanks.


    you could save a representation of it either in a char array or as a uintptr_t value, and work with that (but not try to recreate a pointer
    with it, as that pointer "value" has become indeterminate).

    This issue CAN occur if the implementation is using segment_tag + offset pointers, and free invalidates the segment_tag of that the pointer used,
    and the implementation will perhaps validate the segment_tag when
    looking at the pointer value. (perhaps pointers are loaded into
    registers that automatically validate the segment_tag in them).

    Any better?

    ________________________________
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdint.h>
    #include <inttypes.h>

    int main() {
    int* a = malloc(sizeof(*a));

    if (a)
    {
    *a = 42;

    printf("a = %p\n", (void*)a);
    printf("*a = %d\n", *a);

    uintptr_t x = (uintptr_t)a;

    free(a);

    printf("x = %" PRIxPTR " was just freed! do not deref\n", x);
    }

    return 0;
    }
    ________________________________
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Kaz Kylheku@643-408-1753@kylheku.com to comp.lang.c on Sun Jan 12 02:29:09 2025
    From Newsgroup: comp.lang.c

    On 2025-01-11, Andrey Tarasevich <andreytarasevich@hotmail.com> wrote:
    On 01/10/25 10:37 AM, Kaz Kylheku wrote:

    Destruction by malloc is modifying in any system that recycles the
    memory for another allocation.


    From such a radically "physical" point of view, nothing is and nothing
    will ever be `const`... Sorry, this is not even close to what
    "constness" in C is about.

    Most constness about C is about creating a read-only alias designating
    an object. It's integrated in the type system in a way that encourages spreading/contagion.

    However, objects defined with a declared type that is const are allowed
    to be actually read-only.

    In C and C++ (as well in virtually all higher level languages)
    "constness" is not a physical concept.

    It is for defined objects. A const object of static duration can be
    put into ROM, or virtual memory configured read-only. Either of these
    can generate a trap if written.

    It is a purely high-level
    logic-level concept, designed, implemented and enforced entirely by the author of the code (of the public interface of the module) in accordance with their intent.

    Often it is, but not always.
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Sat Jan 11 18:32:34 2025
    From Newsgroup: comp.lang.c

    On 1/11/2025 2:50 PM, Keith Thompson wrote:
    Julio Di Egidio <julio@diegidio.name> writes:
    On 11/01/2025 12:14, David Brown wrote:
    On 10/01/2025 19:56, Keith Thompson wrote:
    <snip>
    The idea was to place the emphasis on "free" changing the pointer,
    rather than the data pointed to.

    I feel I am still altogether missing the point.

    Is my understanding correct that when freeing a pointer: 1) the
    pointer value, i.e. the address it holds, does not change; OTOH, 2)
    the pointed-to object does change, in the sense that it is marked
    unusable (and, supposedly, made available to re-allocation)?

    Moreover, while the pointer value has not changed, it is in fact
    changed in the sense that it has become invalid, namely the pointer
    cannot be used (validly dereferenced) anymore. Not just that, but
    *every* pointer to the same object, i.e. holding the same address, has
    become invalid.

    All that considered, how isn't `void free(void *p)`, i.e. with no
    const qualifiers anywhere, the only reasonable signature?

    It is.

    The current declaration `void free(void *p)` implies that the free
    function does not promise to modify the data pointed to by p. In fact
    it may or may not do so. (No program can tell whether the data is
    modified without undefined behavior.)

    `void free(const void *p)` would imply a promise by free that it will
    not modify that data. Since it does so in some implementations and
    since any attempt to access that data would have undefined behavior,
    that would not be useful.

    In `void free(void *const p)`, the "const" would be nearly meaningless.
    p is a parameter, a local object in the implementation of free. The
    "const", if it appears in the definition (assuming free() is implemented
    as a C function), would be a promise not to modify that local object -- something that should be of no relevance to callers. In the
    declaration, it means nothing.

    The visible declaration of free does not, and cannot, communicate all
    the relevant information about it. The allocated data reaches the end
    of its lifetime, and the pointer value passed to free therefore becomes indeterminate. The same thing happens with a pointer to a local object
    when that object reaches the end of its lifetime:

    {
    int *p;
    {
    int n = 42;
    p = &n;
    }
    printf("p = %p\n", (void*)p); // undefined behavior
    }

    C's declaration syntax can't express those effects, which is why they're stated explicitly in the standard.


    I hope that that undefined behavior cannot call accidentally call a
    function called launch_missiles(FULL_SPREAD_MULTI_LAUNCH) or something.
    Damn it. ;^)
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Sat Jan 11 20:03:12 2025
    From Newsgroup: comp.lang.c

    Kaz Kylheku <643-408-1753@kylheku.com> writes:
    On 2025-01-11, Andrey Tarasevich <andreytarasevich@hotmail.com> wrote:
    [...]
    In C and C++ (as well in virtually all higher level languages)
    "constness" is not a physical concept.

    It is for defined objects. A const object of static duration can be
    put into ROM, or virtual memory configured read-only. Either of these
    can generate a trap if written.

    True, but a conforming implementation could put all objects into
    read/write memory. Attempting to modify a const-qualified object
    still has undefined behavior, but is likely to "work", unless the
    compiler performs optimizations based on the assumption that such
    an object won't be modified.

    For that matter, a compiler can put a non-const object into ROM if
    it can determine that the program never attempts to modify it.
    (As far as I know that's not a common optimization.)

    All the standard really says about "const" is that directly modifying
    a const-qualified object is a constraint violation, and indirectly
    modifying it has undefined behavior. This gives compilers some
    additional freedom in how to treat such objects (storing them in
    ROM, for example) *and* provides some information that can be used
    in optimization (e.g., not reloading a value from memory because
    it can't have changed).
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Sun Jan 12 13:07:44 2025
    From Newsgroup: comp.lang.c

    On Fri, 10 Jan 2025 08:50:07 -0500
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:

    On 1/10/25 05:23, Michael S wrote:
    On Thu, 09 Jan 2025 23:40:52 -0800
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    ...
    After the call to free(), the int object logically no longer
    exists. Also, the value of the pointer object ptr becomes
    indeterminate. Attempting to refer to the value of either ptr or
    *ptr has undefined behavior.


    I believe that the Standard really says that, but find the part
    about value of ptr variable ridiculous. It breaks natural hierarchy
    by which standard library is somewhat special, but it is not above
    rules of core language. free() is defined as function rather than
    macro. By rules of core language, a function call can not modify
    the value of local variable at caller's scope, unless pointers to
    the variable was passed to it explicitly.

    And that applies to free() just as much as any user-defined function.

    Keep in mind that if p is a value returned by a call to a memory
    management function (alloc_aligned(), malloc(), calloc() or
    realloc()), the values of all pointers anywhere in the program that
    point at any location in the block of memory allocated by the call to
    that memory management function become indeterminate at the same
    time. This doesn't mean that free() has permission to change the bit
    pattern stored in any of those pointer objects. It doesn't. There's
    no legal way to access the values stored in those objects; the only
    thing you can do with those objects as a whole is to store a new
    value in them. It is, however, legal to access the individual bytes
    that make up the pointer's representation. Those bytes are themselves objects, and changing the values stored in those bytes would
    therefore violate 6.2.4p2: "An object exists, has a constant
    address36) , and retains its last-stored value throughout its
    lifetime."

    What the indeterminate value of those pointers does mean is that implementations have permission to remove that entire block of memory
    from the list of valid pointer locations. On a system which uses
    memory maps, that can be as simple as modifying one entry in the
    memory map table.

    By the way, this argument was controversial when I made it in a
    discussion on this newsgroup in 2003 in a thread titled "pointer after
    free indeterminate (your example James)". Yes, I am the "James" in
    that title. You can look up that thread using Google Groups, if you
    want to examine the arguments for and against. I don't believe that
    there's been any relevant changes in the standard, but it's been two
    decades and several revisions of the standard, so I could be wrong
    about that.

    Tried to read the old discussion. Didn't understand much.
    But it strengthened my opinion: it's ridiculous. Standard would be way
    better with this nonsense removed.
    The function below should be guaranteed by standard to return 42.
    int foo(void)
    {
    char* a = malloc(100);
    if (!a) return 42;
    char* b = a + 42;
    free(a);
    return b - a;
    }

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Sun Jan 12 03:22:36 2025
    From Newsgroup: comp.lang.c

    Michael S <already5chosen@yahoo.com> writes:
    [...]
    Tried to read the old discussion. Didn't understand much.
    But it strengthened my opinion: it's ridiculous. Standard would be way
    better with this nonsense removed.
    The function below should be guaranteed by standard to return 42.
    int foo(void)
    {
    char* a = malloc(100);
    if (!a) return 42;
    char* b = a + 42;
    free(a);
    return b - a;
    }

    How exactly would that be useful?

    The intent, I think, is to allow for implementations that perform
    checking when loading a pointer value. It's possible that there
    currently are no such implementations, but even so, how would
    reading a free()d pointer value be useful?

    I'd say that passing a pointer value to free() means you no longer care
    about it.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Sun Jan 12 14:57:25 2025
    From Newsgroup: comp.lang.c

    On 10/01/2025 20:28, Kaz Kylheku wrote:
    On 2025-01-09, David Brown <david.brown@hesbynett.no> wrote:
    On 08/01/2025 17:48, Andrey Tarasevich wrote:


    It is perfectly safe. One can even argue that standard declaration if
    `free` as `void free(void *)` is defective. It should have been `void
    free(const void *)` from the very beginning.

    It is common in simple heap implementations for the allocated block to
    contain data about the block, such as allocation sizes and pointers to
    other blocks, in memory just below the address returned by malloc.
    free() then uses its parameter to access that data, and may change it.
    So "void free(const void *);" would be lying to the user.

    If the pointer came from the allocator, as required, there is no lie.
    The object is writable, and so free may strip away the qualifier
    and do whatever it wants, like cover the object with 0xFE bytes.


    My comment about "lying to the user" is not in regards to what the
    language allows as defined behaviour - it's about what a function
    declaration says to other programmers. The function :

    int square(int x) { return x * x * x; }

    is not lying to the compiler or undefined behaviour - but it /is/ lying
    to the user.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Richard Harnden@richard.nospam@gmail.invalid to comp.lang.c on Sun Jan 12 16:54:17 2025
    From Newsgroup: comp.lang.c

    On 12/01/2025 11:22, Keith Thompson wrote:
    Michael S <already5chosen@yahoo.com> writes:
    [...]
    Tried to read the old discussion. Didn't understand much.
    But it strengthened my opinion: it's ridiculous. Standard would be way
    better with this nonsense removed.
    The function below should be guaranteed by standard to return 42.
    int foo(void)
    {
    char* a = malloc(100);
    if (!a) return 42;
    char* b = a + 42;
    free(a);
    return b - a;
    }

    How exactly would that be useful?

    The intent, I think, is to allow for implementations that perform
    checking when loading a pointer value. It's possible that there
    currently are no such implementations, but even so, how would
    reading a free()d pointer value be useful?

    I'd say that passing a pointer value to free() means you no longer care
    about it.


    What if, after the free(a), you have ...

    char *b = malloc(100);

    ... and the old address of 'a' gets reused.

    Could you then carry on using 'a'?

    You'd be relying on chance, and it'd be terribly bad form ... but would
    it be legal?

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From antispam@antispam@fricas.org (Waldek Hebisch) to comp.lang.c on Sun Jan 12 22:29:36 2025
    From Newsgroup: comp.lang.c

    Ben Bacarisse <ben@bsb.me.uk> wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    On 09/01/2025 02:09, Ben Bacarisse wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT;

    pT = malloc(sizeof(AvlTree_t));

    if (!pT) {
    return NULL;
    }

    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;

    return pT;
    }
    Just on a side issue, I prefer to make tests like this positive so I'd
    write:
    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT = malloc(*pT);
    if (pT) {
    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;
    }
    return pT;
    }
    I'm not going to "make a case" for this (though I will if you want!) --
    I just think it helps to see lots of different styles.

    That is *more* error prone,

    I would be happy for you to expand on why you say that.

    Julio did not want to give his reasons, but I have reasons to
    prefer "negative" version too. Namely, significant trouble
    with malloc is ensuring correct handling of failing case.
    Since this code is not excercised by normal execution, this
    should be done by robust coding pattern. In particular,
    it is easier to check correctness when handling code is
    immediately after 'malloc' and the test. So

    AvlTree_t *pT = malloc(*pT);
    if (!pT) {
    return pT;
    }
    ...

    makes quite visible that handling is there and it is rather
    minimal (in particular caller have to cope with null pointer
    return). That is easily lost in "posive" version, where
    error handling may be far away from the allocation and test.

    Basically, this is similar idea to "defer" discussed in
    another thread, just in this case requires no language
    extention and simply adopting appropriate coding style
    gives the effect.
    --
    Waldek Hebisch
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Sun Jan 12 23:45:24 2025
    From Newsgroup: comp.lang.c

    On 12/01/2025 23:29, Waldek Hebisch wrote:
    Ben Bacarisse <ben@bsb.me.uk> wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    I would be happy for you to expand on why you say that.

    Julio did not want to give his reasons,

    I did give reasons, plenty actually: some people just cannot but keep pretending.

    -Julio

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Sun Jan 12 16:25:23 2025
    From Newsgroup: comp.lang.c

    Julio Di Egidio <julio@diegidio.name> writes:

    On 11/01/2025 12:14, David Brown wrote:

    On 10/01/2025 19:56, Keith Thompson wrote:

    <snip>

    The idea was to place the emphasis on "free" changing the pointer,
    rather than the data pointed to.

    I feel I am still altogether missing the point.

    Is my understanding correct that when freeing a pointer: 1) the
    pointer value, i.e. the address it holds, does not change; OTOH, 2)
    the pointed-to object does change, in the sense that it is marked
    unusable (and, supposedly, made available to re-allocation)?

    Not exactly. The bits in the pointer don't change. The _meaning_
    of those bits, which is to say what address the bits correspond to,
    or even if there is a corresponding address, _does_ change, in the
    sense that any attempt (in a C program) to look at the bits as an
    address is -- as far as the C standard is concerned -- completely unpredictable.

    Moreover, while the pointer value has not changed, it is in fact
    changed in the sense that it has become invalid, namely the pointer
    cannot be used (validly dereferenced) anymore. Not just that, but
    *every* pointer to the same object, i.e. holding the same address, has
    become invalid.

    It isn't just that those pointers cannot be dereferenced. Even
    trying to load their values might blow up the program, or even do
    anything else one might imagine.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Sun Jan 12 16:46:29 2025
    From Newsgroup: comp.lang.c

    On 1/7/2025 11:32 AM, Julio Di Egidio wrote:
    Hi everybody,

    I am back to programming in C after many years:
    indeed I have forgotten so many things, including
    how much I love this language. :)

    From reading this thread I take it you would prefer this style: ______________________
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdint.h>
    #include <inttypes.h>

    int main()
    {
    int* a = malloc(sizeof(*a));

    if (! a)
    {
    // shit happens! ;^o
    return EXIT_FAILURE;
    }

    // okay...
    *a = 42;

    printf("a = %p\n", (void*)a);
    printf("*a = %d\n", *a);

    uintptr_t x = (uintptr_t)a;

    free(a);

    printf("x = %" PRIxPTR " was just freed! do not deref\n", x);

    return 0;
    }

    ______________________



    over this thing:
    ______________________

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

    int main()
    {
    int* a = malloc(sizeof(*a));

    if (a)
    {
    *a = 42;

    printf("a = %p\n", (void*)a);
    printf("*a = %d\n", *a);

    uintptr_t x = (uintptr_t)a;

    free(a);

    printf("x = %" PRIxPTR " was just freed! do not deref\n", x);

    return EXIT_SUCCESS;
    }

    return EXIT_FAILURE;
    }

    ______________________

    ? Am I on the right track with your form? Say you were reviewing by
    code. Bad, or really bad!

    ;^)


    [...]
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Sun Jan 12 18:26:33 2025
    From Newsgroup: comp.lang.c

    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    All the standard really says about "const" is that directly modifying
    a const-qualified object is a constraint violation, and indirectly
    modifying it has undefined behavior. [...]

    This description isn't exactly right. The constraint violation is
    for trying to modify an object through an lvalue that is not
    modifiable, whether or not the object being referenced is const
    qualified. There is undefined behavior for trying to modify a const
    object, but only for objects _defined_ with a const-qualified type.
    So the rule doesn't apply to any object that resides in allocated
    memory, because those objects are not named in any declaration, and
    thus not defined with a const-qualified type.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Sun Jan 12 22:10:18 2025
    From Newsgroup: comp.lang.c

    On 1/9/25 21:51, Julio Di Egidio wrote:
    On 10/01/2025 03:14, Julio Di Egidio wrote:
    ...
    Julio Di Egidio <julio@diegidio.name> writes:

    On 09/01/2025 02:09, Ben Bacarisse wrote:

    Julio Di Egidio <julio@diegidio.name> writes:
    ...
      static AvlTree_t const *AvlTree_node(
      void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
      ) {
      AvlTree_t *pT = malloc(*pT);
      if (pT) {
      pT->pk = pk;
      pT->pL = pL;
      pT->pR = pR;
      }
      return pT;
      }
    I'm not going to "make a case" for this (though I will if you
    want!) -- I just think it helps to see lots of different styles. >>>>>>>
    That is *more* error prone,
    ...
    ... check the return value as soon as the function returns a possibly
    null pointer or an error value is certainly more widely applicable,
    and quite less error prone, especially if it's

    I meant: immediately check the return value and bail out if needed.
    The other approach does not even simplify on the clean-up, by the way...

    The code you're criticizing as more error prone does check the return
    value as soon as the function returns, and bails out if needed. It just
    bails out through the missing else clause rather than from the if-clause.

    It does requires code to be indented farther than the other approach. I
    have avoided writing code like that for that reason, particularly when
    there's a lot of code inside the it statement's controlled blocks -but
    not because it's error prone.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Sun Jan 12 22:38:32 2025
    From Newsgroup: comp.lang.c

    Andrey Tarasevich <andreytarasevich@hotmail.com> writes:

    On 01/10/25 10:37 AM, Kaz Kylheku wrote:

    Destruction by malloc is modifying in any system that recycles the
    memory for another allocation.

    From such a radically "physical" point of view, nothing is and nothing
    will ever be `const`... Sorry, this is not even close to what
    "constness" in C is about.

    In C and C++ (as well in virtually all higher level languages)
    "constness" is not a physical concept. It is a purely high-level
    logic-level concept, designed, implemented and enforced entirely by
    the author of the code (of the public interface of the module) in
    accordance with their intent.

    It has absolutely no relation to any physical modifications that might
    occur anywhere in the execution environment. Nobody cares whether
    something somewhere gets "modified". It is always a question of
    whether _I_ want to recognize such modifications as part of the public interface designed by me. I'm the one who says whether the operation
    is "constant" or not, based purely on my idea of "logical constness".

    That's the reason `const` exists in C (and C++).

    However (returning to the more narrowly focused matter at hand), two
    things - creation and deletion of objects - will always indisputably
    stand apart as operations that transcend/defeat/ignore the idea of "constness" with relation to the object itself. Creation/deletion
    might logically be seen as "non-constant" wrt to the surrounding
    environment (e.g. memory manager), but wrt to the object itself they
    shall not (and, obviously, cannot) care about its "constness" at all.

    An object begins being `const` only after the process of its creation (construction) is complete. [...]

    This idea doesn't match how C semantics works. In particular, an
    object in C can be "created" (start its lifetime) before it is
    "constructed" (initialized). Consider the following code:

    #include <stdio.h>

    int
    main(){
    int i = 0;
    const int *p;
    {
    MORE:
    if( i > 0 ) printf( " i, *p : %2d %2d\n", i, *p );
    const int j = i+7;
    p = &j;
    i++;
    if( i < 10 ) goto MORE;
    }
    return 0;
    }

    The object for j comes into existence once when the inner compound
    statement is entered, but it is initialized 10 times. The access
    to j through *p has well-defined behavior. The initializing
    declaration for j (which is also a definition) does not modify the
    value of j, in the sense that the C standard uses the term, but
    only initializes j, even though the same object is being
    initialized over and over.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Sun Jan 12 22:55:21 2025
    From Newsgroup: comp.lang.c

    Julio Di Egidio <julio@diegidio.name> writes:

    On 10/01/2025 02:43, Tim Rentsch wrote:

    Julio Di Egidio <julio@diegidio.name> writes:

    On 10/01/2025 00:37, Julio Di Egidio wrote:

    On 10/01/2025 00:23, Ben Bacarisse wrote:

    Julio Di Egidio <julio@diegidio.name> writes:

    On 09/01/2025 02:09, Ben Bacarisse wrote:

    Julio Di Egidio <julio@diegidio.name> writes:

    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT;

    pT = malloc(sizeof(AvlTree_t));

    if (!pT) {
    return NULL;
    }

    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;

    return pT;
    }

    Just on a side issue, I prefer to make tests like this positive
    so I'd write:
    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT = malloc(*pT);
    if (pT) {
    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;
    }
    return pT;
    }
    I'm not going to "make a case" for this (though I will if you
    want!) -- I just think it helps to see lots of different styles.

    That is *more* error prone,

    I would be happy for you to expand on why you say that.

    all the more so if it's not a 5 liner...

    There is no such thing as expanding 40 years of professional
    experience in software engineering and programming and doing it
    properly since day one: just think about that code and what I said
    for what it's worth, in particular I haven't mentioned 5 liners by
    chance, things are quite more complicated not in vitro.

    And please do not hold a grudge about that: it's not me who was
    trying to say how to write code... ;)

    BTW, I hadn't mention it, but have you noticed the second one is
    misindented? Between me and you, I can tell how long a piece of
    code will take to break when in production by just looking at
    it... A lot of fun. :)

    The indentation was correct in Ben's original posting.

    The misindentation first appeared in your followup to that
    posting, where the quoted portion had been changed to remove a
    blank line and over-indent the if().

    But indeed the point is what happens in the long run: if you look
    above mine is still better indented... :) But of course it is not indentation per se the problem: for example check the return value as
    soon as the function returns a possibly null pointer or an error value
    is certainly more widely applicable, and quite less error prone,
    especially if it's not a 5 liner...

    I don't necessarily share your stylistic judgments. But in any
    case it was not my intention to participate in a style debate
    but only to correct a misleading impression from a statement in
    your posting.

    Anyway, I also truly believe
    there is no point in belabouring the point: [...]

    Then you should stop belabouring it. No one else is.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Sun Jan 12 23:10:27 2025
    From Newsgroup: comp.lang.c

    Richard Harnden <richard.nospam@gmail.invalid> writes:

    On 12/01/2025 11:22, Keith Thompson wrote:

    Michael S <already5chosen@yahoo.com> writes:
    [...]

    Tried to read the old discussion. Didn't understand much.
    But it strengthened my opinion: it's ridiculous. Standard would be way >>> better with this nonsense removed.
    The function below should be guaranteed by standard to return 42.
    int foo(void)
    {
    char* a = malloc(100);
    if (!a) return 42;
    char* b = a + 42;
    free(a);
    return b - a;
    }

    How exactly would that be useful?

    The intent, I think, is to allow for implementations that perform
    checking when loading a pointer value. It's possible that there
    currently are no such implementations, but even so, how would
    reading a free()d pointer value be useful?

    I'd say that passing a pointer value to free() means you no longer care
    about it.

    What if, after the free(a), you have ...

    char *b = malloc(100);

    ... and the old address of 'a' gets reused.

    Could you then carry on using 'a'?

    You'd be relying on chance, and it'd be terribly bad form ... but
    would it be legal?

    The short answer is it would be a mistake to keep using a.

    The reason for this is as follows. Even if the bits of a and
    the bits of b are the same, and even if the two sets of bits
    mean the same thing as far as the hardware is concerned, they
    don't have to mean the same thing as far as a C compiler is
    concerned. The C standard empowers C implementations to treat
    any further accesses to a in any way they choose, even when as
    far the hardware is concerned a and b are interchangeable.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Sun Jan 12 23:31:19 2025
    From Newsgroup: comp.lang.c

    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    Michael S <already5chosen@yahoo.com> writes:
    [...]

    Tried to read the old discussion. Didn't understand much.
    But it strengthened my opinion: it's ridiculous. Standard would be way
    better with this nonsense removed.
    The function below should be guaranteed by standard to return 42.
    int foo(void)
    {
    char* a = malloc(100);
    if (!a) return 42;
    char* b = a + 42;
    free(a);
    return b - a;
    }

    How exactly would that be useful?

    The intent, I think, is to allow for implementations that perform
    checking when loading a pointer value. [...]

    The motivation for the rule that pointers become indeterminate when
    the object they point at has passed the end of its lifetime is that
    the meaning of the bits in the pointer may have changed, and indeed
    the pointer's bits might not even constitute a well-formed address.
    Someone else posted a canonical example, where each stored pointer
    holds a segment number and an offset within segment, and the
    numbered segment no longer exists (which could happen on a free() or
    even on a return from a function call, with each stack frame being
    given its own segment). Loading a pointer into an "address
    register" might want to precalculate an "absolute address", which of
    course is not possible if the segment information no longer exists.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Mon Jan 13 09:58:42 2025
    From Newsgroup: comp.lang.c

    On 13/01/2025 04:10, James Kuyper wrote:
    On 1/9/25 21:51, Julio Di Egidio wrote:
    On 10/01/2025 03:14, Julio Di Egidio wrote:
    ...
    Julio Di Egidio <julio@diegidio.name> writes:

    On 09/01/2025 02:09, Ben Bacarisse wrote:

    Julio Di Egidio <julio@diegidio.name> writes:
    ...
      static AvlTree_t const *AvlTree_node(
      void const *pk, AvlTree_t const *pL, AvlTree_t const *pR >>>>>>>>>   ) {
      AvlTree_t *pT = malloc(*pT);
      if (pT) {
      pT->pk = pk;
      pT->pL = pL;
      pT->pR = pR;
      }
      return pT;
      }
    I'm not going to "make a case" for this (though I will if you >>>>>>>>> want!) -- I just think it helps to see lots of different styles. >>>>>>>>
    That is *more* error prone,
    ...
    ... check the return value as soon as the function returns a possibly
    null pointer or an error value is certainly more widely applicable,
    and quite less error prone, especially if it's

    I meant: immediately check the return value and bail out if needed.
    The other approach does not even simplify on the clean-up, by the way...

    The code you're criticizing as more error prone does check the return
    value as soon as the function returns, and bails out if needed. It just
    bails out through the missing else clause rather than from the if-clause.

    It does requires code to be indented farther than the other approach. I
    have avoided writing code like that for that reason, particularly when there's a lot of code inside the it statement's controlled blocks -but
    not because it's error prone.


    I'm wary of assuming a particular interpretation of what someone else
    wrote, but I would say there is a valid argument for say that the second
    style of code is more "error prone".

    To me, the phrase "error prone" in regard to a coding style, rather than
    a piece of code, means that it is more likely that there will be errors (actual code errors, or misunderstandings by other programmers reading
    the code) written in that style compared to using a different style.

    For a short function like this, it's fairly easy to see that there are
    no errors. In a longer function, perhaps with multiple allocations, the
    risk is higher.

    When you use the "early bail out" form:

    if (!pt) return NULL;

    there are three key points. One is that the error handling is done at
    the same point in the code that the error could occur - right after the "malloc". Secondly, the error handling is explicit - it is not left for
    the reader to realise it is an implicit default result. Thirdly, the
    return of "NULL" rather than "pt" (known to be null at that point),
    makes the error situation stand out more clearly, and it is also easier
    to change it to a different error return value if appropriate.


    You can of course also argue that it is best to have the code ordered according to the normal flow of operations - error handling in the
    middle of the normal code flow can make it hard to follow the algorithm
    of a function. (C++ exceptions are an extreme version of this.) Some
    people might prefer a compromise:


    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT = malloc(*pT);

    if (pT) {
    // Normal case
    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;
    return pT;
    } else {
    // Error case - malloc failed
    return NULL;
    }
    }


    Different styles have different pros and cons, and different balances of emphasis. (They can also have different efficiencies in the normal and
    error cases, but that's usually less important.)


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Mon Jan 13 10:05:25 2025
    From Newsgroup: comp.lang.c

    On 12/01/2025 23:29, Waldek Hebisch wrote:
    Ben Bacarisse <ben@bsb.me.uk> wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    On 09/01/2025 02:09, Ben Bacarisse wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT;

    pT = malloc(sizeof(AvlTree_t));

    if (!pT) {
    return NULL;
    }

    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;

    return pT;
    }
    Just on a side issue, I prefer to make tests like this positive so I'd >>>> write:
    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT = malloc(*pT);
    if (pT) {
    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;
    }
    return pT;
    }
    I'm not going to "make a case" for this (though I will if you want!) -- >>>> I just think it helps to see lots of different styles.

    That is *more* error prone,

    I would be happy for you to expand on why you say that.

    Julio did not want to give his reasons, but I have reasons to
    prefer "negative" version too. Namely, significant trouble
    with malloc is ensuring correct handling of failing case.
    Since this code is not excercised by normal execution, this
    should be done by robust coding pattern. In particular,
    it is easier to check correctness when handling code is
    immediately after 'malloc' and the test. So

    AvlTree_t *pT = malloc(*pT);
    if (!pT) {
    return pT;
    }
    ...

    makes quite visible that handling is there and it is rather
    minimal (in particular caller have to cope with null pointer
    return). That is easily lost in "posive" version, where
    error handling may be far away from the allocation and test.

    Basically, this is similar idea to "defer" discussed in
    another thread, just in this case requires no language
    extention and simply adopting appropriate coding style
    gives the effect.


    "defer" can help for some types of cleanup, but won't help here - you
    can't "defer" an bail-out from the main function - a "return" in the
    deferred code returns from the deferred function, not the main function.
    At least, that is the case with the macro / gcc local function version
    of "defer". A "defer" language feature might be different.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Mon Jan 13 10:08:40 2025
    From Newsgroup: comp.lang.c

    On 12/01/2025 23:45, Julio Di Egidio wrote:
    On 12/01/2025 23:29, Waldek Hebisch wrote:
    Ben Bacarisse <ben@bsb.me.uk> wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    I would be happy for you to expand on why you say that.

    Julio did not want to give his reasons,

    I did give reasons, plenty actually: some people just cannot but keep pretending.


    Could you try posting them again? I've looked through your posts here
    again, and I haven't seen any clear justification for why you think
    Ben's alternative structure for the code is "more error-prone". Waldek
    and I have both given some (very similar) reasons for why /we/ can see
    the style as potentially error-prone, but we'd like to hear what /your/ reasons are.


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Mon Jan 13 07:35:10 2025
    From Newsgroup: comp.lang.c

    On 1/13/25 04:08, David Brown wrote:
    On 12/01/2025 23:45, Julio Di Egidio wrote:
    On 12/01/2025 23:29, Waldek Hebisch wrote:
    Ben Bacarisse <ben@bsb.me.uk> wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    I would be happy for you to expand on why you say that.

    Julio did not want to give his reasons,

    I did give reasons, plenty actually: some people just cannot but keep
    pretending.


    Could you try posting them again? I've looked through your posts here again, and I haven't seen any clear justification for why you think
    Ben's alternative structure for the code is "more error-prone".

    After he claimed that he had already given his reasons, I went back and reviewed his messages. He didn't provide any such reasons as a response
    to Ben's request above, which I think is odd, but I think I found the
    reasons he's referring to on the other branch of this thread:

    On 1/9/25 21:51, Julio Di Egidio wrote:
    ... check the return value as soon as the
    function returns a possibly null pointer or an error value is certainly
    more widely applicable, and quite less error prone, ...
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Mon Jan 13 14:23:07 2025
    From Newsgroup: comp.lang.c

    On 13/01/2025 09:58, David Brown wrote:
    On 13/01/2025 04:10, James Kuyper wrote:
    On 1/9/25 21:51, Julio Di Egidio wrote:

    You can of course also argue that it is best to have the code ordered according to the normal flow of operations - error handling in the
    middle of the normal code flow can make it hard to follow the algorithm
    of a function.  (C++ exceptions are an extreme version of this.)  Some people might prefer a compromise:

    Some people are just wrong and not even honest at that.

    **Please don't mind and don't feed the trolls**:

    I had the misfortune of calling out the utter and ugly bullshit from
    these characters in the past in other groups: this gang of nazi-retarded
    and mostly academic spammers and polluters of all ponds just hates my
    guts since then.

    BTW, on that C++ vs C issue (you know who you are), I respect that but
    I'd say it's wrong: C and C++ share nothing but some syntax, and one is high-level, not the other...

    Thanks again and please use a kill-file: I do.

    Cheers,

    -Julio

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Mon Jan 13 17:30:58 2025
    From Newsgroup: comp.lang.c

    On 13/01/2025 14:23, Julio Di Egidio wrote:
    On 13/01/2025 09:58, David Brown wrote:
    On 13/01/2025 04:10, James Kuyper wrote:
    On 1/9/25 21:51, Julio Di Egidio wrote:

    You can of course also argue that it is best to have the code ordered
    according to the normal flow of operations - error handling in the
    middle of the normal code flow can make it hard to follow the
    algorithm of a function.  (C++ exceptions are an extreme version of
    this.)  Some people might prefer a compromise:

    Some people are just wrong and not even honest at that.

      **Please don't mind and don't feed the trolls**:


    There are a few trollish characters in this newsgroup, but I don't
    believe I have seen any in this thread. There are several people who
    have made suggestions that don't match your opinions - that does not
    make them "trolls", and it most certainly does not make them dishonest.
    Such wild accusations will make you unpopular here, and greatly reduce
    the chances of you getting helpful answers to your questions.

    People in this thread have been discussing various aspects of C
    programming - that's what the newsgroup is all about. It's great that
    you made a post that started such a topical thread that has engaged the interest of many people here. Please don't spoil that with absolute
    nonsense and random insults.

    I had the misfortune of calling out the utter and ugly bullshit from
    these characters in the past in other groups: this gang of nazi-retarded
    and mostly academic spammers and polluters of all ponds just hates my
    guts since then.


    Perhaps you have mixed up threads in different newsgroups? That is not
    a characterisation that matches anyone who posts regularly here. Yes,
    there are a few people that seem to live in ivory towers, and a few who occasionally appear to gain pleasure from annoying everyone else, and
    some who post things that are barely comprehensible. But those are a
    minority of posts, they have not been significant in this thread, and
    even at their worst they do not warrant the kind of abusive
    characterisation you are giving them here.

    BTW, on that C++ vs C issue (you know who you are), I respect that but
    I'd say it's wrong: C and C++ share nothing but some syntax, and one is high-level, not the other...

    While there is one person who posts primarily about C++ in this C group,
    and that can be annoying, it is not unreasonable to make occasional
    references or comparisons to C++ precisely because they are strongly
    related languages that share a great deal of overlap. Many C
    programmers are familiar with C++ - most C++ programmers are familiar
    with C. There are a lot of us that use both languages. They /are/
    different languages, with different strengths and weaknesses, but to
    suggest they share nothing but some syntax shows that you have little understanding of at least one of the languages.


    Thanks again and please use a kill-file: I do.


    I suspect that with your post here, you risk ending up on more than a
    few kill files.

    Please refrain from abusing others, and get back to discussing C.


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Mon Jan 13 20:30:39 2025
    From Newsgroup: comp.lang.c

    On 13/01/2025 17:30, David Brown wrote:
    On 13/01/2025 14:23, Julio Di Egidio wrote:
    On 13/01/2025 09:58, David Brown wrote:
    On 13/01/2025 04:10, James Kuyper wrote:
    On 1/9/25 21:51, Julio Di Egidio wrote:

    You can of course also argue that it is best to have the code ordered
    according to the normal flow of operations - error handling in the
    middle of the normal code flow can make it hard to follow the
    algorithm of a function.  (C++ exceptions are an extreme version of
    this.)  Some people might prefer a compromise:

    Some people are just wrong and not even honest at that.

       **Please don't mind and don't feed the trolls**:

    There are a few trollish characters in this newsgroup, but I don't
    believe I have seen any in this thread.
    <snipped>

    Then, with all due respect, you are being naive: "let's also not forget
    to tell the OP how to write code" is a piece of it, for example. But of course from an impolite and vile request (reread how I was asked
    initially) to me being the impolite one: it happens all the time
    everywhere, I don't even mind anymore.

    Nor it can become my problem that people can hardly ready, indeed
    literalism, mangling quotes especially never going past a formal level
    that misses all that is substantial... which is where I said not all
    code paths are ifs: only formally those two pieces of code are equivalent.

    In fact my explanations go quite past what is just obvious: what does it
    even mean "in the long run?", or that formal is just half of the story
    and not even the most relevant, more relevant is the concrete
    substance...? And should I just repeat myself? Really? For that questioning??

    I had the misfortune of calling out the utter and ugly bullshit from
    these characters in the past in other groups: this gang of
    nazi-retarded and mostly academic spammers and polluters of all ponds
    just hates my guts since then.

    Perhaps you have mixed up threads in different newsgroups?

    Or perhaps you just don't know and in fact don't need to know all the
    gory details and pasty history. I am sorry that I even have to explain,
    to you, who have been kind, polite, professional, to the point, helpful
    and competently so, etc.: and not just you of course.

    BTW, on that C++ vs C issue (you know who you are), I respect that but
    I'd say it's wrong: C and C++ share nothing but some syntax, and one
    is high-level, not the other...

    While there is one person who posts primarily about C++ in this C group,
    and that can be annoying, it is not unreasonable to make occasional references or comparisons to C++ precisely because they are strongly
    related languages that share a great deal of overlap.

    There is a discussion going on and not only in this thread about e.g. "destructors" and what that means: of course I am referring to that and nothing but that. And there is nothing I need to add to that either:
    those who know already know (what I mean), and those who don't, please
    at least learn how to use question marks instead of just pretending.
    Mileages may very: your impoliteness or worse I won't take lightly.

    And I hope that at least makes sense, and again I apologize to the
    innocent and well intentioned, but what can we do, this is the world we
    live in.... Anyway, of course, you be the judge. I am out, indeed I am
    here for the C language, not for anything else: and as far as I am
    concerned this thread (surely my questions) is done.

    -Julio

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Mon Jan 13 11:46:22 2025
    From Newsgroup: comp.lang.c

    Michael S <already5chosen@yahoo.com> writes:

    On Thu, 09 Jan 2025 23:40:52 -0800
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:

    Andrey Tarasevich <andreytarasevich@hotmail.com> writes:

    On 01/09/25 12:12 AM, Julio Di Egidio wrote:

    I do not understand that: `free` is changing the pointed data, so
    how can `const void *` even be "correct"?

    `free` is destroying the pointed data.

    Right. In other words, it causes the pointed-to data to reach the end
    of its lifetime. "Changing" the data generally means modifying its
    value (that's what "const" forbids).

    Given:

    int *ptr = malloc(sizeof *ptr);
    *ptr = 42;
    printf("*ptr = %d\n", *ptr);
    free(ptr);

    After the call to free(), the int object logically no longer exists.
    Also, the value of the pointer object ptr becomes indeterminate.
    Attempting to refer to the value of either ptr or *ptr has undefined
    behavior.

    I believe that the Standard really says that, but find the part about
    value of ptr variable ridiculous. It breaks natural hierarchy by which standard library is somewhat special, but it is not above rules of core language. free() is defined as function rather than macro. By rules of
    core language, a function call can not modify the value of local
    variable at caller's scope, unless pointers to the variable was passed
    to it explicitly.

    There is an important distinction here, one I suspect you are either
    glossing over or missing. The bits in the pointer don't change, but
    the meaning of those bits can change. To say that a different way,
    what memory location a pointer object representation (the bits)
    designates can depend on information outside the pointer object
    itself, depending on the machine architecture. A natural example is
    a machine with segmented memory, where "a pointer" consists of a
    segment number and an offset within the segment. If a free() has
    been done by marking a particular segment invalid (such as by
    setting a bit in a global segment table), the bits in the pointer
    passed to free() don't change, but those bits no longer refer to any
    memory location. The pointer becomes "indeterminate" even though
    none of the bits in the pointer have changed. Does that make sense?

    Editorial sidebar: in my opinion the C standard doesn't describe
    what is going on here as well as it should. What the C standard
    says is that the (pointer) object becomes indeterminate. What is
    really happening is not that the object changes but that the stored
    bits are no longer a valid value representation. It would be more
    accurate to say the validity of the object representation changes,
    starting as valid before the free() and ending up as invalid after
    the free(). Unfortunately there is some historical baggage tied to
    the phrasing here, and so the description says the object becomes indeterminate.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Mon Jan 13 11:55:58 2025
    From Newsgroup: comp.lang.c

    Michael S <already5chosen@yahoo.com> writes:

    On Fri, 10 Jan 2025 08:50:07 -0500
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:

    On 1/10/25 05:23, Michael S wrote:

    On Thu, 09 Jan 2025 23:40:52 -0800
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:

    ...

    After the call to free(), the int object logically no longer
    exists. Also, the value of the pointer object ptr becomes
    indeterminate. Attempting to refer to the value of either ptr or
    *ptr has undefined behavior.

    I believe that the Standard really says that, but find the part
    about value of ptr variable ridiculous. It breaks natural hierarchy
    by which standard library is somewhat special, but it is not above
    rules of core language. free() is defined as function rather than
    macro. By rules of core language, a function call can not modify
    the value of local variable at caller's scope, unless pointers to
    the variable was passed to it explicitly.

    And that applies to free() just as much as any user-defined function.

    Keep in mind that if p is a value returned by a call to a memory
    management function (alloc_aligned(), malloc(), calloc() or
    realloc()), the values of all pointers anywhere in the program that
    point at any location in the block of memory allocated by the call to
    that memory management function become indeterminate at the same
    time. This doesn't mean that free() has permission to change the bit
    pattern stored in any of those pointer objects. It doesn't. There's
    no legal way to access the values stored in those objects; the only
    thing you can do with those objects as a whole is to store a new
    value in them. It is, however, legal to access the individual bytes
    that make up the pointer's representation. Those bytes are themselves
    objects, and changing the values stored in those bytes would
    therefore violate 6.2.4p2: "An object exists, has a constant
    address36) , and retains its last-stored value throughout its
    lifetime."

    What the indeterminate value of those pointers does mean is that
    implementations have permission to remove that entire block of memory
    from the list of valid pointer locations. On a system which uses
    memory maps, that can be as simple as modifying one entry in the
    memory map table.

    By the way, this argument was controversial when I made it in a
    discussion on this newsgroup in 2003 in a thread titled "pointer after
    free indeterminate (your example James)". Yes, I am the "James" in
    that title. You can look up that thread using Google Groups, if you
    want to examine the arguments for and against. I don't believe that
    there's been any relevant changes in the standard, but it's been two
    decades and several revisions of the standard, so I could be wrong
    about that.

    Tried to read the old discussion. Didn't understand much.
    But it strengthened my opinion: it's ridiculous. Standard would be way better with this nonsense removed.
    The function below should be guaranteed by standard to return 42.
    int foo(void)
    {
    char* a = malloc(100);
    if (!a) return 42;
    char* b = a + 42;
    free(a);
    return b - a;
    }

    Imposing that requirement could mean assuming a level of
    architectural support that some hardware doesn't provide.
    Historically the decision made in the original C standard
    was the most sensible choice available. Even though the
    current description leaves something to be desired, it's
    hard to find a compelling argument that the rule be changed
    now.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Mon Jan 13 21:45:39 2025
    From Newsgroup: comp.lang.c

    On 13/01/2025 20:30, Julio Di Egidio wrote:
    On 13/01/2025 17:30, David Brown wrote:
    On 13/01/2025 14:23, Julio Di Egidio wrote:
    On 13/01/2025 09:58, David Brown wrote:
    On 13/01/2025 04:10, James Kuyper wrote:
    On 1/9/25 21:51, Julio Di Egidio wrote:

    You can of course also argue that it is best to have the code
    ordered according to the normal flow of operations - error handling
    in the middle of the normal code flow can make it hard to follow the
    algorithm of a function.  (C++ exceptions are an extreme version of
    this.)  Some people might prefer a compromise:

    Some people are just wrong and not even honest at that.

       **Please don't mind and don't feed the trolls**:

    There are a few trollish characters in this newsgroup, but I don't
    believe I have seen any in this thread.
    <snipped>

    Then, with all due respect, you are being naive: "let's also not forget
    to tell the OP how to write code" is a piece of it, for example.

    Could you point to the post from which you are quoting?

      But of
    course from an impolite and vile request (reread how I was asked
    initially) to me being the impolite one: it happens all the time
    everywhere, I don't even mind anymore.

    Could you point to the post where someone made an "impolite and vile
    request" of you? Perhaps I missed something, but I haven't seen
    anything remotely like that. Example quotations from posts directly
    upthread from here include:

    "I would be happy for you to expand on why you say that."

    "I was not trying to tell anyone how to write code."


    I am completely at a loss to see how these could be viewed as "vile".


    Nor it can become my problem that people can hardly ready, indeed literalism, mangling quotes especially never going past a formal level
    that misses all that is substantial... which is where I said not all
    code paths are ifs: only formally those two pieces of code are equivalent.

    In fact my explanations go quite past what is just obvious: what does it even mean "in the long run?", or that formal is just half of the story
    and not even the most relevant, more relevant is the concrete substance...?  And should I just repeat myself?  Really?  For that questioning??


    It is very difficult to figure out what you are trying to say here. Let
    me try to rephrase it, before addressing it. If you don't think my
    rephrasing is accurate, then my response to it is probably not relevant
    and should be ignored.

    You are annoyed because someone told you that the misleading indentation
    you complained about, was due to artifacts of Usenet post quoting.
    Perhaps you feel that was a side-track. Is that right?

    If so, then be aware that this is Usenet - people are free to post what
    they like, and discuss what they like. It is common practice for a
    thread to branch into subthreads as people latch on to particular things
    that interest them or that they think are worth pointing out to others.

    I had the misfortune of calling out the utter and ugly bullshit from
    these characters in the past in other groups: this gang of
    nazi-retarded and mostly academic spammers and polluters of all ponds
    just hates my guts since then.

    Perhaps you have mixed up threads in different newsgroups?

    Or perhaps you just don't know and in fact don't need to know all the
    gory details and pasty history.  I am sorry that I even have to explain,
    to you, who have been kind, polite, professional, to the point, helpful
    and competently so, etc.: and not just you of course.


    You haven't explained /anything/. As far as I can see, this was a calm
    and reasoned thread that had branched into a number of aspects of
    "const" in C programming, with some exchanges of thoughts, different
    styles, and some pros and cons of various choices. Then you made some consecutive posts that were increasingly hard to understand, and
    suddenly you are accusing multiple unnamed posters of trolling,
    dishonesty, "vile", etc. I simply don't get it. I don't see anything
    from anyone who could have provoked that reaction. I see people
    answering your questions, you thanking them for the answers, people
    branching out and discussing other related topics, and sometimes other
    people asking you things.

    And I've been a regular in this group for far more years than I care to remember. I "know" pretty much everyone in this thread, as most of them
    have also been here forever. None of the people posting here comes
    anywhere close to deserving the accusations you gave - none of them have /ever/ come close to that in the history of this group. (I, on the
    other hand, /have/ provoked extreme reactions on occasion.)

    BTW, on that C++ vs C issue (you know who you are), I respect that
    but I'd say it's wrong: C and C++ share nothing but some syntax, and
    one is high-level, not the other...

    While there is one person who posts primarily about C++ in this C
    group, and that can be annoying, it is not unreasonable to make
    occasional references or comparisons to C++ precisely because they are
    strongly related languages that share a great deal of overlap.

    There is a discussion going on and not only in this thread about e.g. "destructors" and what that means: of course I am referring to that and nothing but that.  And there is nothing I need to add to that either:
    those who know already know (what I mean), and those who don't, please
    at least learn how to use question marks instead of just pretending. Mileages may very: your impoliteness or worse I won't take lightly.

    And I hope that at least makes sense, and again I apologize to the
    innocent and well intentioned, but what can we do, this is the world we
    live in....  Anyway, of course, you be the judge.  I am out, indeed I am here for the C language, not for anything else: and as far as I am
    concerned this thread (surely my questions) is done.


    If that is all the explanation we will get for your outburst, then let's
    leave it there. But no, you are not making sense at all.



    --- Synchronet 3.20a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Mon Jan 13 16:11:10 2025
    From Newsgroup: comp.lang.c

    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
    [...]
    There is an important distinction here, one I suspect you are either
    glossing over or missing. The bits in the pointer don't change, but
    the meaning of those bits can change. To say that a different way,
    what memory location a pointer object representation (the bits)
    designates can depend on information outside the pointer object
    itself, depending on the machine architecture. A natural example is
    a machine with segmented memory, where "a pointer" consists of a
    segment number and an offset within the segment. If a free() has
    been done by marking a particular segment invalid (such as by
    setting a bit in a global segment table), the bits in the pointer
    passed to free() don't change, but those bits no longer refer to any
    memory location. The pointer becomes "indeterminate" even though
    none of the bits in the pointer have changed. Does that make sense?

    A quibble: it's the pointer *value* that becomes indeterminate,
    not the pointer object. After free(p), p is still a valid pointer
    object; you can take its address, store a new value in it, etc.
    You just can't read its value without undefined behavior. Note also
    that the argument to free() needn't be an lvalue; there might not
    be an object holding the value that was passed to free().

    As for free() not changing the bits in the pointer, I'm only
    mostly sure that's guaranteed. On one hand, "An object exists,
    has a constant address, and retains its last-stored value throughout
    its lifetime.", which suggests that the bits don't change. On the
    other hand, a program with defined behavior can't tell whether the
    value changed, since it can't examine the pointer value. On the
    other other hand, a program can tell whether the representation
    changed by examining the pointer object as an array of unsigned char.

    Can free(p) cause the representation of p to become what C23 calls
    a "non-value representation", and what earlier editions called a
    "trap representation"? If so, one could argue that p doesn't have a
    "value", and perhaps the "last-stored value" wording doesn't apply.

    I don't expect any implementation to do anything to the bits of
    a pointer object whose value is passed to free(), but I'm wondering
    whether an implementation that does so could be conforming.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.20a-Linux NewsLink 1.2
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Tue Jan 14 09:02:17 2025
    From Newsgroup: comp.lang.c

    On 13/01/2025 21:45, David Brown wrote:
    On 13/01/2025 20:30, Julio Di Egidio wrote:
    On 13/01/2025 17:30, David Brown wrote:
    On 13/01/2025 14:23, Julio Di Egidio wrote:
    On 13/01/2025 09:58, David Brown wrote:
    On 13/01/2025 04:10, James Kuyper wrote:
    On 1/9/25 21:51, Julio Di Egidio wrote:

    You can of course also argue that it is best to have the code
    ordered according to the normal flow of operations - error handling >>>>> in the middle of the normal code flow can make it hard to follow
    the algorithm of a function.  (C++ exceptions are an extreme
    version of this.)  Some people might prefer a compromise:

    Some people are just wrong and not even honest at that.

       **Please don't mind and don't feed the trolls**:

    There are a few trollish characters in this newsgroup, but I don't
    believe I have seen any in this thread.
    <snipped>

    Then, with all due respect, you are being naive: "let's also not
    forget to tell the OP how to write code" is a piece of it, for example.

    Could you point to the post from which you are quoting?

    That was said to you by Ben Bacarisse at some point early in the thread.
    Another piece of it is from "this is better than that" to "YOU have
    made a claim". Get your head out of you8r ass: I don't give a shit if
    YOU too cannot read. I am not making sense is all you have to say, you
    other fucking cretin??

    It is very difficult to figure out what you are trying to say here.
    Let me try to rephrase it, before addressing it. If you don't think
    my rephrasing is accurate, then my response to it is probably not
    relevant and should be ignored.

    Your blindness and insistence is yet another a piece of stupid and vile
    shit indeed, not just "irrelevant" or off mark, you other stupid piece
    of nazi-retarded shit. Go fuck yourself, you and your whole gang. --
    Happier now? Is that clearer at least? I am all for clarity, you
    stupid fuck. Now try and hack me, so we get to the next level.

    BTW, you piece of retarded shit, I was posting on Usenet from circa 1991
    for your info, and that was not even the beginning of it. I have seen
    this world go down the drain thanks more to the vile morons like you
    than to the true criminals. -- Is that clear enough? I can do even
    better if you keep insisting, more substance and less profanity if I am
    really really careful.

    If that is all the explanation we will get for your outburst, then
    let's leave it there. But no, you are not making sense at all.

    Stupid ignorant deadly and especially vile pieces of shit, the colonists...

    Eat my socks and die.

    *Plonk*

    -Julio

    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From bart@bc@freeuk.com to comp.lang.c on Tue Jan 14 12:44:52 2025
    From Newsgroup: comp.lang.c

    On 14/01/2025 08:02, Julio Di Egidio wrote:

    Stupid ignorant deadly and especially vile pieces of shit, the colonists...

    Eat my socks and die.

    *Plonk*

    I think we just found the mysterious troll you were talking about.

    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Tue Jan 14 14:50:17 2025
    From Newsgroup: comp.lang.c

    On 14/01/2025 13:44, bart wrote:
    On 14/01/2025 08:02, Julio Di Egidio wrote:

    Stupid ignorant deadly and especially vile pieces of shit, the
    colonists...

    Eat my socks and die.

    *Plonk*

    I think we just found the mysterious troll you were talking about.


    I'm not sure if that beats being seriously accused of being possessed by
    the devil (as I once was - I am sure you can guess who by). But at
    least that case took a long time to build up - this character went from
    "you who have been kind, polite, professional, to the point, helpful" to
    a foaming-at-the-mouth meltdown in just one post.

    Ah well. I hope he finds some help somewhere for whatever his problem
    is - he is certainly now unlikely to find much help on C around here!


    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Tue Jan 14 12:46:30 2025
    From Newsgroup: comp.lang.c

    On 1/14/25 03:02, Julio Di Egidio wrote:
    On 13/01/2025 21:45, David Brown wrote:
    On 13/01/2025 20:30, Julio Di Egidio wrote:
    ...
    Then, with all due respect, you are being naive: "let's also not
    forget to tell the OP how to write code" is a piece of it, for example.

    Could you point to the post from which you are quoting?

    That was said to you by Ben Bacarisse at some point early in the thread.

    I found only one message by Ben Bacarisse to David Brown in this thread.
    The entirety of Ben's comments follow:

    "I think missed out the crucial "const" on the first line of the second example! It's always the way."

    and

    "I prefer
    const int *cp = malloc(sizeof *cp);"

    Which of those two comments did you misinterpret as "let also not forget
    to tell the OP how to write code"?
    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Tue Jan 14 13:06:21 2025
    From Newsgroup: comp.lang.c

    On 1/14/25 08:50, David Brown wrote:
    ...
    I'm not sure if that beats being seriously accused of being possessed by
    the devil (as I once was - I am sure you can guess who by).

    The oldest message I could find using Google Groups that contained
    "David Brown", "possessed" and "devil" was a message posted by you on 2015-11-01 saying that Rick Hodgins had repeatedly told you that you
    were possessed by the devil. It didn't show me any messages in which he actually made that claim. I don't doubt you, but I'm curious about the
    context in which he said that. Can you identify one such message?

    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.lang.c on Tue Jan 14 18:53:20 2025
    From Newsgroup: comp.lang.c

    In article <vm692t$2gnpi$1@dont-email.me>,
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 1/14/25 08:50, David Brown wrote:
    ...
    I'm not sure if that beats being seriously accused of being possessed by
    the devil (as I once was - I am sure you can guess who by).

    The oldest message I could find using Google Groups that contained
    "David Brown", "possessed" and "devil" was a message posted by you on >2015-11-01 saying that Rick Hodgins had repeatedly told you that you
    were possessed by the devil. It didn't show me any messages in which he >actually made that claim. I don't doubt you, but I'm curious about the >context in which he said that. Can you identify one such message?

    Why are we bringing up that gibberish? But if you insist on
    looking into that guy's drivel: https://groups.google.com/g/comp.arch/c/hXPciRKcjZU/m/pB2wdHmmCQAJ

    Hint: another name for "the devil" is "satan".

    - Dan C.

    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Tue Jan 14 20:31:23 2025
    From Newsgroup: comp.lang.c

    On 14/01/2025 19:06, James Kuyper wrote:
    On 1/14/25 08:50, David Brown wrote:
    ...
    I'm not sure if that beats being seriously accused of being possessed by
    the devil (as I once was - I am sure you can guess who by).

    The oldest message I could find using Google Groups that contained
    "David Brown", "possessed" and "devil" was a message posted by you on 2015-11-01 saying that Rick Hodgins had repeatedly told you that you
    were possessed by the devil. It didn't show me any messages in which he actually made that claim. I don't doubt you, but I'm curious about the context in which he said that. Can you identify one such message?


    I hadn't intended to go into detail about that - it was a throw-away
    comment that I thought Bart might find amusing as it could bring back
    memories of some quite imaginative attacks. If you don't remember the
    poster in question and some of the discussions back then, then I am
    certainly not going to regurgitate them now.

    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Tue Jan 14 12:38:23 2025
    From Newsgroup: comp.lang.c

    Julio Di Egidio <julio@diegidio.name> writes:
    [...]
    Your blindness and insistence is yet another a piece of stupid and
    vile shit indeed, not just "irrelevant" or off mark, you other stupid
    piece of nazi-retarded shit.
    [...]

    *plonk*
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Tue Jan 14 14:05:08 2025
    From Newsgroup: comp.lang.c

    On 1/14/2025 5:50 AM, David Brown wrote:
    On 14/01/2025 13:44, bart wrote:
    On 14/01/2025 08:02, Julio Di Egidio wrote:

    Stupid ignorant deadly and especially vile pieces of shit, the
    colonists...

    Eat my socks and die.

    *Plonk*

    I think we just found the mysterious troll you were talking about.


    I'm not sure if that beats being seriously accused of being possessed by
    the devil (as I once was - I am sure you can guess who by).

    The hard core religious guy? Iirc, Rick?



    But at
    least that case took a long time to build up - this character went from
    "you who have been kind, polite, professional, to the point, helpful" to
    a foaming-at-the-mouth meltdown in just one post.

    Ah well.  I hope he finds some help somewhere for whatever his problem
    is - he is certainly now unlikely to find much help on C around here!



    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Tue Jan 14 17:20:53 2025
    From Newsgroup: comp.lang.c

    On 1/14/25 14:31, David Brown wrote:
    On 14/01/2025 19:06, James Kuyper wrote:
    ...
    The oldest message I could find using Google Groups that contained
    "David Brown", "possessed" and "devil" was a message posted by you on
    2015-11-01 saying that Rick Hodgins had repeatedly told you that you
    were possessed by the devil. It didn't show me any messages in which he
    actually made that claim. I don't doubt you, but I'm curious about the
    context in which he said that. Can you identify one such message?


    I hadn't intended to go into detail about that - it was a throw-away
    comment that I thought Bart might find amusing as it could bring back memories of some quite imaginative attacks. If you don't remember the
    poster in question and some of the discussions back then, then I am
    certainly not going to regurgitate them now.

    I certainly remember him, which is why I could easily believe that he
    might accuse you of being possessed by the devil. However, I don't
    remember him actually doing so. Since he spent a lot of time in my
    killfile, that's not exactly unlikely.


    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Tue Jan 14 14:29:22 2025
    From Newsgroup: comp.lang.c

    On 1/14/2025 2:05 PM, Chris M. Thomasson wrote:
    On 1/14/2025 5:50 AM, David Brown wrote:
    On 14/01/2025 13:44, bart wrote:
    On 14/01/2025 08:02, Julio Di Egidio wrote:

    Stupid ignorant deadly and especially vile pieces of shit, the
    colonists...

    Eat my socks and die.

    *Plonk*

    I think we just found the mysterious troll you were talking about.


    I'm not sure if that beats being seriously accused of being possessed
    by the devil (as I once was - I am sure you can guess who by).

    The hard core religious guy? Iirc, Rick?

    Prematurely posted it without reading all responses. sorry about that.
    It had to be Rick.





    But at least that case took a long time to build up - this character
    went from "you who have been kind, polite, professional, to the point,
    helpful" to a foaming-at-the-mouth meltdown in just one post.

    Ah well.  I hope he finds some help somewhere for whatever his problem
    is - he is certainly now unlikely to find much help on C around here!




    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Wed Jan 15 02:48:35 2025
    From Newsgroup: comp.lang.c

    On 14/01/2025 14:50, David Brown wrote:
    On 14/01/2025 13:44, bart wrote:
    On 14/01/2025 08:02, Julio Di Egidio wrote:

    Stupid ignorant deadly and especially vile pieces of shit, the
    colonists...

    Eat my socks and die.

    *Plonk*

    I think we just found the mysterious troll you were talking about.

    I'm not sure if that beats being seriously accused of being possessed by
    the devil (as I once was - I am sure you can guess who by).  But at
    least that case took a long time to build up - this character went from
    "you who have been kind, polite, professional, to the point, helpful" to
    a foaming-at-the-mouth meltdown in just one post.

    Ah well.  I hope he finds some help somewhere for whatever his problem
    is - he is certainly now unlikely to find much help on C around here!

    You must mean some even more nazi-retarded lurid whit, don't you...

    You ungrateful, impolite, not just fraudulent moron, I was trying to be
    nice: do you think those smilies to you since the very beginning were
    per chance? And do you really think I am gonna use C99? Because you
    said so?? And so competently??? LOL, I should have known better: not
    to reply neither to you nor to anybody going even vaguely near that

    PIECE OF polluting and fraudulent shit BEN BACARISSE

    and his gang of retarded and fraudulent polluters of all ponds, of
    course. But what can I do, I am a nice guy: I always give people a chance.

    But, back to us, YOU piece of shit: the ONE crucial bottom question I
    had for you, you have not even tried to approach: on a sub-thread that
    YOU had started by the way, not me: as if I give a shit what C standard
    people suggest on the Internet, on Usenet of all things, in 2025 or
    whether "what is the problem with malloc"? Really?? YOU must be
    fucking out of your mind.

    You, your friends, and the fucking fraudulent monsters: 10 mistakes
    every 5 lines of code I call you. It's just fucking EMBARRASSING: and
    that's your legacy, you polluters of all ponds: that, climate change,
    and the global shithole for everybody that is covering it all. Indeed
    not me, your kids and grand-kids are gonna thank you so much.

    But really, do you think I give a shit about "having conversations on
    Usenet"? About C? With people who cannot even read English?? You
    demented pieces of abusive crap: indeed, I rather hope the innocents
    meanwhile are having a fucking good laugh.

    (Sure, you keep looking...)

    *Plonk* (morons)

    -Julio

    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Wed Jan 15 02:51:02 2025
    From Newsgroup: comp.lang.c

    On 15/01/2025 02:48, Julio Di Egidio wrote:
    On 14/01/2025 14:50, David Brown wrote:
    On 14/01/2025 13:44, bart wrote:
    On 14/01/2025 08:02, Julio Di Egidio wrote:

    Stupid ignorant deadly and especially vile pieces of shit, the
    colonists...

    Eat my socks and die.

    *Plonk*

    I think we just found the mysterious troll you were talking about.

    I'm not sure if that beats being seriously accused of being possessed
    by the devil (as I once was - I am sure you can guess who by).  But at
    least that case took a long time to build up - this character went
    from "you who have been kind, polite, professional, to the point,
    helpful" to a foaming-at-the-mouth meltdown in just one post.

    Ah well.  I hope he finds some help somewhere for whatever his problem
    is - he is certainly now unlikely to find much help on C around here!

    You must mean some even more nazi-retarded lurid whit, don't you...

    Correction: I mean shit.

    -Julio

    You ungrateful, impolite, not just fraudulent moron, I was trying to be nice: do you think those smilies to you since the very beginning were
    per chance?  And do you really think I am gonna use C99?  Because you
    said so??  And so competently???  LOL, I should have known better: not
    to reply neither to you nor to anybody going even vaguely near that

      PIECE OF polluting and fraudulent shit BEN BACARISSE

    and his gang of retarded and fraudulent polluters of all ponds, of
    course.  But what can I do, I am a nice guy: I always give people a chance.

    But, back to us, YOU piece of shit: the ONE crucial bottom question I
    had for you, you have not even tried to approach: on a sub-thread that
    YOU had started by the way, not me: as if I give a shit what C standard people suggest on the Internet, on Usenet of all things, in 2025 or
    whether "what is the problem with malloc"?  Really??  YOU must be
    fucking out of your mind.

    You, your friends, and the fucking fraudulent monsters: 10 mistakes
    every 5 lines of code I call you.  It's just fucking EMBARRASSING: and that's your legacy, you polluters of all ponds: that, climate change,
    and the global shithole for everybody that is covering it all.  Indeed
    not me, your kids and grand-kids are gonna thank you so much.

    But really, do you think I give a shit about "having conversations on Usenet"?  About C?  With people who cannot even read English??  You demented pieces of abusive crap: indeed, I rather hope the innocents meanwhile are having a fucking good laugh.

    (Sure, you keep looking...)

    *Plonk*  (morons)



    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Lawrence D'Oliveiro@ldo@nz.invalid to comp.lang.c on Wed Jan 15 02:09:11 2025
    From Newsgroup: comp.lang.c

    So ... the form of expressions allowed with “const” ... do we call them “const-able”?

    Seems this term could have unfortunate implications if used carelessly ... wonder how you would police such a thing ...
    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Tue Jan 14 21:15:41 2025
    From Newsgroup: comp.lang.c

    On 1/14/25 17:20, James Kuyper wrote:
    ...
    I certainly remember him, which is why I could easily believe that he
    might accuse you of being possessed by the devil. However, I don't
    remember him actually doing so. Since he spent a lot of time in my
    killfile, that's not exactly unlikely.

    I had considerable trouble finding a message that I think is the basis
    for what you said. On 2014-11-11, Rick C. Hodgin responded to one of
    your comments by referring to "cold, hate-filled evil spirits speaking
    through your mouth.". He didn't mention the Devil, or Satan (though he
    referred to Satan in many places in that same message), or possession. I
    think he meant only that a devil (as opposed to The Devil) had inspired
    you to say those things, and NOT that you were being possessed by that
    devil.

    It's a matter of responsibility: when a devil tempts you to say things
    you shouldn't, it's you're responsibility for giving into temptation. If
    a devil possesses you, everything you do while under possession is that
    devil's responsibility, not yours.

    I think you know this, but for the sake of others less familiar with me:
    I'm an atheist, and believe none of this; I'm just describing what I
    think he believes. And I think that the things you said that triggered
    that comment were completely innocuous.
    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Kaz Kylheku@643-408-1753@kylheku.com to comp.lang.c on Wed Jan 15 03:12:14 2025
    From Newsgroup: comp.lang.c

    On 2025-01-15, Julio Di Egidio <julio@diegidio.name> wrote:
    moron, I was trying to be nice:

    I.e. you're not nice, but you try (good for you); but this is what
    happens when it gets too much and you need a break from all that trying.
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Wed Jan 15 04:19:51 2025
    From Newsgroup: comp.lang.c

    On 15/01/2025 04:12, Kaz Kylheku wrote:
    On 2025-01-15, Julio Di Egidio <julio@diegidio.name> wrote:
    moron, I was trying to be nice:

    I.e. you're not nice, but you try (good for you); but this is what
    happens when it gets too much and you need a break from all that trying.

    Are you missing your share? You did help, and I said that much. I have
    not even commented on the article that brought me here to begin with.
    Is that not enough?

    -Julio

    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Wed Jan 15 04:30:34 2025
    From Newsgroup: comp.lang.c

    On 15/01/2025 04:19, Julio Di Egidio wrote:
    On 15/01/2025 04:12, Kaz Kylheku wrote:
    On 2025-01-15, Julio Di Egidio <julio@diegidio.name> wrote:
    moron, I was trying to be nice:

    I.e. you're not nice, but you try (good for you); but this is what
    happens when it gets too much and you need a break from all that trying.

    Are you missing your share?  You did help, and I said that much.  I have not even commented on the article that brought me here to begin with. Is that not enough?

    But you are right, I am a liar: I didn't need any help, I should have
    opened the language reference, I was lazy. But I was also curious,
    what's happening in comp.lang.c, C being one of the 3 languages
    everybody should learn, of all things... My mistake indeed.

    No worries, unless I'll read some more personal insults, I am just out
    of here. Have fun.

    -Julio

    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Wed Jan 15 09:29:54 2025
    From Newsgroup: comp.lang.c

    On 15/01/2025 03:15, James Kuyper wrote:
    On 1/14/25 17:20, James Kuyper wrote:
    ...
    I certainly remember him, which is why I could easily believe that he
    might accuse you of being possessed by the devil. However, I don't
    remember him actually doing so. Since he spent a lot of time in my
    killfile, that's not exactly unlikely.

    I had considerable trouble finding a message that I think is the basis
    for what you said.

    James, I am sure you mean well, but I really don't think we need to drag
    this up here. I mentioned that old accusation as a humorous comparison
    of absurd are over-the-top insults that I have seen over the years in
    Usenet. If I had imagined for a moment that someone would take it as a serious task to dig through archives and expand on it here in one of the
    rare topical threads in comp.lang.c, I would not have mentioned it.

    Of course I am not the slightest bit bothered by either that old
    accusation or Julio's current ones. And in both cases I do not think I
    wrote things that justified such reactions. (I /have/ written things in
    the past that have justified angry reactions - I am not always as polite
    or considerate as I should be.)

    So please forget that I ever brought that up, and drop this line of
    posting here on Usenet, for the sake of everyone else. If you want to
    discuss it more, you know my email address is valid.


    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Wed Jan 15 09:39:10 2025
    From Newsgroup: comp.lang.c

    On 15/01/2025 03:09, Lawrence D'Oliveiro wrote:
    So ... the form of expressions allowed with “const” ... do we call them “const-able”?

    Seems this term could have unfortunate implications if used carelessly ... wonder how you would police such a thing ...

    I think we could merge in some ideas from INTERCAL and get a modern and
    very safe programming language full of "PLEASE const-able" expressions!

    :-)

    <https://en.wikipedia.org/wiki/INTERCAL>

    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Wed Jan 15 13:35:31 2025
    From Newsgroup: comp.lang.c

    On 15/01/2025 09:29, David Brown wrote:
    On 15/01/2025 03:15, James Kuyper wrote:
    On 1/14/25 17:20, James Kuyper wrote:
    ...
    I certainly remember him, which is why I could easily believe that he
    might accuse you of being possessed by the devil. However, I don't
    remember him actually doing so. Since he spent a lot of time in my
    killfile, that's not exactly unlikely.

    I had considerable trouble finding a message that I think is the basis
    for what you said.

    James, I am sure you mean well, but I really don't think we need to drag this up here.  I mentioned that old accusation as a humorous comparison
    of absurd are over-the-top insults that I have seen over the years in Usenet.  If I had imagined for a moment that someone would take it as a serious task to dig through archives and expand on it here in one of the rare topical threads in comp.lang.c, I would not have mentioned it.

    Of course I am not the slightest bit bothered by either that old
    accusation or Julio's current ones.  And in both cases I do not think I wrote things that justified such reactions.  (I /have/ written things in the past that have justified angry reactions - I am not always as polite
    or considerate as I should be.)

    So please forget that I ever brought that up, and drop this line of
    posting here on Usenet, for the sake of everyone else.  If you want to discuss it more, you know my email address is valid.

    You PIECE OF SHIT, you were pumped by Ben Becarisse, but what's happened
    here eventually was mostly *your* doing.

    I won't forget this one either, you piece of shit.

    -Julio


    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Wed Jan 15 13:47:29 2025
    From Newsgroup: comp.lang.c

    On 13/01/2025 21:45, David Brown wrote:
    On 13/01/2025 20:30, Julio Di Egidio wrote:
    On 13/01/2025 17:30, David Brown wrote:
    On 13/01/2025 14:23, Julio Di Egidio wrote:
    On 13/01/2025 09:58, David Brown wrote:
    On 13/01/2025 04:10, James Kuyper wrote:
    On 1/9/25 21:51, Julio Di Egidio wrote:

    You can of course also argue that it is best to have the code
    ordered according to the normal flow of operations - error handling >>>>> in the middle of the normal code flow can make it hard to follow
    the algorithm of a function.  (C++ exceptions are an extreme
    version of this.)  Some people might prefer a compromise:

    Some people are just wrong and not even honest at that.

       **Please don't mind and don't feed the trolls**:

    There are a few trollish characters in this newsgroup, but I don't
    believe I have seen any in this thread.
    <snipped>

    Then, with all due respect, you are being naive: "let's also not
    forget to tell the OP how to write code" is a piece of it, for example.

    Could you point to the post from which you are quoting?

    FUCK YOU and the whole nazi-retarded gang, you piece of ungodly shit.

    -Julio

    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Wed Jan 15 13:51:08 2025
    From Newsgroup: comp.lang.c

    On 13/01/2025 17:30, David Brown wrote:
    On 13/01/2025 14:23, Julio Di Egidio wrote:
    On 13/01/2025 09:58, David Brown wrote:
    On 13/01/2025 04:10, James Kuyper wrote:
    On 1/9/25 21:51, Julio Di Egidio wrote:

    You can of course also argue that it is best to have the code ordered
    according to the normal flow of operations - error handling in the
    middle of the normal code flow can make it hard to follow the
    algorithm of a function.  (C++ exceptions are an extreme version of
    this.)  Some people might prefer a compromise:

    Some people are just wrong and not even honest at that.

       **Please don't mind and don't feed the trolls**:

    There are a few trollish characters in this newsgroup, but I don't

    Who?? You piece of NAZI-RETARDED SHIT.

    Now I must find out what life-saving products you nazi-retarded piece of
    shit actually put on the market.

    -Julio

    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Wed Jan 15 13:52:23 2025
    From Newsgroup: comp.lang.c

    On 15/01/2025 09:39, David Brown wrote:
    On 15/01/2025 03:09, Lawrence D'Oliveiro wrote:
    So ... the form of expressions allowed with “const” ... do we call them >> “const-able”?

    Seems this term could have unfortunate implications if used carelessly
    ...
    wonder how you would police such a thing ...

    I think we could merge in some ideas from INTERCAL and get a modern and
    very safe programming language full of "PLEASE const-able" expressions!

    :-)

    <https://en.wikipedia.org/wiki/INTERCAL>


    STICK IT UP YOUR ASS you fucking nazi retard.

    -Julio

    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Wed Jan 15 13:53:22 2025
    From Newsgroup: comp.lang.c

    On 08/01/2025 13:25, David Brown wrote:
    On 08/01/2025 12:25, Ben Bacarisse wrote:
    David Brown <david.brown@hesbynett.no> writes:

    Suppose you have :

        int v = 123;        // Non-const object definition
        const int * cp = &v;    // Const pointer to non-const data
        int * p = (int *) cp;    // Cast to non-const pointer
        *p = 456;        // Change the target data

    This is allowed, because the original object definition was not a const
    definition.

    However, with this:

        int v = 123;        // Const object definition

    Correction:
        const int v = 123;

        const int * cp = &v;    // Const pointer to const data
        int * p = (int *) cp;    // Cast to non-const pointer
        *p = 456;        // Undefined behaviour

    I think missed out the crucial "const" on the first line of the second
    example!  It's always the way.

    Fortunately, Usenet is a self-correcting medium :-)  Thanks for pointing out that mistake, and I hope the OP sees your correction before getting confused by my copy-pasta error.

    LOOK AT THESE TWO PIECES OF NAZI-RETARDED SPAMMING SHIT.

    -Julio

    You can make the pointer to non-const, but trying to change an object
    that
    was /defined/ as const is undefined behaviour (even if it was not
    placed in
    read-only memory).

    When you use dynamic memory, however, you are not defining an object
    in the
    same way.  If you write :

        const int * cp = malloc(sizeof(int));

    I prefer

         const int *cp = malloc(sizeof *cp);


    That's a common preference.  Personally, I prefer the former - I think
    it makes it clearer that we are allocating space for an int.  Hopefully
    the OP will hang around this group and we'll get a chance to give advice
    and suggestions on many different aspects of C programming.

    you are defining the object "p" as a pointer to type "const int" -
    but you
    are not defining a const int.  You can cast "cp" to "int *" and use that >>> new pointer to change the value.



    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Wed Jan 15 13:54:07 2025
    From Newsgroup: comp.lang.c

    On 09/01/2025 02:09, Ben Bacarisse wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT;

    pT = malloc(sizeof(AvlTree_t));

    if (!pT) {
    return NULL;
    }

    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;

    return pT;
    }

    Just on a side issue, I prefer to make tests like this positive so I'd
    write:

    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT = malloc(*pT);

    if (pT) {
    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;
    }
    return pT;
    }

    I'm not going to "make a case" for this (though I will if you want!) --
    I just think it helps to see lots of different styles.


    I prefer you eat shit and die. Indeed, keep going...

    -Julio

    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Wed Jan 15 13:56:12 2025
    From Newsgroup: comp.lang.c

    On 09/01/2025 10:35, David Brown wrote:
    On 09/01/2025 05:24, Kaz Kylheku wrote:
    On 2025-01-09, Ben Bacarisse <ben@bsb.me.uk> wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    static AvlTree_t const *AvlTree_node(
         void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
         AvlTree_t *pT;

         pT = malloc(sizeof(AvlTree_t));

         if (!pT) {
             return NULL;
         }

         pT->pk = pk;
         pT->pL = pL;
         pT->pR = pR;

         return pT;
    }

    Just on a side issue, I prefer to make tests like this positive so I'd
    write:

      static AvlTree_t const *AvlTree_node(
          void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
      ) {
          AvlTree_t *pT = malloc(*pT);
          if (pT) {
              pT->pk = pk;
              pT->pL = pL;
              pT->pR = pR;
          }
          return pT;
      }

    More generally:

        foo_handle *foo = foo_create();
        bar_handle *bar = foo ? bar_create(foo) : 0; // doesn't like null
        xyzzy_handle *xyz = xyzzy_create(42, bar, arg);
        container *con = malloc(sizeof *con);

        if (foo && bar && xyz && con) {
          // happy case: we have all three resources

          con->foo = foo;
          con->bar = bar;
          con->xyz = xyz;

          return con;
        }

        xyzzy_destroy(xyz);
        xyzzy_destroy(bar);
        if (foo)
           xyzzy_destroy(foo); // stupidly doesn't like null

        return 0;

    I'm not going to "make a case" for this (though I will if you want!) --
    I just think it helps to see lots of different styles.

    I might just have made the case. When more resources need to be
    acquired that might fail, it consolidates the happy case under one
    conjunctive test, and consolidates the cleanup in the unhappy case.
    Effectively it's almost if we have only two cases.

    A minor disadvantage is that in the unhappy flow, we may allocate
    resources past the point where it is obvious they are not going to be
    needed: if foo_create() failed, we are pointlessly calling
    xyzzy_create() and malloc for the container. It's possible that these
    succeed, and we are just going to turn around and free them.


    How about taking the idea slightly further and making the later
    allocations conditional too?

       foo_handle *foo = foo_create();
       bar_handle *bar = foo ? bar_create(foo) : 0; // doesn't like null
       xyzzy_handle *xyz = bar ? xyzzy_create(42, bar, arg) : 0;
       container *con = xyz ? malloc(sizeof *con) : 0;

       if (con) {
         // happy case: we have all three resources

       ...

    If you are going to use that style (and I not arguing for or against
    it), go all in!


    It's a form of consolidated error checking, like when we make
    several system calls and check them for errors as a batch;
    e.g. call fprintf several times and check for disk full (etc)
    just once.

    HOW about you and that piece of nazi-retarded shit rather go fuck
    yourself somewhere else? Eh?? Yeah, that would be it: I am having a
    great idea: YOU GO fuck yourself out of cpomp.lang.cv !! I am a fucking genius...

    -Julio

    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Wed Jan 15 13:57:39 2025
    From Newsgroup: comp.lang.c

    On 10/01/2025 00:23, Ben Bacarisse wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    On 09/01/2025 02:09, Ben Bacarisse wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT;

    pT = malloc(sizeof(AvlTree_t));

    if (!pT) {
    return NULL;
    }

    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;

    return pT;
    }
    Just on a side issue, I prefer to make tests like this positive so I'd
    write:
    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT = malloc(*pT);
    if (pT) {
    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;
    }
    return pT;
    }
    I'm not going to "make a case" for this (though I will if you want!) --
    I just think it helps to see lots of different styles.

    That is *more* error prone,

    I would be happy for you to expand on why you say that.

    I would be happy for you to eat shit and die.

    all the more so if it's not a 5 liner...

    Except that you cannot, so you must be a piece of nazi-retarded
    fraudulent vile shit.

    -Julio

    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Wed Jan 15 14:01:57 2025
    From Newsgroup: comp.lang.c

    On 13/01/2025 09:58, David Brown wrote:
    On 13/01/2025 04:10, James Kuyper wrote:
    On 1/9/25 21:51, Julio Di Egidio wrote:
    On 10/01/2025 03:14, Julio Di Egidio wrote:
    ...
    Julio Di Egidio <julio@diegidio.name> writes:

    On 09/01/2025 02:09, Ben Bacarisse wrote:

    Julio Di Egidio <julio@diegidio.name> writes:
    ...
       static AvlTree_t const *AvlTree_node(
       void const *pk, AvlTree_t const *pL, AvlTree_t const *pR >>>>>>>>>>    ) {
       AvlTree_t *pT = malloc(*pT);
       if (pT) {
       pT->pk = pk;
       pT->pL = pL;
       pT->pR = pR;
       }
       return pT;
       }
    I'm not going to "make a case" for this (though I will if you >>>>>>>>>> want!) -- I just think it helps to see lots of different styles. >>>>>>>>>
    That is *more* error prone,
    ...
    ... check the return value as soon as the function returns a possibly
    null pointer or an error value is certainly more widely applicable,
    and quite less error prone, especially if it's

    I meant: immediately check the return value and bail out if needed.
    The other approach does not even simplify on the clean-up, by the way...

    The code you're criticizing as more error prone does check the return
    value as soon as the function returns, and bails out if needed. It just
    bails out through the missing else clause rather than from the if-clause.

    It does requires code to be indented farther than the other approach. I
    have avoided writing code like that for that reason, particularly when
    there's a lot of code inside the it statement's controlled blocks -but
    not because it's error prone.

    That's of course correct, what else.

    I'm wary of assuming a particular interpretation of what someone else
    wrote, but I would say there is a valid argument for say that the second style of code is more "error prone".

    Because you really are a piece of nazi-retarded shit. Indeed always self-indulgent, always self-apologetic, and always in group: you stupid nazi-retarded fraudulent polluters of all ponds and nothing else.

    Different styles have different pros and cons, and different balances of emphasis.  (They can also have different efficiencies in the normal and error cases, but that's usually less important.)

    Fuck you and the fraudulent bullshi8t that floods every single fucking
    public channels at this point.

    -Julio

    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Wed Jan 15 14:06:59 2025
    From Newsgroup: comp.lang.c

    On 15/01/2025 14:01, Julio Di Egidio wrote:
    On 13/01/2025 09:58, David Brown wrote:
    On 13/01/2025 04:10, James Kuyper wrote:

    It does requires code to be indented farther than the other approach. I
    have avoided writing code like that for that reason, particularly when
    there's a lot of code inside the it statement's controlled blocks -but
    not because it's error prone.

    That's of course correct, what else.

    Eh, I meant, you are partly right but you are wrong. Error prone means
    that the programmer is more easily going to make mistakes. And that is
    what I meant, as there isn't much else to say about a 5 liner if your interlocutor cannot even read and has never seen anything but 5 liners anyway...

    Never mind, what a fucking shithole.

    -Julio

    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Wed Jan 15 14:11:09 2025
    From Newsgroup: comp.lang.c

    On 10/01/2025 02:43, Tim Rentsch wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    On 10/01/2025 00:37, Julio Di Egidio wrote:

    On 10/01/2025 00:23, Ben Bacarisse wrote:

    Julio Di Egidio <julio@diegidio.name> writes:

    On 09/01/2025 02:09, Ben Bacarisse wrote:

    Julio Di Egidio <julio@diegidio.name> writes:

    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT;

    pT = malloc(sizeof(AvlTree_t));

    if (!pT) {
    return NULL;
    }

    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;

    return pT;
    }

    Just on a side issue, I prefer to make tests like this positive
    so I'd write:
    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT = malloc(*pT);
    if (pT) {
    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;
    }
    return pT;
    }
    I'm not going to "make a case" for this (though I will if you
    want!) -- I just think it helps to see lots of different styles.

    That is *more* error prone,

    I would be happy for you to expand on why you say that.

    all the more so if it's not a 5 liner...

    There is no such thing as expanding 40 years of professional
    experience in software engineering and programming and doing it
    properly since day one: just think about that code and what I said
    for what it's worth, in particular I haven't mentioned 5 liners by
    chance, things are quite more complicated not in vitro.

    And please do not hold a grudge about that: it's not me who was
    trying to say how to write code... ;)

    BTW, I hadn't mention it, but have you noticed the second one is
    misindented? Between me and you, I can tell how long a piece of
    code will take to break when in production by just looking at
    it... A lot of fun. :)

    The indentation was correct in Ben's original posting.

    The misindentation first appeared in your followup to that
    posting, where the quoted portion had been changed to remove a
    blank line and over-indent the if().

    You other blithering idiot: Ben's initial code had *a missed blank
    line*, and that was the benningg of my pointing out what a stupid fuck
    thgat was: and the long run for that. You guys just cannot even read
    but you did manage to get the bottom of it, a pile of lurid shit...

    BTW, I do am having troubles finding some of Ben Bacarisse's initial
    pieces of trolling shit: there is indeed something strange going on
    around here.,.

    -Julio

    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Wed Jan 15 14:15:53 2025
    From Newsgroup: comp.lang.c

    On 08/01/2025 16:16, Ben Bacarisse wrote:
    Julio Di Egidio <julio@diegidio.name> writes:


    Overall, I am surmising this and only this might go write-protected:

    MyStruct_t const T = {...};

    Yes, though you should extend your concern beyond what might be

    You piece of fraudulent, incompetent, then always nazi-retarded shit,
    now you get it? No, since you incompetent piece of shit indeed don't
    even know half of it. I just hope you aren't teaching, you piece of
    ungodly shit.

    Ah, and I was just quoting from an answer I had got, you piece of nazi-retarded ungodly fraudulent nazi-retarded shit...

    -Julio

    write-protected. Modifying an object whose type is const qualified is > undefined, even if the object is in writable storage. A compiler may
    assume that such an object has not changed because in a program that has undefined behaviour, all bets are off. For example, under gcc with
    almost any optimisation this program prints 42:

    #include <stdio.h>

    void f(const int *ip)
    {
    *(int *)ip = 0;
    }

    int main(void)
    {
    const int a = 42;
    f(&a);
    printf("%d\n", a);
    }

    While this one allocates a "byte-array", i.e. irrespective of how the
    pointer we are assigning it is declared:

    MyStruct_t const *pT = malloc(...);

    Is my understanding (to that point) correct?

    Technically you get an object with no effective type. David's reply
    included some references to find out more about the effective type of an object, but it is safe to say that these only come into play if you are messing about with the way you access the allocated storage (for example accessing it as a MyStruct but then later as a floating point object).

    More relevant to a discussion of const is to ask what you plan to do
    with pT since you can't (without a cast) assign any useful value to the allocated object.

    It is generally better to use a non const-qualified pointer for the allocation but, when using the pointer, to pass it to functions that use
    the right type depending on whether they modify the pointed-to object or
    not. For example:

    MyStack *sp = malloc(*sp);
    ...
    stack_push(sp, 99);
    ...
    if (stack_empty(sp)) ...
    ...
    stack_free(sp);

    we would have

    void stack_push(MyStack *sp, int v) { ... }
    bool stack_empty(MyStack const *sp) { ... }
    void stack_free(MyStack *sp) { ... }


    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Wed Jan 15 14:17:10 2025
    From Newsgroup: comp.lang.c

    On 09/01/2025 02:09, Ben Bacarisse wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT;

    pT = malloc(sizeof(AvlTree_t));

    if (!pT) {
    return NULL;
    }

    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;

    return pT;
    }

    Just on a side issue, I prefer to make tests like this positive so I'd
    write:

    Just on a side issue, I prefer you go eat shit and die.

    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT = malloc(*pT);

    if (pT) {
    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;
    }
    return pT;
    }

    I'm not going to "make a case" for this (though I will if you want!) --
    I just think it helps to see lots of different styles.


    No, you won't, your pal David Satan Brown is gonna do it for you...

    -Julio

    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Wed Jan 15 14:22:56 2025
    From Newsgroup: comp.lang.c

    On 13/01/2025 04:10, James Kuyper wrote:
    On 1/9/25 21:51, Julio Di Egidio wrote:
    On 10/01/2025 03:14, Julio Di Egidio wrote:
    ...
    Julio Di Egidio <julio@diegidio.name> writes:

    On 09/01/2025 02:09, Ben Bacarisse wrote:

    Julio Di Egidio <julio@diegidio.name> writes:
    ...
      static AvlTree_t const *AvlTree_node(
      void const *pk, AvlTree_t const *pL, AvlTree_t const *pR >>>>>>>>>   ) {
      AvlTree_t *pT = malloc(*pT);
      if (pT) {
      pT->pk = pk;
      pT->pL = pL;
      pT->pR = pR;
      }
      return pT;
      }
    I'm not going to "make a case" for this (though I will if you >>>>>>>>> want!) -- I just think it helps to see lots of different styles. >>>>>>>>
    That is *more* error prone,
    ...
    ... check the return value as soon as the function returns a possibly
    null pointer or an error value is certainly more widely applicable,
    and quite less error prone, especially if it's

    I meant: immediately check the return value and bail out if needed.
    The other approach does not even simplify on the clean-up, by the way...

    The code you're criticizing as more error prone does check the return
    value as soon as the function returns, and bails out if needed.It just
    bails out through the missing else clause rather than from the if-clause.

    LOL, the convincing arguments. Tell that to your customer when things
    start going south: "it is formally equivalent"!

    It does requires code to be indented farther than the other approach. I

    Sure, one level for each checked call, and that's still just the tip of
    that iceberg: I mean, are you serious or just you too sell bullshit for
    a living? Not even my worst enemy I would bullshit that way.

    have avoided writing code like that for that reason, particularly when there's a lot of code inside the it statement's controlled blocks -but
    not because it's error prone.

    It's called error prone when it's easy for the programmers to trip on
    it. Get a real education for a change.

    -Julio

    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Wed Jan 15 14:46:55 2025
    From Newsgroup: comp.lang.c

    On 15/01/2025 13:53, Julio Di Egidio wrote:
    On 08/01/2025 13:25, David Brown wrote:
    On 08/01/2025 12:25, Ben Bacarisse wrote:
    David Brown <david.brown@hesbynett.no> writes:

    Suppose you have :

        int v = 123;        // Non-const object definition
        const int * cp = &v;    // Const pointer to non-const data
        int * p = (int *) cp;    // Cast to non-const pointer
        *p = 456;        // Change the target data

    This is allowed, because the original object definition was not a const >>>> definition.

    However, with this:

        int v = 123;        // Const object definition

    Correction:
         const int v = 123;

        const int * cp = &v;    // Const pointer to const data
        int * p = (int *) cp;    // Cast to non-const pointer
        *p = 456;        // Undefined behaviour

    I think missed out the crucial "const" on the first line of the second
    example!  It's always the way.

    Fortunately, Usenet is a self-correcting medium :-)  Thanks for
    pointing out that mistake, and I hope the OP sees your correction
    before getting confused by my copy-pasta error.

    LOOK AT THESE TWO PIECES OF NAZI-RETARDED SPAMMING SHIT.

    Indeed keep eating mud.

    -Julio

    You can make the pointer to non-const, but trying to change an
    object that
    was /defined/ as const is undefined behaviour (even if it was not
    placed in
    read-only memory).

    When you use dynamic memory, however, you are not defining an object
    in the
    same way.  If you write :

        const int * cp = malloc(sizeof(int));

    I prefer

         const int *cp = malloc(sizeof *cp);


    That's a common preference.  Personally, I prefer the former - I think
    it makes it clearer that we are allocating space for an int.
    Hopefully the OP will hang around this group and we'll get a chance to
    give advice and suggestions on many different aspects of C programming.

    you are defining the object "p" as a pointer to type "const int" -
    but you
    are not defining a const int.  You can cast "cp" to "int *" and use
    that
    new pointer to change the value.




    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From gazelle@gazelle@shell.xmission.com (Kenny McCormack) to comp.lang.c on Wed Jan 15 14:05:36 2025
    From Newsgroup: comp.lang.c

    In article <87zfjt8728.fsf@nosuchdomain.example.com>,
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    Julio Di Egidio <julio@diegidio.name> writes:
    [...]
    Your blindness and insistence is yet another a piece of stupid and
    vile shit indeed, not just "irrelevant" or off mark, you other stupid
    piece of nazi-retarded shit.
    [...]

    *plonk*

    Sounds like he's got your number, Keithy.

    Pro-tip: Never announce that you are plonking someone (just do it); it is
    bad form, and shows that the guy has gotten under your skin.
    --
    Marshall: 10/22/51
    Jessica: 4/4/79
    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Wed Jan 15 15:18:16 2025
    From Newsgroup: comp.lang.c

    On 15/01/2025 15:05, Kenny McCormack wrote:
    In article <87zfjt8728.fsf@nosuchdomain.example.com>,
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    Julio Di Egidio <julio@diegidio.name> writes:
    [...]
    Your blindness and insistence is yet another a piece of stupid and
    vile shit indeed, not just "irrelevant" or off mark, you other stupid
    piece of nazi-retarded shit.
    [...]

    *plonk*

    Sounds like he's got your number, Keithy.

    Pro-tip: Never announce that you are plonking someone (just do it); it is
    bad form, and shows that the guy has gotten under your skin.

    Mangling quotes is another pro tip?

    Anyway, I am just forwarding it to the Punisher, I am only here to have
    a good look at the fraudulent then lurid shit by the/some? comp.lang.c regulars.

    -Julio

    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Kaz Kylheku@643-408-1753@kylheku.com to comp.lang.c on Wed Jan 15 20:03:47 2025
    From Newsgroup: comp.lang.c

    On 2025-01-15, Julio Di Egidio <julio@diegidio.name> wrote:
    Could you point to the post from which you are quoting?

    What's your ETA on this bizarre meltdown coming to an end?
    Days? Hopefully not weeks!

    Bacarisse and Brown are among the most level-headed people who
    have ever been regulars here, and to my recollection have never resorted
    to making anyone feel small, let alone direct insult.

    Odd choice of targets.

    We have discussed ways of coding this and that here for years now; it's
    never intended as "Imbecile, thou shalt code every situation like this
    using the following pattern, which is overhwelmingly superior in every conceivable variant of such a situation."

    Anyway, in professional settings, organizations do code reviews. You may
    be an ultra senior with forty years of coding experience, but you will
    still get comments about something being doable in a nicer way and
    graciously act on them.
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Kaz Kylheku@643-408-1753@kylheku.com to comp.lang.c on Wed Jan 15 20:16:14 2025
    From Newsgroup: comp.lang.c

    On 2025-01-15, Julio Di Egidio <julio@diegidio.name> wrote:
    Anyway, I am just forwarding it to the Punisher, I am only here to have
    a good look at the fraudulent then lurid shit by the/some? comp.lang.c regulars.

    Well, now you have seen that you can say almost whatever the hell you
    want on Usenet, and you will not be banned. At worst, individuals might
    filter you out with their private kill files (which can be evaded with
    a tiny effort).

    If this were Redddit, Hackernews, Mastodon, Facebook, you would be gone,
    right? And that's not necessarily a good thing; I probably speak for
    nearly all of us here when I say we like Usenet the way it is.

    Here you would have to abuse the actual network (e.g. with massive
    spamming) in order to bring on a network-level action against you.

    Now that you have felt what it's like to wield this mighty Usenet
    superpower, do you think you can you put it back into its box?

    You can always open the box at some future time, when humanity is
    grappling with a strange, alien adversary that happens to turn into a
    puddle of grease when told to "eat shit and die". You will be a hero!
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Wed Jan 15 13:46:25 2025
    From Newsgroup: comp.lang.c

    On 1/15/2025 4:52 AM, Julio Di Egidio wrote:
    On 15/01/2025 09:39, David Brown wrote:
    On 15/01/2025 03:09, Lawrence D'Oliveiro wrote:
    So ... the form of expressions allowed with “const” ... do we call them >>> “const-able”?

    Seems this term could have unfortunate implications if used
    carelessly ...
    wonder how you would police such a thing ...

    I think we could merge in some ideas from INTERCAL and get a modern
    and very safe programming language full of "PLEASE const-able"
    expressions!

    :-)

    <https://en.wikipedia.org/wiki/INTERCAL>


    STICK IT UP YOUR ASS you fucking nazi retard.

    Afaict, the ass is exit only?

    Wow, you got pissed off really bad!

    ;^o

    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Wed Jan 15 13:51:43 2025
    From Newsgroup: comp.lang.c

    On 1/15/2025 5:17 AM, Julio Di Egidio wrote:
    On 09/01/2025 02:09, Ben Bacarisse wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    static AvlTree_t const *AvlTree_node(
         void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
         AvlTree_t *pT;

         pT = malloc(sizeof(AvlTree_t));

         if (!pT) {
             return NULL;
         }

         pT->pk = pk;
         pT->pL = pL;
         pT->pR = pR;

         return pT;
    }

    Just on a side issue, I prefer to make tests like this positive so I'd
    write:

    Just on a side issue, I prefer you go eat shit and die.

      static AvlTree_t const *AvlTree_node(
          void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
      ) {
          AvlTree_t *pT = malloc(*pT);
          if (pT) {
              pT->pk = pk;
              pT->pL = pL;
              pT->pR = pR;
          }
          return pT;
      }

    I'm not going to "make a case" for this (though I will if you want!) --
    I just think it helps to see lots of different styles.


    No, you won't, your pal David Satan Brown is gonna do it for you...

    I don't think David is the actual devil? ;^o

    God damn! You got really pissed off!

    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Sat Jan 18 11:36:35 2025
    From Newsgroup: comp.lang.c

    Julio Di Egidio <julio@diegidio.name> writes:

    On 10/01/2025 02:43, Tim Rentsch wrote:

    Julio Di Egidio <julio@diegidio.name> writes:

    On 10/01/2025 00:37, Julio Di Egidio wrote:

    On 10/01/2025 00:23, Ben Bacarisse wrote:

    Julio Di Egidio <julio@diegidio.name> writes:

    On 09/01/2025 02:09, Ben Bacarisse wrote:

    Julio Di Egidio <julio@diegidio.name> writes:

    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT;

    pT = malloc(sizeof(AvlTree_t));

    if (!pT) {
    return NULL;
    }

    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;

    return pT;
    }

    Just on a side issue, I prefer to make tests like this
    positive so I'd write:
    static AvlTree_t const *AvlTree_node(
    void const *pk, AvlTree_t const *pL, AvlTree_t const *pR
    ) {
    AvlTree_t *pT = malloc(*pT);
    if (pT) {
    pT->pk = pk;
    pT->pL = pL;
    pT->pR = pR;
    }
    return pT;
    }
    I'm not going to "make a case" for this (though I will if you
    want!) -- I just think it helps to see lots of different
    styles.

    That is *more* error prone,

    I would be happy for you to expand on why you say that.

    all the more so if it's not a 5 liner...

    There is no such thing as expanding 40 years of professional
    experience in software engineering and programming and doing it
    properly since day one: just think about that code and what I
    said for what it's worth, in particular I haven't mentioned 5
    liners by chance, things are quite more complicated not in vitro.

    And please do not hold a grudge about that: it's not me who was
    trying to say how to write code... ;)

    BTW, I hadn't mention it, but have you noticed the second one is
    misindented? Between me and you, I can tell how long a piece of
    code will take to break when in production by just looking at
    it... A lot of fun. :)

    The indentation was correct in Ben's original posting.

    The misindentation first appeared in your followup to that
    posting, where the quoted portion had been changed to remove a
    blank line and over-indent the if().

    You other blithering idiot: Ben's initial code had *a missed blank
    line*, [...]

    I was responding only to the issue of misindentation.
    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Julio Di Egidio@julio@diegidio.name to comp.lang.c on Sun Jan 19 14:42:15 2025
    From Newsgroup: comp.lang.c

    On 18/01/2025 20:36, Tim Rentsch wrote:
    Julio Di Egidio <julio@diegidio.name> writes:
    On 10/01/2025 02:43, Tim Rentsch wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    BTW, I hadn't mention it, but have you noticed the second one is
    misindented? Between me and you, I can tell how long a piece of
    code will take to break when in production by just looking at
    it... A lot of fun. :)

    The indentation was correct in Ben's original posting.

    The misindentation first appeared in your followup to that
    posting, where the quoted portion had been changed to remove a
    blank line and over-indent the if().

    You other blithering idiot: Ben's initial code had *a missed blank
    line*, [...]

    I was responding only to the issue of misindentation.

    Still rewriting history, you piece of shit? You and your fraudulent
    pals have indeed been talking RETARDED CRAP all the way. -- Now snip it again...

    Stupid fucking nazi-retarded pieces of ungodly shit. Eat shit and die.

    -Julio

    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Sun Jan 19 16:03:45 2025
    From Newsgroup: comp.lang.c

    On 1/19/2025 5:42 AM, Julio Di Egidio wrote:
    On 18/01/2025 20:36, Tim Rentsch wrote:
    Julio Di Egidio <julio@diegidio.name> writes:
    On 10/01/2025 02:43, Tim Rentsch wrote:
    Julio Di Egidio <julio@diegidio.name> writes:

    BTW, I hadn't mention it, but have you noticed the second one is
    misindented?  Between me and you, I can tell how long a piece of
    code will take to break when in production by just looking at
    it...  A lot of fun.  :)

    The indentation was correct in Ben's original posting.

    The misindentation first appeared in your followup to that
    posting, where the quoted portion had been changed to remove a
    blank line and over-indent the if().

    You other blithering idiot:  Ben's initial code had *a missed blank
    line*, [...]

    I was responding only to the issue of misindentation.

    Still rewriting history, you piece of shit?  You and your fraudulent
    pals have indeed been talking RETARDED CRAP all the way. -- Now snip it again...

    Stupid fucking nazi-retarded pieces of ungodly shit.  Eat shit and die.

    Why do you go to the nazi comparisons? It's a bit odd to me...
    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Lawrence D'Oliveiro@ldo@nz.invalid to comp.lang.c on Sun Jan 26 06:32:56 2025
    From Newsgroup: comp.lang.c

    Something you will never see in Turkish-written code:

    const int anople = 1;
    --- Synchronet 3.20c-Linux NewsLink 1.2