• switch/extension for see below strongly needed

    From fir@profesor.fir@gmail.com to comp.lang.c on Sun May 17 00:03:40 2026
    From Newsgroup: comp.lang.c

    the fact that in c a language/compiler sees only functions or variables
    that are up in code is a disaster

    it is a disaster becouse it dont alow you to split code on N files
    each file has realted functions and variables and not to care on the
    global order of it

    you should not care becouse it has no sense those separate files are
    then like in parralel dimensions and that is good (they will not go in conflict as it had to have unique names anyway)

    the thing that yu need to care is some kind of bad annoying design flaw
    as those historic goto abuse or other like that

    So the solution is give at least compiler extension that would allow you
    to have it changed that it see up and down

    the fact thet this switch is not present is another flaw..so you have
    two flaws here
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Sun May 17 01:08:32 2026
    From Newsgroup: comp.lang.c

    On 16/05/2026 23:03, fir wrote:
    the fact that in c a language/compiler sees only functions or variables
    that are up in code is a disaster

    it is a disaster becouse it dont alow you to split code on N files
    each file has realted functions and variables and not to care on the
    global order of it


    I mentioned something like this a week ago, suggesting that in C it was
    harder work than necessary to split one source file up into two or more.

    However, some of the regulars here seemed to think it was a non-problem:

    Keith Thompson:
    If you have something to say about splitting a C translation unit
    (something I don't think I've ever had a need to do), perhaps because
    you've had difficulties doing so yourself, feel free to elaborate.

    Scott Lurndal:
    I don't recall refactoring existing code, primarily because the
    original programmers used multiple translation units logically
    dividing the code into functionly related segments, where necessary,
    from the start.

    Tim Rentch:
    Having said that, I don't remember it ever being a big deal. If
    some source file needs to be subdivided, you simply subdivide it
    and move on.

    Somebody never had to do it; somebody else said they get the perfect
    split right from the start; and yet another said it was not a big deal.

    So nobody is that bothered (or people are just conditioned to oppose
    anything I say).

    you should not care becouse it has no sense those separate files are
    then like in parralel dimensions and that is good (they will not go in conflict as it had to have unique names anyway)

    the thing that yu need to care is some kind of bad annoying design flaw
    as those historic goto abuse or other like that

    So the solution is give at least compiler extension that would allow you
    to have it changed that it see up and down

    the fact thet this switch is not present is another flaw..so you have
    two flaws here

    The context a week ago was that a module scheme would make refactoring
    across files simpler. But you still need to manage visibility across the
    new set of files.

    Adding such a scheme to C would be a huge change.

    However, doing away with forward references for functions within one
    file, so that no local prototypes are needed, is doable and would be a significant convenience.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Sat May 16 18:21:18 2026
    From Newsgroup: comp.lang.c

    Bart <bc@freeuk.com> writes:
    On 16/05/2026 23:03, fir wrote:
    the fact that in c a language/compiler sees only functions or
    variables that are up in code is a disaster
    it is a disaster becouse it dont alow you to split code on N files
    each file has realted functions and variables and not to care on the
    global order of it

    I mentioned something like this a week ago, suggesting that in C it
    was harder work than necessary to split one source file up into two or
    more.

    And you offered no evidence for your claim, not even telling us
    that you had tried it and found it difficult.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Sun May 17 05:50:11 2026
    From Newsgroup: comp.lang.c

    On Sun, 17 May 2026 00:03:40 +0200, fir wrote:

    the fact that in c a language/compiler sees only functions or
    variables that are up in code is a disaster

    Back in those days, languages that needed multipass compilers (e.g.
    Algol 68) were considered complicated and expensive to implement.
    That’s why C went for a single-pass language design, like Pascal. And
    like Pascal, it has forward declarations to mitigate this somewhat.

    You need some kind of use-before-define facility in any realistic
    language, if you want to allow recursion, and in particular mutual
    recursion.

    It’s amusing to think that C++, that behemoth that, in terms of sheer complexity, leaves old-style monsters like Algol 68 or PL/I in the
    dust, is still essentially a single-pass language design.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Sun May 17 11:26:31 2026
    From Newsgroup: comp.lang.c

    On 17/05/2026 06:50, Lawrence D’Oliveiro wrote:
    On Sun, 17 May 2026 00:03:40 +0200, fir wrote:

    the fact that in c a language/compiler sees only functions or
    variables that are up in code is a disaster

    Back in those days, languages that needed multipass compilers (e.g.
    Algol 68) were considered complicated and expensive to implement.
    That’s why C went for a single-pass language design, like Pascal. And
    like Pascal, it has forward declarations to mitigate this somewhat.

    You need some kind of use-before-define facility in any realistic
    language, if you want to allow recursion, and in particular mutual
    recursion.

    It’s amusing to think that C++, that behemoth that, in terms of sheer complexity, leaves old-style monsters like Algol 68 or PL/I in the
    dust, is still essentially a single-pass language design.

    The way it works in Python is peculiar. This fails for example:

    def F():
    G()

    F()

    def G():
    print("G")

    But it works if that F() call is moved to the end, even though G is
    defined after F.

    This is because 'def' is an executable statement, so executing 'def G'
    before doing F() is sufficient to get G into the global symbol table.

    All languages I develop have out-of-order definitions so this stuff is
    never a problem.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Sun May 17 12:16:14 2026
    From Newsgroup: comp.lang.c

    On 17/05/2026 02:21, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:
    On 16/05/2026 23:03, fir wrote:
    the fact that in c a language/compiler sees only functions or
    variables that are up in code is a disaster
    it is a disaster becouse it dont alow you to split code on N files
    each file has realted functions and variables and not to care on the
    global order of it

    I mentioned something like this a week ago, suggesting that in C it
    was harder work than necessary to split one source file up into two or
    more.

    And you offered no evidence for your claim, not even telling us
    that you had tried it and found it difficult.

    Everyone here will know what is involved. But nobody wants to admit that
    it can be onerous.

    Here is a very simple example of one 'module', which involves two source files, 'a.h' and 'a.c':

    ====================
    //a.h:

    T C();
    T D();

    ====================
    // a.c:

    #include a.h

    static T A();
    static T B();
    static T E();
    static T F();

    T A(){}
    T B(){}
    T C(){}
    T D(){}
    T E(){}
    T F(){}
    ====================

    In this file, let's say that each function can call any other, so that
    those forward declarations are needed. Two functions are also exported
    to other 'source files'.

    Suppose now I want to split 'a.c' into two files, a.c and b.c, exactly
    in the middle so that A B C stay in a.c, and D E F go in b.c. You now
    have to shuffle things around like this, ending up with 4 source files:

    ====================
    //a.h:

    T A();
    T B();
    T C();

    ====================
    //a.c:
    #include a.h
    #include b.h

    T A(){}
    T B(){}
    T C(){}

    ====================
    //b.h:
    T D()
    T E()
    T F()

    ====================
    //b.c:

    #include a.h
    #include b.h

    T D(){}
    T E(){}
    T F(){}
    ====================

    I've kept it simple by having only functions, and ignoring variables,
    types, enumerations, macros and #includes (maybe some functions need
    that include, but not all).

    Other files that previously included 'a.h', /may/ now also need 'b.h'
    (if they call D, E or F). Or maybe they can drop 'a.h' if they don't
    call A, B or C).

    There may also be new global name clashes to sort out, say if A() was
    already exported from elsewhere.

    So, it's a bit messy. In my language with its module scheme, the
    equivalent 'a' module might be:

    ====================
    #a.m:

    func A:T = end # (real code needs a suitable return value)
    func B:T = end
    global func C:T = end
    global func D:T = end
    func E:T = end
    func F:T = end
    ====================

    After the same split, you get these two files instead of one:

    ====================
    #a.m:
    global func A:T = end
    global func B:T = end
    global func C:T = end

    ====================
    #b.m:
    global func D:T = end
    global func E:T = end
    global func F:T = end
    ====================

    The lead module needs this line added to project info: "module b".

    There could also be clashes here with an existing A() for example, which
    can either be renamed, or namespace qualifiers used (needed at all call-sites); or the two new modules can form their own private group.

    (In that case, C and D need 'export' scope to be visible from outside as before.)

    In this languages, variables, types, enumerations and macro are handled
    with the exact same mechanism.

    So, yes, I believe a decent module scheme means stuff like this is less
    work than in C.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From fir@profesor.fir@gmail.com to comp.lang.c on Sun May 17 14:57:54 2026
    From Newsgroup: comp.lang.c

    Bart pisze:
    On 16/05/2026 23:03, fir wrote:
    the fact that in c a language/compiler sees only functions or
    variables that are up in code is a disaster

    it is a disaster becouse it dont alow you to split code on N files
    each file has realted functions and variables and not to care on the
    global order of it


    I mentioned something like this a week ago, suggesting that in C it was harder work than necessary to split one source file up into two or more.



    ye i remember we afair both agree on that point even few years ago (that
    it is bed - the fact you need to bother if it is up makes DEPENDENCY
    more worse it is a CURSED DEPENDENCY

    in this post above hovever i mentioned slightly other thing - that not
    only this is bad (which was talken already) but that there is real need
    for compiler extension/switch - i know it would violate standard c, but
    there is a need a switch that violates c imo - for practicel reasons of
    get rid of this annoyance
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From fir@profesor.fir@gmail.com to comp.lang.c on Sun May 17 15:04:55 2026
    From Newsgroup: comp.lang.c

    Bart pisze:
    static T A();
    static T B();
    static T E();
    static T F();

    T A(){}
    T B(){}
    T C(){}
    T D(){}

    imo the variables (call if file variables or block of code variables)
    makes probably more problem than functions

    good way of design is imo to have few functions and variables/arrays who
    work on this together - like in small c file...in another file you make another set of functions and variables...bad design is to split those
    variables out of its functions, and today i would need move the
    variables like up (enforcing BAD DESIGN) of make extern declarations for visibility (enforcing even more BAD DESIGN imo) it also involves CODE
    JUMPING which is BAD

    solution - add this f**kin switch to compiler...what i said is
    mathematical (logical) proof its bad design
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From fir@profesor.fir@gmail.com to comp.lang.c on Sun May 17 15:08:38 2026
    From Newsgroup: comp.lang.c

    fir pisze:
    Bart pisze:
    static T A();
    static T B();
    static T E();
    static T F();

    T A(){}
    T B(){}
    T C(){}
    T D(){}

    imo the variables (call if file variables or block of code variables)
    makes probably more problem than functions

    good way of design is imo to have few functions and variables/arrays who work on this together - like in small c file...in another file you make another set of functions and variables...bad design is to split those variables out of its functions, and today i would need move the
    variables like up (enforcing BAD DESIGN) of make extern declarations for visibility (enforcing even more BAD DESIGN imo) it also involves CODE JUMPING which is BAD

    solution - add this f**kin switch to compiler...what i said is
    mathematical (logical) proof its bad design

    facing such proof its totally not important if
    Keith Thompson, Scott Lurndal, Tim Rentch have different opinion, a
    proof is a proof

    and either youre burdened with bad design or you at leas make compiler
    switch to take it out your back/neck :/
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Sun May 17 06:48:06 2026
    From Newsgroup: comp.lang.c

    Bart <bc@freeuk.com> writes:

    On 17/05/2026 02:21, Keith Thompson wrote:

    Bart <bc@freeuk.com> writes:

    On 16/05/2026 23:03, fir wrote:

    the fact that in c a language/compiler sees only functions or
    variables that are up in code is a disaster
    it is a disaster becouse it dont alow you to split code on N files
    each file has realted functions and variables and not to care on the
    global order of it

    I mentioned something like this a week ago, suggesting that in C it
    was harder work than necessary to split one source file up into two or
    more.

    And you offered no evidence for your claim, not even telling us
    that you had tried it and found it difficult.

    Everyone here will know what is involved. But nobody wants to admit
    that it can be onerous.

    It could be onerous. The point is, in actual practice it almost
    never is onerous.

    Here is a very simple example [...]

    The example is not evidence but a strawman argument. It just
    doesn't match the experience of actual practice of other C
    developers (speaking for myself, and other developers I have
    known personally, and the comments of other newsgroup folks who
    have participated in the conversation).
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From fir@profesor.fir@gmail.com to comp.lang.c on Sun May 17 16:06:34 2026
    From Newsgroup: comp.lang.c

    fir pisze:
    Bart pisze:
    On 16/05/2026 23:03, fir wrote:
    the fact that in c a language/compiler sees only functions or
    variables that are up in code is a disaster

    it is a disaster becouse it dont alow you to split code on N files
    each file has realted functions and variables and not to care on the
    global order of it


    I mentioned something like this a week ago, suggesting that in C it
    was harder work than necessary to split one source file up into two or
    more.



    ye i remember we afair both agree on that point even few years ago (that
    it is bed - the fact you need to bother if it is up makes DEPENDENCY
    more worse it is a CURSED DEPENDENCY

    in this post above hovever i  mentioned slightly other thing - that not only this is bad (which was talken already) but that there is real need
    for compiler extension/switch - i know it would violate standard c, but there is a need a switch that violates c imo - for practicel reasons of
    get rid of this annoyance

    note such dependencies are terrible becouse clen code is with minimal
    clan dependencies (minimally needed) and also minimal code jumping


    other important flaw is lack of what i call (not sure if proper)
    aspect programming

    if yu write a module/pice of code you need somewhat to integrate it with system..amd very often this integration is to add call to it in proper
    place or two

    it is annoying that you must travel there and add it it would be much
    much more good if you could add this locally in the pice you write


    its like

    MainLoop()
    [
    //some things

    .. <- you need to add function all here
    }

    so in language you need to define this point


    MainLoop()
    [
    //some things

    #bots
    }

    update_duck() @ bots
    {

    }

    something like that though how it should look like exactly i dont know yet

    i mention it here becouse there are some resemblances of bad design -
    you need to have it to have good design imo (some culd say that having a
    point where you dont see exlicitely what is called here is bad but imo
    its worth and needed)
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Lew Pitcher@lew.pitcher@digitalfreehold.ca to comp.lang.c on Sun May 17 14:43:28 2026
    From Newsgroup: comp.lang.c

    On Sun, 17 May 2026 06:48:06 -0700, Tim Rentsch wrote:

    Bart <bc@freeuk.com> writes:

    On 17/05/2026 02:21, Keith Thompson wrote:

    Bart <bc@freeuk.com> writes:

    On 16/05/2026 23:03, fir wrote:

    the fact that in c a language/compiler sees only functions or
    variables that are up in code is a disaster
    it is a disaster becouse it dont alow you to split code on N files
    each file has realted functions and variables and not to care on the >>>>> global order of it

    fir, "You are doing it wrong".

    I mentioned something like this a week ago, suggesting that in C it
    was harder work than necessary to split one source file up into two or >>>> more.

    Bart, in reality, a smart developer almost never has to "split one source
    file up into two or more". Instead, they /plan/ for isolation and encapsulation of the functional parts of their code, and /intentionally/ develop
    multiple source files from the start. That's the way professionals do it.

    You, for instance, might write the parser, calling external functions
    to generate output code. For your purposes, those external functions
    can be debugging stubs in a separate source or object module.
    Fir, OTOH, might write the code generator functions, with a simple
    debugging driver as a separate source or object module. Once both of
    you have tested your parts to success, you can combine your two works,
    with fir supplying the code generator and you the parser, to create a
    single compiler program. None of this has to be "onerous". The hard
    part is agreeing on the contract between your code and fir's code, and
    that's part of what a professional does /before/ (s)he starts coding.

    And you offered no evidence for your claim, not even telling us
    that you had tried it and found it difficult.

    Everyone here will know what is involved. But nobody wants to admit
    that it can be onerous.

    It could be onerous. The point is, in actual practice it almost
    never is onerous.

    Here is a very simple example [...]

    The example is not evidence but a strawman argument. It just
    doesn't match the experience of actual practice of other C
    developers (speaking for myself, and other developers I have
    known personally, and the comments of other newsgroup folks who
    have participated in the conversation).

    Agreed. Bart and fir have a "special" view of coding, which is
    at odds with my 30+ years experience in the profession (and
    my 20+ years of post-professional (amateur) programming.
    --
    Lew Pitcher
    "In Skills We Trust"
    Not LLM output - I'm just like this.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Sun May 17 17:31:38 2026
    From Newsgroup: comp.lang.c

    Am 17.05.2026 um 07:50 schrieb Lawrence D’Oliveiro:

    It’s amusing to think that C++, that behemoth that, in terms of sheer complexity, leaves old-style monsters like Algol 68 or PL/I in the
    dust, is still essentially a single-pass language design.

    Not absolutelty true because of this f.e.:

    struct Class
    {
    int sum() { return a + b; }
    int a, b;
    }

    This requires the compiler to do multiple passes.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Sun May 17 16:53:56 2026
    From Newsgroup: comp.lang.c

    On 17/05/2026 15:43, Lew Pitcher wrote:
    On Sun, 17 May 2026 06:48:06 -0700, Tim Rentsch wrote:

    Bart <bc@freeuk.com> writes:

    On 17/05/2026 02:21, Keith Thompson wrote:

    Bart <bc@freeuk.com> writes:

    On 16/05/2026 23:03, fir wrote:

    the fact that in c a language/compiler sees only functions or
    variables that are up in code is a disaster
    it is a disaster becouse it dont alow you to split code on N files >>>>>> each file has realted functions and variables and not to care on the >>>>>> global order of it

    fir, "You are doing it wrong".

    I mentioned something like this a week ago, suggesting that in C it
    was harder work than necessary to split one source file up into two or >>>>> more.

    Bart, in reality, a smart developer almost never has to "split one source file up into two or more". Instead, they /plan/ for isolation and encapsulation
    of the functional parts of their code, and /intentionally/ develop
    multiple source files from the start. That's the way professionals do it.

    You, for instance, might write the parser, calling external functions
    to generate output code. For your purposes, those external functions
    can be debugging stubs in a separate source or object module.
    Fir, OTOH, might write the code generator functions, with a simple
    debugging driver as a separate source or object module. Once both of
    you have tested your parts to success, you can combine your two works,
    with fir supplying the code generator and you the parser, to create a
    single compiler program. None of this has to be "onerous". The hard
    part is agreeing on the contract between your code and fir's code, and
    that's part of what a professional does /before/ (s)he starts coding.

    None of that is about taking one source file and splitting it, or taking multiple sources and combining them, which is where module support would
    help.

    Your example is simply about modular programming.


    And you offered no evidence for your claim, not even telling us
    that you had tried it and found it difficult.

    Everyone here will know what is involved. But nobody wants to admit
    that it can be onerous.

    It could be onerous. The point is, in actual practice it almost
    never is onerous.

    Here is a very simple example [...]

    The example is not evidence but a strawman argument. It just
    doesn't match the experience of actual practice of other C
    developers (speaking for myself, and other developers I have
    known personally, and the comments of other newsgroup folks who
    have participated in the conversation).

    Agreed. Bart and fir have a "special" view of coding, which is
    at odds with my 30+ years experience in the profession (and
    my 20+ years of post-professional (amateur) programming.

    I can't speak for fir. But it is very common in my work to start with a
    module that does a specific job, and find it gets too large. Or I find
    it is really two or more kinds of tasks, or task that can be segregated.
    Both may demand a split.

    It is also common to find some modules end up with not much in them and
    can combined with each or each incorporated into another.

    Yet another situation is where certain modules export functions for an
    API, and it is convenient to combine all such functions into the same
    module.

    Or there might be a set of helper functions across modules, which can be combined into one support module.

    Or, I want to port my app to another OS, and decide to collect
    OS-specific routines into a dedicated OS-specific module which is easier
    to swap with another.

    Or I want to have multiple configurations of my app, and I want to
    arrange it so that, switching configuration means swapping one
    self-contained module for another.

    There, you want common functions to stay within the main program (so no duplication).

    An actual example from two of the my projects, is where I have a
    compiler and an assembler, both generate EXEs and initially maintained
    their own backends. I wanted them to share backend modules, but this
    meant a lot of refactoring so that the assembler backend didn't see
    references to the compiler front end and vice versa.


    But I guess no amount of examples will cut any ice because your
    experience is different, and that's the one that counts?

    You always know right from the start of any project exactly what needs
    to go where.

    So, if your project is 50Kloc, say, split over 50 modules and 1000
    functions, you write all 50K lines, including all the functions, data
    types, global data, enumerations, imports, macros etc, before you even
    submit it to a compiler.

    Oh, you don't do that? So your projects can gradually evolve, converge
    and diverge, make 3 steps forward and two back, algorithms etc can
    change, just like everyone else's?

    But you still know the exact layout from the outset!

    Please accept that other people especially sole developers have
    different working practices.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From fir@profesor.fir@gmail.com to comp.lang.c on Sun May 17 18:24:37 2026
    From Newsgroup: comp.lang.c

    Lew Pitcher pisze:
    On Sun, 17 May 2026 06:48:06 -0700, Tim Rentsch wrote:

    Bart <bc@freeuk.com> writes:

    On 17/05/2026 02:21, Keith Thompson wrote:

    Bart <bc@freeuk.com> writes:

    On 16/05/2026 23:03, fir wrote:

    the fact that in c a language/compiler sees only functions or
    variables that are up in code is a disaster
    it is a disaster becouse it dont alow you to split code on N files >>>>>> each file has realted functions and variables and not to care on the >>>>>> global order of it

    fir, "You are doing it wrong".

    I mentioned something like this a week ago, suggesting that in C it
    was harder work than necessary to split one source file up into two or >>>>> more.

    Bart, in reality, a smart developer almost never has to "split one source file up into two or more". Instead, they /plan/ for isolation and encapsulation
    of the functional parts of their code, and /intentionally/ develop
    multiple source files from the start. That's the way professionals do it.

    You, for instance, might write the parser, calling external functions
    to generate output code. For your purposes, those external functions
    can be debugging stubs in a separate source or object module.
    Fir, OTOH, might write the code generator functions, with a simple
    debugging driver as a separate source or object module. Once both of
    you have tested your parts to success, you can combine your two works,
    with fir supplying the code generator and you the parser, to create a
    single compiler program. None of this has to be "onerous". The hard
    part is agreeing on the contract between your code and fir's code, and
    that's part of what a professional does /before/ (s)he starts coding.

    And you offered no evidence for your claim, not even telling us
    that you had tried it and found it difficult.

    Everyone here will know what is involved. But nobody wants to admit
    that it can be onerous.

    It could be onerous. The point is, in actual practice it almost
    never is onerous.

    Here is a very simple example [...]

    The example is not evidence but a strawman argument. It just
    doesn't match the experience of actual practice of other C
    developers (speaking for myself, and other developers I have
    known personally, and the comments of other newsgroup folks who
    have participated in the conversation).

    Agreed. Bart and fir have a "special" view of coding, which is
    at odds with my 30+ years experience in the profession (and
    my 20+ years of post-professional (amateur) programming.


    were talking here about pices of c code..name it c files for example

    say you have N of such pices - when i code my app i got the pices just
    as i sait ..one is for example setup_window.c another is timer.c another
    is blitter.c and so on

    each have set of functions and "global" variables related to
    them..mostly to them but some of them also may be accesed by other
    pices/files

    if you have such system that those functions in each pice see all other
    pieces up and down its ideal and proper situation becouse all thise
    files like orthogonal one to another and thus like separated only they
    see other by names


    adding a rigiud constrain that you must keep that files in artifical
    linear order from up to down ias absolute crazyy becuose

    1) naturally given order dont exist
    2) ist absolutly sill and tiresome to manage such stupid order


    its absolute FLAW and its a disaster

    bartc simplyhas a dose of intelligence to see it too (though such things
    some may see in different extent..with time im even more angry than
    before (when i was writing on this many years ago)

    c also has other flaws (also mentioned) - language should be designed
    such way not to make code jumping and unnecessary dependencies which
    kill codes making work on it more tiresome and stupid
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Sun May 17 17:56:03 2026
    From Newsgroup: comp.lang.c

    On 17/05/2026 17:24, fir wrote:
    Lew Pitcher pisze:

    Agreed. Bart and fir have a "special" view of coding, which is
    at odds with my 30+ years experience in the profession (and
    my 20+ years of post-professional (amateur) programming.


    were talking here about pices of c code..name it c files for example

    say you have N of such pices - when i code my app i got the pices just
    as i sait ..one is for example setup_window.c another is timer.c another
    is blitter.c and so on

    each have set of functions and "global" variables related to
    them..mostly to them but some of them also may be accesed by other pices/files

    if you have such system that those functions in each pice see all other pieces up and down its ideal and proper situation becouse all thise
    files like orthogonal one to another and thus like separated only they
    see other by names


    adding a rigiud constrain that you must keep that files in artifical
    linear order from up to down ias absolute crazyy becuose

    1) naturally given order dont exist
    2) ist absolutly sill and tiresome to manage such stupid order

    C does allow you to have them in arbitrary order.

    But it means writing and maintaining function prototypes at the top of
    the file. That is what's tiresome.


    its absolute FLAW and its a disaster

    bartc simplyhas a dose of intelligence to see it too (though such things some may see in different extent..with time im even more angry than
    before (when i was writing on this many years ago)

    c also has other flaws (also mentioned) - language should be designed
    such way not to make code jumping and unnecessary dependencies which
    kill codes making work on it more tiresome and stupid

    My personal language allows anything to be defined in any order,
    including types, variables, enums and macros, at module scope or inside
    a function.

    So if you wanted, you could define all local variables at the end of a function rather than at the top; in C syntax:

    void GF() {
    F(&x, &y, &z);
    ...
    int x, y, z;
    }

    This doesn't look that useful at first, but in a current project where I
    am generating code in that language, it is invaluable, as I don't know
    exactly how many tempory variables need to be defined until I'm done generating code for the body.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From fir@profesor.fir@gmail.com to comp.lang.c on Sun May 17 21:07:04 2026
    From Newsgroup: comp.lang.c

    Bart pisze:
    On 17/05/2026 17:24, fir wrote:
    Lew Pitcher pisze:

    Agreed. Bart and fir have a "special" view of coding, which is
    at odds with my 30+ years experience in the profession (and
    my 20+ years of post-professional (amateur) programming.


    were talking here about pices of c code..name it c files for example

    say you have N of such pices - when i code my app i got the pices just
    as i sait ..one is for example setup_window.c another is timer.c
    another is blitter.c and so on

    each have set of functions and "global" variables related to
    them..mostly to them but some of them also may be accesed by other
    pices/files

    if you have such system that those functions in each pice see all
    other pieces up and down its ideal and proper situation becouse all
    thise files like orthogonal one to another and thus like separated
    only they
    see other by names


    adding a rigiud constrain that you must keep that files in artifical
    linear order from up to down ias absolute crazyy becuose

    1) naturally given order dont exist
    2) ist absolutly sill and tiresome to manage such stupid order

    C does allow you to have them in arbitrary order.

    But it means writing and maintaining function prototypes at the top of
    the file. That is what's tiresome.


    i know it as you for sure know i know it

    if i wuld guess what is more nonsense keeping all in this up-down
    artificil order (and constant trouble of managing it) or the adding
    thos redundant not neede declaration (and this dependency)
    i dont know but probably those declarations are even worse

    1. you need to write them, move them up etc and this a lot of work
    2. it spoils the fact that then you dont know which file this variable naturally belong
    3. there could be differences between declaration and function and that
    makes error

    so
    I. artifical up - down order is hell
    II. artifical declarations are hell

    no need to say more imo, case closed i would say, some should make this flag.switch that i could turn it on in compiler and stop to worry with
    this up down shit


    its absolute FLAW and its a disaster

    bartc simplyhas a dose of intelligence to see it too (though such
    things some may see in different extent..with time im even more angry
    than before (when i was writing on this many years ago)

    c also has other flaws (also mentioned) - language should be designed
    such way not to make code jumping and unnecessary dependencies which
    kill codes making work on it more tiresome and stupid

    My personal language allows anything to be defined in any order,
    including types, variables, enums and macros, at module scope or inside
    a function.

    So if you wanted, you could define all local variables at the end of a function rather than at the top; in C syntax:

       void GF() {
           F(&x, &y, &z);
           ...
           int x, y, z;
       }

    This doesn't look that useful at first, but in a current project where I
    am generating code in that language, it is invaluable, as I don't know exactly how many tempory variables need to be defined until I'm done generating code for the body.



    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Sun May 17 23:30:22 2026
    From Newsgroup: comp.lang.c

    On Sun, 17 May 2026 14:43:28 -0000 (UTC), Lew Pitcher wrote:

    Bart, in reality, a smart developer almost never has to "split one
    source file up into two or more". Instead, they /plan/ for isolation
    and encapsulation of the functional parts of their code, and
    /intentionally/ develop multiple source files from the start. That's
    the way professionals do it.

    Meanwhile, back in the real world, programs evolve and need to adapt
    to changes in the problems they were written to solve.

    What starts out as one source file might later grow to two or more.
    For instance, I start out with one program for performing the main
    functions of my time-and-billing system, then later discover the need
    for additional utilities driven by the same config settings: so this necessitates breaking out some common routines into a new module that
    is common to all the executables.

    It’s called “refactoring”.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.lang.c on Mon May 18 01:22:02 2026
    From Newsgroup: comp.lang.c

    In article <10uc81u$1kd2r$1@dont-email.me>, Bart <bc@freeuk.com> wrote:
    On 17/05/2026 02:21, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:
    On 16/05/2026 23:03, fir wrote:
    the fact that in c a language/compiler sees only functions or
    variables that are up in code is a disaster
    it is a disaster becouse it dont alow you to split code on N files
    each file has realted functions and variables and not to care on the
    global order of it

    I mentioned something like this a week ago, suggesting that in C it
    was harder work than necessary to split one source file up into two or
    more.

    And you offered no evidence for your claim, not even telling us
    that you had tried it and found it difficult.

    Everyone here will know what is involved. But nobody wants to admit that
    it can be onerous.

    Here is a very simple example of one 'module', which involves two source >files, 'a.h' and 'a.c':

    [snip]

    So, yes, I believe a decent module scheme means stuff like this is less
    work than in C.

    The example is you have two files, a.h and a.c; a.c has (say) a
    bunch of `static`-qualified functions; obviously these are
    file-scope, but have internal linkage. They can call each
    other.

    Now you split the file into two, and these functions need
    external linkage and prototypes in a header file. Instead of
    just creating a new `.c` file, you've got to change some stuff
    in a header file (possibly newly created) as well.

    I'll admit: you have a valid point.

    Yes, this happens. I do it pretty frequently. It's a chore,
    though I don't think it's quite as bad as you are making it out
    to be; annoying, certainly, but probably not in my list of top
    10, 15, or 20 annoyances about C.

    But it is an annoyance, nonetheless.

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Mon May 18 08:56:55 2026
    From Newsgroup: comp.lang.c

    On 17/05/2026 18:56, Bart wrote:
    On 17/05/2026 17:24, fir wrote:
    Lew Pitcher pisze:

    Agreed. Bart and fir have a "special" view of coding, which is
    at odds with my 30+ years experience in the profession (and
    my 20+ years of post-professional (amateur) programming.


    were talking here about pices of c code..name it c files for example

    say you have N of such pices - when i code my app i got the pices just
    as i sait ..one is for example setup_window.c another is timer.c
    another is blitter.c and so on

    each have set of functions and "global" variables related to
    them..mostly to them but some of them also may be accesed by other
    pices/files

    if you have such system that those functions in each pice see all
    other pieces up and down its ideal and proper situation becouse all
    thise files like orthogonal one to another and thus like separated
    only they
    see other by names


    adding a rigiud constrain that you must keep that files in artifical
    linear order from up to down ias absolute crazyy becuose

    1) naturally given order dont exist
    2) ist absolutly sill and tiresome to manage such stupid order

    C does allow you to have them in arbitrary order.

    But it means writing and maintaining function prototypes at the top of
    the file. That is what's tiresome.


    /If/ you want to write your functions in an arbitrary order, then you
    need to declare your static functions before you use them. That is
    certainly true.

    But remember this is a personal preference for the way you want to write
    code - arbitrary order of declarations and definitions is /not/
    necessarily a good thing. I accept that you like it, and I appreciate
    that you are not alone in that. But other people have different
    preferences. I /like/ having a fixed order. I almost never declare
    static functions, and when programming in a language that allows
    arbitrary order (such as Python), I still order my code bottom-up. This
    means when you look at my code, if you want to know the definition of an identifier, you only need to look in one direction - upwards.

    I am not saying that this ordering is somehow universally or objectively better than arbitrary order. But I am saying that arbitrary order is
    not universally or objectively better in a programming language.
    Flexibility has its downsides, and there's a lot of personal opinion and preferences involved.

    But I agree that if you use a language that has a "declare before use"
    rule (as many languages do), and you want to write in an arbitrary
    order, then it will involve extra effort. Such effort may be annoying,
    but it is entirely self-imposed.



    its absolute FLAW and its a disaster

    bartc simplyhas a dose of intelligence to see it too (though such
    things some may see in different extent..with time im even more angry
    than before (when i was writing on this many years ago)

    c also has other flaws (also mentioned) - language should be designed
    such way not to make code jumping and unnecessary dependencies which
    kill codes making work on it more tiresome and stupid

    My personal language allows anything to be defined in any order,
    including types, variables, enums and macros, at module scope or inside
    a function.

    So if you wanted, you could define all local variables at the end of a function rather than at the top; in C syntax:

       void GF() {
           F(&x, &y, &z);
           ...
           int x, y, z;
       }

    This doesn't look that useful at first, but in a current project where I
    am generating code in that language, it is invaluable, as I don't know exactly how many tempory variables need to be defined until I'm done generating code for the body.


    It does not just look useless at first sight, it looks horrible. But I
    write my code myself, for the most part - generated code does not need
    to be as easily read and understood. (I can't imagine how this
    "feature" is useful for generating code - surely it would be negligible
    effort to build up your list of variables as you build up the generated statements, and output the whole function in one lump. You are no
    longer trying to fit this into a few KB of ram on a Z80.)

    Much better, IMHO, is to use a language that lets you mix declarations
    and statements as needed. I see declaring your local variables in a
    list at the top of a function - or, far worse, at the bottom - as
    archaic style.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Mon May 18 09:02:36 2026
    From Newsgroup: comp.lang.c

    On 18/05/2026 01:30, Lawrence D’Oliveiro wrote:
    On Sun, 17 May 2026 14:43:28 -0000 (UTC), Lew Pitcher wrote:

    Bart, in reality, a smart developer almost never has to "split one
    source file up into two or more". Instead, they /plan/ for isolation
    and encapsulation of the functional parts of their code, and
    /intentionally/ develop multiple source files from the start. That's
    the way professionals do it.

    Meanwhile, back in the real world, programs evolve and need to adapt
    to changes in the problems they were written to solve.

    What starts out as one source file might later grow to two or more.
    For instance, I start out with one program for performing the main
    functions of my time-and-billing system, then later discover the need
    for additional utilities driven by the same config settings: so this necessitates breaking out some common routines into a new module that
    is common to all the executables.

    It’s called “refactoring”.

    Yes, I think some people have been overstating their claims that they
    rarely or never split a source file into two or more parts. Certainly
    with good planing you reduce the likelihood of having to split code
    later, but it's rare that you start a project with a clear enough specification and that never changes during the lifetime of the code.

    But I also think Bart is wildly overstating his claims of how much of an effort it is. Usually it's just something you do, without needing much
    extra effort or risk - the thought and planning effort going in to how
    you are doing your re-structure is the time-consuming part, not the
    mechanical copy-and-paste or adding a couple of extern declarations to a
    new header.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Mon May 18 09:08:05 2026
    From Newsgroup: comp.lang.c

    On 17/05/2026 12:26, Bart wrote:
    On 17/05/2026 06:50, Lawrence D’Oliveiro wrote:
    On Sun, 17 May 2026 00:03:40 +0200, fir wrote:

    the fact that in c a language/compiler sees only functions or
    variables that are up in code is a disaster

    Back in those days, languages that needed multipass compilers (e.g.
    Algol 68) were considered complicated and expensive to implement.
    That’s why C went for a single-pass language design, like Pascal. And
    like Pascal, it has forward declarations to mitigate this somewhat.

    You need some kind of use-before-define facility in any realistic
    language, if you want to allow recursion, and in particular mutual
    recursion.

    It’s amusing to think that C++, that behemoth that, in terms of sheer
    complexity, leaves old-style monsters like Algol 68 or PL/I in the
    dust, is still essentially a single-pass language design.

    The way it works in Python is peculiar.

    No, the way Python works is quite simple. You declare things before you
    use them. You just have to understand what "using" something means in
    Python.

    This fails for example:

      def F():
          G()

    This declares "F", but does run the body of the code - so there is no
    lookup of "G".


      F()

    Now you are trying to run "F()", and now "G" is found missing.


      def G():
         print("G")

    But it works if that F() call is moved to the end, even though G is
    defined after F.


    Python is a "declare before use" language, like most imperative
    languages. It just has a different meaning of "use" from C.

    This is because 'def' is an executable statement, so executing 'def G' before doing F() is sufficient to get G into the global symbol table.

    All languages I develop have out-of-order definitions so this stuff is
    never a problem.


    It's not a problem.

    Your languages have oddities and unusual behaviour not commonly found in
    other languages. If you like them, fine - that's your choice. But that
    does not mean languages that do otherwise are a "problem".



    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Mon May 18 07:17:22 2026
    From Newsgroup: comp.lang.c

    On Sun, 17 May 2026 17:56:03 +0100, Bart wrote:

    C does allow you to have them in arbitrary order.

    But it means writing and maintaining function prototypes at the top
    of the file. That is what's tiresome.

    Niklaus Wirth created a successor to Pascal which removed this
    requirement. It was designed as a two-pass language, with declarations processed on the first pass and function/procedure bodies (statements)
    on the second pass. So no forward declarations necessary, and mutual
    recursion worked fine.

    It was called Modula-2.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Mon May 18 09:22:13 2026
    From Newsgroup: comp.lang.c

    On 2026-05-18 08:56, David Brown wrote:
    [...]

    Much better, IMHO, is to use a language that lets you mix declarations
    and statements as needed.

    Indeed. But not "mixing" as a value per se, but to keep declarations
    locally is a good thing, IMO.

    I see declaring your local variables in a
    list at the top of a function - or, far worse, at the bottom - as
    archaic style.

    Well, "archaic" expresses a time-related qualification. But even in
    earlier times we saw, depending on the actual programming language,
    both styles existing.

    Anyway we need forward declarations or other means (e.g. multi-pass)
    to make mutual recursions or circular data structures possible.

    Janis

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Mon May 18 09:38:04 2026
    From Newsgroup: comp.lang.c

    On 2026-05-18 09:02, David Brown wrote:
    On 18/05/2026 01:30, Lawrence D’Oliveiro wrote:
    [...]

    It’s called “refactoring”.

    Yes, I think some people have been overstating their claims that they
    rarely or never split a source file into two or more parts.  Certainly
    with good planing you reduce the likelihood of having to split code
    later, but it's rare that you start a project with a clear enough specification and that never changes during the lifetime of the code.

    In C++ I had usually written a class first in one source file, tested
    its basic function, and then separated the declaration in a header and
    the implementation in a separate implementation file. It was just for convenience, and no issue at all, or rather trivial, to separate the
    parts. That was no refactoring, though.

    In real refactoring projects I've done _complex transformation_ tasks,
    not just the mundane and trivial task of separating code across files.


    But I also think Bart is wildly overstating his claims of how much of an effort it is.  Usually it's just something you do, without needing much extra effort or risk - the thought and planning effort going in to how
    you are doing your re-structure is the time-consuming part, not the mechanical copy-and-paste or adding a couple of extern declarations to a
    new header.

    Indeed. - Actually I'm unsure whether he's making up these statements
    because he really has a mental problem and difficulties handling that,
    or whether he has some satisfaction in making up peculiar claims just
    for the sake of an argument.

    Janis

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From fir@profesor.fir@gmail.com to comp.lang.c on Mon May 18 10:00:26 2026
    From Newsgroup: comp.lang.c

    David Brown pisze:
    On 18/05/2026 01:30, Lawrence D’Oliveiro wrote:
    On Sun, 17 May 2026 14:43:28 -0000 (UTC), Lew Pitcher wrote:

    Bart, in reality, a smart developer almost never has to "split one
    source file up into two or more". Instead, they /plan/ for isolation
    and encapsulation of the functional parts of their code, and
    /intentionally/ develop multiple source files from the start. That's
    the way professionals do it.

    Meanwhile, back in the real world, programs evolve and need to adapt
    to changes in the problems they were written to solve.

    What starts out as one source file might later grow to two or more.
    For instance, I start out with one program for performing the main
    functions of my time-and-billing system, then later discover the need
    for additional utilities driven by the same config settings: so this
    necessitates breaking out some common routines into a new module that
    is common to all the executables.

    It’s called “refactoring”.

    Yes, I think some people have been overstating their claims that they
    rarely or never split a source file into two or more parts.  Certainly
    with good planing you reduce the likelihood of having to split code
    later, but it's rare that you start a project with a clear enough specification and that never changes during the lifetime of the code.

    But I also think Bart is wildly overstating his claims of how much of an effort it is.  Usually it's just something you do, without needing much extra effort or risk - the thought and planning effort going in to how
    you are doing your re-structure is the time-consuming part, not the mechanical copy-and-paste or adding a couple of extern declarations to a
    new header.


    some people here may live in 70-ties or 80-ties...

    the splitinc code on small obj files was afait viable/apt
    in 70 ties when compilations are probably thousnd times slower

    now in 2026 on my not much stron pc i complile 400kb .c code
    split in files (which is 20k lines and it was generated in 1-2 weeks
    of work thanx to ai) in less than a second...

    so reasons to split code on separate compilation units still may
    are but for a codes possibly at least of size of few MB of source
    - so very many cases there is a bit nonsense to split projects on
    separate compilation units

    so it may be seen that some here probably talk bulshit (if they talk it
    as im not much reading into what who states precisely)

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Mon May 18 10:35:36 2026
    From Newsgroup: comp.lang.c

    On 18/05/2026 09:22, Janis Papanagnou wrote:
    On 2026-05-18 08:56, David Brown wrote:
    [...]

    Much better, IMHO, is to use a language that lets you mix declarations
    and statements as needed.

    Indeed. But not "mixing" as a value per se, but to keep declarations
    locally is a good thing, IMO.


    Yes - it is not the mixing itself that is good, it is what it allows.
    You get to keep your scopes small, you don't need to declare variables
    until you have an initial value for them (who cares if reading an uninitialised variable is UB if you never have them!), and you can often declare your variables as const. That means it's easy to know what the variable holds because it is only set once, and never changed.

    I see declaring your local variables in a list at the top of a
    function - or, far worse, at the bottom - as archaic style.

    Well, "archaic" expresses a time-related qualification. But even in
    earlier times we saw, depending on the actual programming language,
    both styles existing.

    Sure. But in the C world, pre-C99 code is often written in a style with
    local variables all declared at the top of the function - after C99, it
    is common to declare them when you need them. So as a C programmer, I
    see declaring local variables in one clump together as archaic.

    (Of course there are situations where it makes sense to declare local variables without initial values.)


    Anyway we need forward declarations or other means (e.g. multi-pass)
    to make mutual recursions or circular data structures possible.


    Of course.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Mon May 18 10:41:09 2026
    From Newsgroup: comp.lang.c

    On 2026-05-18 10:35, David Brown wrote:

    [...]  But in the C world, pre-C99 code is often written in a style with local variables all declared at the top of the function - after C99, it
    is common to declare them when you need them.  So as a C programmer, I
    see declaring local variables in one clump together as archaic.

    Ah, I forgot, even though I've learned "C" from K&R. - As soon as
    it became possible I had switched to the local style; a no-brainer.

    Janis

    [...]

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Mon May 18 10:50:11 2026
    From Newsgroup: comp.lang.c

    On 18/05/2026 08:38, Janis Papanagnou wrote:
    On 2026-05-18 09:02, David Brown wrote:
    On 18/05/2026 01:30, Lawrence D’Oliveiro wrote:
    [...]

    It’s called “refactoring”.

    Yes, I think some people have been overstating their claims that they
    rarely or never split a source file into two or more parts.  Certainly
    with good planing you reduce the likelihood of having to split code
    later, but it's rare that you start a project with a clear enough
    specification and that never changes during the lifetime of the code.

    In C++ I had usually written a class first in one source file, tested
    its basic function, and then separated the declaration in a header and
    the implementation in a separate implementation file. It was just for convenience, and no issue at all, or rather trivial, to separate the
    parts. That was no refactoring, though.

    In real refactoring projects I've done _complex transformation_ tasks,
    not just the mundane and trivial task of separating code across files.


    But I also think Bart is wildly overstating his claims of how much of
    an effort it is.  Usually it's just something you do, without needing
    much extra effort or risk - the thought and planning effort going in
    to how you are doing your re-structure is the time-consuming part, not
    the mechanical copy-and-paste or adding a couple of extern
    declarations to a new header.

    Indeed. - Actually I'm unsure whether he's making up these statements
    because he really has a mental problem and difficulties handling that,
    or whether he has some satisfaction in making up peculiar claims just
    for the sake of an argument.

    Most modern languages allow out-of-order function definitions without
    needing a forward declaration to make it possible. (Go which I just
    tried also allows it with global variables at least.)

    So lots of other people thought that was a jolly good idea.

    Does that make /them/ mentally unstable? I guess they must have thought
    the C model was lacking.

    Do you think it is reasonable for me to suggest that you lot are
    masochists for lightly dismissing the inconveniences of C?

    Meanwhile C allows this:

    void F(int c, int d);
    void F(int a, int b) {}
    void F(int, int a);
    void F(int x, int y);
    void F(int32_t z, int32_t);

    int a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a;

    Nothing to see here; please move on!

    BTW, that last example is interesting:

    static int a;
    void F() {a;}

    static int a;
    void G() {a;}

    Here the intention was that each function uses a private global
    variable, but a mistake is made and they have identical names.

    However, that is not an error in C. Maybe, at one point, each pair was
    in a different source file, and then they were combined.

    So is this a valid criticism of the way C works, or do you still think
    I'm round the bend?

    FWIW, I think you're the one who needs to see somebody; I mean,
    constantly with the personal insults. What the hell is it with you?
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Mon May 18 11:23:00 2026
    From Newsgroup: comp.lang.c

    On 18/05/2026 02:22, Dan Cross wrote:
    In article <10uc81u$1kd2r$1@dont-email.me>, Bart <bc@freeuk.com> wrote:
    On 17/05/2026 02:21, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:
    On 16/05/2026 23:03, fir wrote:
    the fact that in c a language/compiler sees only functions or
    variables that are up in code is a disaster
    it is a disaster becouse it dont alow you to split code on N files
    each file has realted functions and variables and not to care on the >>>>> global order of it

    I mentioned something like this a week ago, suggesting that in C it
    was harder work than necessary to split one source file up into two or >>>> more.

    And you offered no evidence for your claim, not even telling us
    that you had tried it and found it difficult.

    Everyone here will know what is involved. But nobody wants to admit that
    it can be onerous.

    Here is a very simple example of one 'module', which involves two source
    files, 'a.h' and 'a.c':

    [snip]

    So, yes, I believe a decent module scheme means stuff like this is less
    work than in C.

    The example is you have two files, a.h and a.c; a.c has (say) a
    bunch of `static`-qualified functions; obviously these are
    file-scope, but have internal linkage. They can call each
    other.

    Now you split the file into two, and these functions need
    external linkage and prototypes in a header file. Instead of
    just creating a new `.c` file, you've got to change some stuff
    in a header file (possibly newly created) as well.

    I'll admit: you have a valid point.

    Yes, this happens. I do it pretty frequently. It's a chore,
    though I don't think it's quite as bad as you are making it out
    to be; annoying, certainly, but probably not in my list of top
    10, 15, or 20 annoyances about C.


    So you admit you have a list too! Mine had 100 items.

    But it is an annoyance, nonetheless.

    It would have been around 2013 that I decided to write my first big C
    program. Still, I found the syntax unpalatable enough that I needed some
    help. Macros couldn't do everything I wanted, so I decided to use a
    script to convert annotated C code (ie. a thin syntax wrapper) to pure C.

    Source files had a .cc extension. A file prog.cc might look like this:

    #include "prog.cl"

    global func int F(int a, b) =
    switch (a)
    when 1, 2 then
    when 3 then
    elsesw
    endsw
    end

    proc G(int a, b) =
    end

    The generated .c file had to match lines 1:1 with the original so that
    error reports from the C compiler would have the same .cc file number.

    Three files were generated:

    * prog.c which is submitted to the C compiler

    * prog.cl which contained prototypes of all local functions (this
    allows functions in any order), which is included in this file

    * prog.cx containing prototypes of exported functions (included
    in a project-wide header)

    The output file prog.c from my example might look like this:

    #include "prog.cl"

    int F(int a, b) {
    switch (a) {
    break; case 1:case 2:
    break; case 3:
    break; default:
    }
    }

    void G(int a, b) {
    }

    This is prog.cl:

    static void G(int a, b);

    And prog.cx:

    extern int F(int a, b);

    So, no manual writing of forward declarations. No manual maintainence of
    a function signature in two places.

    The scheme worked well enough for my 20Kloc application. But it only
    fixed 10% of my grievances with C. In the end I decided (again) to
    perservere with my language.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Mon May 18 11:57:43 2026
    From Newsgroup: comp.lang.c

    On 18/05/2026 07:56, David Brown wrote:
    On 17/05/2026 18:56, Bart wrote:

    But it means writing and maintaining function prototypes at the top of
    the file. That is what's tiresome.


    /If/ you want to write your functions in an arbitrary order, then you
    need to declare your static functions before you use them.  That is certainly true.

      I accept that you like it, and I appreciate
    that you are not alone in that.  But other people have different preferences.  I /like/ having a fixed order.  I almost never declare static functions, and when programming in a language that allows
    arbitrary order (such as Python), I still order my code bottom-up.  This means when you look at my code, if you want to know the definition of an identifier, you only need to look in one direction - upwards.

    I am not saying that this ordering is somehow universally or objectively better than arbitrary order.  But I am saying that arbitrary order is
    not universally or objectively better in a programming language.
    Flexibility has its downsides, and there's a lot of personal opinion and preferences involved.

    But I agree that if you use a language that has a "declare before use"
    rule (as many languages do), and you want to write in an arbitrary
    order, then it will involve extra effort.  Such effort may be annoying,
    but it is entirely self-imposed.

    In my C compiler project, there are nearly 3000 top-level functions, variables, types, enumerations, named constants and macros. That is,
    declared at module scope, and including local and exported names.

    (My non-C compiler generates that list which is picked up by my IDE.)

    I really don't want the extra hassle of managing a dependency order. In
    any case, there are also mutual out of order references.

    So it is a necessity. Once you use such a language yourself, C will seem archaic.

    So if you wanted, you could define all local variables at the end of a
    function rather than at the top; in C syntax:

        void GF() {
            F(&x, &y, &z);
            ...
            int x, y, z;
        }

    This doesn't look that useful at first, but in a current project where
    I am generating code in that language, it is invaluable, as I don't
    know exactly how many tempory variables need to be defined until I'm
    done generating code for the body.


    It does not just look useless at first sight, it looks horrible.  But I write my code myself, for the most part - generated code does not need
    to be as easily read and understood.  (I can't imagine how this
    "feature" is useful for generating code - surely it would be negligible effort to build up your list of variables as you build up the generated statements, and output the whole function in one lump.  You are no
    longer trying to fit this into a few KB of ram on a Z80.)

    It is for simplicity. I don't want an extra pass generating internal
    data structures when I can just generate source text as I go. But then
    some details are not known until later.

    There are ways of injecting new text into an earlier spot, but they are
    hairy. Since the target language has this feature, then why not use it?

    Since either it has 'out-of-order' definitions, or it hasn't. In this
    case it has.



    Much better, IMHO, is to use a language that lets you mix declarations
    and statements as needed.  I see declaring your local variables in a
    list at the top of a function - or, far worse, at the bottom - as
    archaic style.

    I allow definitions anywhere, including mixed with executable statements.

    But I prefer to keep executable code clean and free of type-related
    clutter, and to have separate summary of locals in one place.

    This also allows easier transitioning between my static language, which
    needs type annotations, and my dynamic one, which doesn't. Or porting to
    other languages where details of my algorithm are relevant, but language specific types aren't.

    I don't routinely declare locals at the end of a function. Especially if
    they are initialised, as that assignment takes place at that spot. So
    then placement is important.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.lang.c on Mon May 18 11:07:27 2026
    From Newsgroup: comp.lang.c

    In article <10uepa5$2dpee$1@dont-email.me>, Bart <bc@freeuk.com> wrote:
    On 18/05/2026 02:22, Dan Cross wrote:
    In article <10uc81u$1kd2r$1@dont-email.me>, Bart <bc@freeuk.com> wrote:
    On 17/05/2026 02:21, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:
    On 16/05/2026 23:03, fir wrote:
    the fact that in c a language/compiler sees only functions or
    variables that are up in code is a disaster
    it is a disaster becouse it dont alow you to split code on N files >>>>>> each file has realted functions and variables and not to care on the >>>>>> global order of it

    I mentioned something like this a week ago, suggesting that in C it
    was harder work than necessary to split one source file up into two or >>>>> more.

    And you offered no evidence for your claim, not even telling us
    that you had tried it and found it difficult.

    Everyone here will know what is involved. But nobody wants to admit that >>> it can be onerous.

    Here is a very simple example of one 'module', which involves two source >>> files, 'a.h' and 'a.c':

    [snip]

    So, yes, I believe a decent module scheme means stuff like this is less
    work than in C.

    The example is you have two files, a.h and a.c; a.c has (say) a
    bunch of `static`-qualified functions; obviously these are
    file-scope, but have internal linkage. They can call each
    other.

    Now you split the file into two, and these functions need
    external linkage and prototypes in a header file. Instead of
    just creating a new `.c` file, you've got to change some stuff
    in a header file (possibly newly created) as well.

    I'll admit: you have a valid point.

    Yes, this happens. I do it pretty frequently. It's a chore,
    though I don't think it's quite as bad as you are making it out
    to be; annoying, certainly, but probably not in my list of top
    10, 15, or 20 annoyances about C.

    So you admit you have a list too! Mine had 100 items.

    Of course. I never said that I did not.

    Indeed, no one I've seen engage with you seriously recently has
    suggested that they think C is perfect; most have said there are
    things they wish were different about C.

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Mon May 18 13:57:00 2026
    From Newsgroup: comp.lang.c

    On 18/05/2026 12:57, Bart wrote:
    On 18/05/2026 07:56, David Brown wrote:
    On 17/05/2026 18:56, Bart wrote:

    But it means writing and maintaining function prototypes at the top
    of the file. That is what's tiresome.


    /If/ you want to write your functions in an arbitrary order, then you
    need to declare your static functions before you use them.  That is
    certainly true.

      I accept that you like it, and I appreciate that you are not alone
    in that.  But other people have different preferences.  I /like/
    having a fixed order.  I almost never declare static functions, and
    when programming in a language that allows arbitrary order (such as
    Python), I still order my code bottom-up.  This means when you look at
    my code, if you want to know the definition of an identifier, you only
    need to look in one direction - upwards.

    I am not saying that this ordering is somehow universally or
    objectively better than arbitrary order.  But I am saying that
    arbitrary order is not universally or objectively better in a
    programming language. Flexibility has its downsides, and there's a lot
    of personal opinion and preferences involved.

    But I agree that if you use a language that has a "declare before use"
    rule (as many languages do), and you want to write in an arbitrary
    order, then it will involve extra effort.  Such effort may be
    annoying, but it is entirely self-imposed.

    In my C compiler project, there are nearly 3000 top-level functions, variables, types, enumerations, named constants and macros. That is, declared at module scope, and including local and exported names.

    (My non-C compiler generates that list which is picked up by my IDE.)

    I really don't want the extra hassle of managing a dependency order. In
    any case, there are also mutual out of order references.

    So it is a necessity. Once you use such a language yourself, C will seem archaic.

    You are simply incorrect.

    You have a personal style that is somewhat anarchic and you prefer to be
    able to jumble your declarations around. Okay, I accept that's what you
    like. But you are wrong to think it is somehow an objectively better
    way to organise code or an objectively good language feature. You are
    wrong to guess what I or anyone else might think or prefer.

    You really need to stop assuming that you have found some kind of
    nirvana of programming. All you have found is what you happen to like - nothing more than that.


    So if you wanted, you could define all local variables at the end of
    a function rather than at the top; in C syntax:

        void GF() {
            F(&x, &y, &z);
            ...
            int x, y, z;
        }

    This doesn't look that useful at first, but in a current project
    where I am generating code in that language, it is invaluable, as I
    don't know exactly how many tempory variables need to be defined
    until I'm done generating code for the body.


    It does not just look useless at first sight, it looks horrible.  But
    I write my code myself, for the most part - generated code does not
    need to be as easily read and understood.  (I can't imagine how this
    "feature" is useful for generating code - surely it would be
    negligible effort to build up your list of variables as you build up
    the generated statements, and output the whole function in one lump.
    You are no longer trying to fit this into a few KB of ram on a Z80.)

    It is for simplicity. I don't want an extra pass generating internal
    data structures when I can just generate source text as I go. But then
    some details are not known until later.


    I don't care. This is a limitation of your own making, and there is no
    good technical reason for that limitation. So your "solution" to your
    own invented problem does not provide any evidence or justification for
    why declaring after usage is a useful feature in a language. Of course
    you can make your own language the way you prefer, and use it the way
    you want - but don't imagine that it is "better" in any sense, or that
    others would also like it. Similarly, I don't claim to know that others
    would /not/ like it - I can only answer for myself. And I can only
    point to the strong bias of real-world imperative languages where
    "declare before use" is the norm as an indication that people see that
    as a better way for a language to work.

    There are ways of injecting new text into an earlier spot, but they are hairy. Since the target language has this feature, then why not use it?

    Since either it has 'out-of-order' definitions, or it hasn't. In this
    case it has.



    Much better, IMHO, is to use a language that lets you mix declarations
    and statements as needed.  I see declaring your local variables in a
    list at the top of a function - or, far worse, at the bottom - as
    archaic style.

    I allow definitions anywhere, including mixed with executable statements.

    But I prefer to keep executable code clean and free of type-related
    clutter, and to have separate summary of locals in one place.

    This also allows easier transitioning between my static language, which needs type annotations, and my dynamic one, which doesn't. Or porting to other languages where details of my algorithm are relevant, but language specific types aren't.

    I don't routinely declare locals at the end of a function. Especially if they are initialised, as that assignment takes place at that spot. So
    then placement is important.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From fir@profesor.fir@gmail.com to comp.lang.c on Mon May 18 14:10:14 2026
    From Newsgroup: comp.lang.c

    Dan Cross pisze:
    In article <10uepa5$2dpee$1@dont-email.me>, Bart <bc@freeuk.com> wrote:
    On 18/05/2026 02:22, Dan Cross wrote:
    In article <10uc81u$1kd2r$1@dont-email.me>, Bart <bc@freeuk.com> wrote: >>>> On 17/05/2026 02:21, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:
    On 16/05/2026 23:03, fir wrote:
    the fact that in c a language/compiler sees only functions or
    variables that are up in code is a disaster
    it is a disaster becouse it dont alow you to split code on N files >>>>>>> each file has realted functions and variables and not to care on the >>>>>>> global order of it

    I mentioned something like this a week ago, suggesting that in C it >>>>>> was harder work than necessary to split one source file up into two or >>>>>> more.

    And you offered no evidence for your claim, not even telling us
    that you had tried it and found it difficult.

    Everyone here will know what is involved. But nobody wants to admit that >>>> it can be onerous.

    Here is a very simple example of one 'module', which involves two source >>>> files, 'a.h' and 'a.c':

    [snip]

    So, yes, I believe a decent module scheme means stuff like this is less >>>> work than in C.

    The example is you have two files, a.h and a.c; a.c has (say) a
    bunch of `static`-qualified functions; obviously these are
    file-scope, but have internal linkage. They can call each
    other.

    Now you split the file into two, and these functions need
    external linkage and prototypes in a header file. Instead of
    just creating a new `.c` file, you've got to change some stuff
    in a header file (possibly newly created) as well.

    I'll admit: you have a valid point.

    Yes, this happens. I do it pretty frequently. It's a chore,
    though I don't think it's quite as bad as you are making it out
    to be; annoying, certainly, but probably not in my list of top
    10, 15, or 20 annoyances about C.

    So you admit you have a list too! Mine had 100 items.

    Of course. I never said that I did not.

    Indeed, no one I've seen engage with you seriously recently has
    suggested that they think C is perfect; most have said there are
    things they wish were different about C.

    in fast this "only see up" is on top or near top on that list..what you find worse?

    i never make a list but this "see only up" is definitelly near top

    other very annoing is that long x = 'dhbsf00d' is not standarized afaik
    (such 8-char tags would be extremely handy sometimes)

    also annoying is thet if {} has its own scope

    also annoying if you cant foo( float x,y,z) {} (this ia somewhat
    disputable if its allowed but x, y are ints but im not sure if it work
    in today c

    also annoying is that foo() {} has return type int instead of void
    (though this could be eventually disputable)



    annoying are also things that are not in c like "int x, y = foo(2,3);"
    but those dont belong to that list
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From fir@profesor.fir@gmail.com to comp.lang.c on Mon May 18 15:01:25 2026
    From Newsgroup: comp.lang.c

    fir pisze:
    Dan Cross pisze:
    In article <10uepa5$2dpee$1@dont-email.me>, Bart  <bc@freeuk.com> wrote: >>> On 18/05/2026 02:22, Dan Cross wrote:
    In article <10uc81u$1kd2r$1@dont-email.me>, Bart  <bc@freeuk.com>
    wrote:
    On 17/05/2026 02:21, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:
    On 16/05/2026 23:03, fir wrote:
    the fact that in c a language/compiler sees only functions or
    variables that are up in code is a disaster
    it is a disaster becouse it dont alow you to split code on N files >>>>>>>> each file has realted functions and variables and not to care on >>>>>>>> the
    global order of it

    I mentioned something like this a week ago, suggesting that in C it >>>>>>> was harder work than necessary to split one source file up into >>>>>>> two or
    more.

    And you offered no evidence for your claim, not even telling us
    that you had tried it and found it difficult.

    Everyone here will know what is involved. But nobody wants to admit >>>>> that
    it can be onerous.

    Here is a very simple example of one 'module', which involves two
    source
    files, 'a.h' and 'a.c':

    [snip]

    So, yes, I believe a decent module scheme means stuff like this is
    less
    work than in C.

    The example is you have two files, a.h and a.c; a.c has (say) a
    bunch of `static`-qualified functions; obviously these are
    file-scope, but have internal linkage.  They can call each
    other.

    Now you split the file into two, and these functions need
    external linkage  and prototypes in a header file.  Instead of
    just creating a new `.c` file, you've got to change some stuff
    in a header file (possibly newly created) as well.

    I'll admit: you have a valid point.

    Yes, this happens.  I do it pretty frequently.  It's a chore,
    though I don't think it's quite as bad as you are making it out
    to be; annoying, certainly, but probably not in my list of top
    10, 15, or 20 annoyances about C.

    So you admit you have a list too! Mine had 100 items.

    Of course.  I never said that I did not.

    Indeed, no one I've seen engage with you seriously recently has
    suggested that they think C is perfect; most have said there are
    things they wish were different about C.

    in fast this "only see up" is on top or near top on that list..what you
    find
    worse?

    i never make a list but this "see only up" is definitelly near top

    other very annoing is that long x = 'dhbsf00d' is not standarized afaik (such 8-char tags would be extremely handy sometimes)

    also annoying is thet if {} has its own scope

    also annoying if you cant foo( float x,y,z) {}  (this ia somewhat disputable if its allowed but x, y are ints but im not sure if it work
    in today c

    also annoying is that foo() {} has return type int instead of void
    (though this could be eventually disputable)



    annoying is alos that "'" dont work more like

    if(a) a=3,c=3,print(2), x=10;

    AND ye i forgot

    b instead of *a.b THAT is annoying (esp im not sure does not a.*b is notjust avaliable?)

    annoying are also things that are not in c like "int x, y = foo(2,3);"
    but those dont belong to that list

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Mon May 18 14:18:23 2026
    From Newsgroup: comp.lang.c

    On 18/05/2026 12:57, David Brown wrote:
    On 18/05/2026 12:57, Bart wrote:

    In my C compiler project, there are nearly 3000 top-level functions,
    variables, types, enumerations, named constants and macros. That is,
    declared at module scope, and including local and exported names.

    (My non-C compiler generates that list which is picked up by my IDE.)

    I really don't want the extra hassle of managing a dependency order.
    In any case, there are also mutual out of order references.

    So it is a necessity. Once you use such a language yourself, C will
    seem archaic.

    You are simply incorrect.

    You have a personal style that is somewhat anarchic and you prefer to be able to jumble your declarations around.  Okay, I accept that's what you like.

      But you are wrong to think it is somehow an objectively better
    way to organise code or an objectively good language feature.  You are wrong to guess what I or anyone else might think or prefer.

    You really need to stop assuming that you have found some kind of
    nirvana of programming.  All you have found is what you happen to like - nothing more than that.

    This is more fundamental. You seem think all top-level entities, even
    within one source file, need to be some sort of ordered set, which
    depends on which interactions there may or may not be between then.

    Interactions which can change as code is developed, which may require a
    change in the order.

    Nobody should really need to care about such things; they are plenty of
    other matters to deal with.

    You only thing it is a 'good' thing because C requires it.

    If you were using a language where order didn't matter, would you go to
    the same trouble?

    Maybe some people prefer their functions in alphabetical order!

    So it's not question of liking or not liking, but doing anyway with an irrelevant distraction.

    In any case, when I look at open source C code, I do see loads of
    forward function declarations, even for non-exported functions.

    I guess not everyone is as meticuluous as you. So for those people,
    needing those declarations is a nuisance.

    It is for simplicity. I don't want an extra pass generating internal
    data structures when I can just generate source text as I go. But then
    some details are not known until later.


    I don't care.  This is a limitation of your own making, and there is no good technical reason for that limitation.  So your "solution" to your
    own invented problem does not provide any evidence or justification for
    why declaring after usage is a useful feature in a language.  Of course
    you can make your own language the way you prefer, and use it the way
    you want - but don't imagine that it is "better" in any sense, or that others would also like it.  Similarly, I don't claim to know that others would /not/ like it - I can only answer for myself.  And I can only
    point to the strong bias of real-world imperative languages where
    "declare before use" is the norm as an indication that people see that
    as a better way for a language to work.


    Same reasons as for the above for functions. I make much use of
    enumerations for example, especially in table form where there are
    parallel sets of data defined at the same time. Example:

    enumdata []int promotions =
    (ti8, ti64),
    (ti16, ti64),
    (ti32, ti64),
    (ti64, ti64),
    (tu8, tu64), ....
    end

    This defines enums ti8-ti64... which I want to be ordered that way,
    while the parallel array gives the type it will be promoted to. But
    'ti64' in those first three entries isn't defined until the fourth entry.

    But a more typical example is simply where one table may refers to enums defined earlier, later, or in some other module.

    Here again I simply do not want to worry about the order of the tables.
    Maybe tables A, B, C all have references to each order, so it is
    impossible to order them anyway.

    Sure, there will be /some/ way to get around it in C, maybe involving
    those lovely X-macros, but the point is /not needing to care or bother/.

    People here always say, just write code clearly and in the most obvious
    way, and the compiler will take care of making it efficient.

    But when I try and do similar things, then Oh, it doesn't really matter!
    It's not needed. I've never needed to do that. It's a minor annoyance.
    You just do X, Y and Z, no problem. You're doing it wrong! You must be mentally ill to think that! (JP actually said this.)

    You know, all it takes is to admit that out-of-order definitions can be
    a desirable and convenient language feature. It's one that other
    languages have, it's not something that only exists in my language
    (though I take it a bit further).



    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Mon May 18 14:33:10 2026
    From Newsgroup: comp.lang.c

    On 18/05/2026 14:01, fir wrote:
    fir pisze:
    Dan Cross pisze:

    i never make a list but this "see only up" is definitelly near top

    other very annoing is that long x = 'dhbsf00d' is not standarized
    afaik (such 8-char tags would be extremely handy sometimes)

    I think you would have liked my language! That does have char constants
    up to 'ABCDEFGH' (When I supported 128-bit types, up to 'ABCDEFGHIJKLMNOP'.)

    C will support them up to 'int' width, which means 'ABCD' for 32-bit
    int. In theory a C with a 64-bit int would allow longer constants, but
    that's not going to happen on any common platforms.

    As it is, even anything above 'A' is implementation-defined, unless C23
    has changed that.

    also annoying is thet if {} has its own scope

    (My language only has a single function-wide scope.)


    also annoying if you cant foo( float x,y,z) {}  (this ia somewhat
    disputable if its allowed but x, y are ints but im not sure if it work
    in today c>> also annoying is that foo() {} has return type int instead of void
    (though this could be eventually disputable)

    Not sure what either of these mean.



    annoying is alos that "'" dont work more like

    if(a) a=3,c=3,print(2), x=10;

    AND ye i forgot

    b instead of *a.b THAT is annoying (esp im not sure does not a.*b is notjust avaliable?)

    m is equivalent to (*P).m. However, (**Q).m cannot be reduced to
    (*Q)->m; -> only fixes one level!

    A post-fix '*' operator would mean being able to type P*.m and Q**.m.
    You wouldn't really need -> here.

    (My language uses post-fix '^' for derefs. However, the language allows
    you to drop the explicit '^'; it will add in the derefs as needed.

    That means those examples can be written as P.m and Q.m. This is very nice.

    I was against allowing P->m in C to be written as P.m in C at one time,
    but I've changed my mind; the cleaner code is too big an advantage.)



    annoying are also things that are not in c like "int x, y = foo(2,3);"
    but those dont belong to that list

    Huh? This is legal C.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Mon May 18 16:07:26 2026
    From Newsgroup: comp.lang.c

    On 18/05/2026 15:18, Bart wrote:
    On 18/05/2026 12:57, David Brown wrote:
    On 18/05/2026 12:57, Bart wrote:

    In my C compiler project, there are nearly 3000 top-level functions,
    variables, types, enumerations, named constants and macros. That is,
    declared at module scope, and including local and exported names.

    (My non-C compiler generates that list which is picked up by my IDE.)

    I really don't want the extra hassle of managing a dependency order.
    In any case, there are also mutual out of order references.

    So it is a necessity. Once you use such a language yourself, C will
    seem archaic.

    You are simply incorrect.

    You have a personal style that is somewhat anarchic and you prefer to
    be able to jumble your declarations around.  Okay, I accept that's
    what you like.

      But you are wrong to think it is somehow an objectively better way
    to organise code or an objectively good language feature.  You are
    wrong to guess what I or anyone else might think or prefer.

    You really need to stop assuming that you have found some kind of
    nirvana of programming.  All you have found is what you happen to like
    - nothing more than that.

    This is more fundamental. You seem think all top-level entities, even
    within one source file, need to be some sort of ordered set, which
    depends on which interactions there may or may not be between then.


    I am telling you that the order within a source file comes from personal preference. Some people like a specific order - it makes the code
    easier to navigate, and easier to understand. Some people prefer the
    freedom to order any way they fancy as they go along.

    There are occasions - a very small minority of cases - where you can't
    have a fixed order because you have mutual references between functions
    or data structures. That means you need some sort of forward
    declaration mechanism. Such situations are rare in most code.


    Interactions which can change as code is developed, which may require a change in the order.

    Nobody should really need to care about such things; they are plenty of other matters to deal with.

    You only thing it is a 'good' thing because C requires it.


    Please stop doing that. As has been pointed out to you, your ability to understand other people or guess their reasoning is almost completely non-existent. Stopped clocks are better at guessing than you are.

    And please stop insinuating that people in this group are lying to you
    or inventing things just to disagree with you.

    I have already said that when programming in a language where functions
    can usually be ordered willy-nilly, I prefer to use a bottom-up
    ordering. I've explained why I prefer that.

    If you were using a language where order didn't matter, would you go to
    the same trouble?

    It is not "trouble" - I /choose/ bottom-up ordering, because I prefer
    it. I think it makes code clearer and easier to navigate, both for
    myself and for others.


    Maybe some people prefer their functions in alphabetical order!

    So it's not question of liking or not liking, but doing anyway with an irrelevant distraction.


    The "trouble" or "distraction" is purely in your own mind.

    That does not mean it is not real, for you. If you find it bothers you
    to be unable to re-order functions (and other top-level entities)
    freely, then the hassle is real - for you. And you are not alone in
    your preferences. But that does not mean it is real for everyone else,
    or that everyone else has the same preferences you do.

    In any case, when I look at open source C code, I do see loads of
    forward function declarations, even for non-exported functions.


    Some people do write forward function declarations for static functions.
    I don't - I find tracking and updating them to be an inconvenience and
    a not insignificant source of inconsistency. But I can pretty much
    guarantee that you do not understand /why/ some people choose to write
    such declarations, because you appear to assume it is so that they can re-order their function definitions. That is no doubt the case for some people, but I think most people write such declarations because they
    think it is helpful (to themselves, or to others reading the code) to
    see a summary of the file's functions in one place.

    I guess not everyone is as meticuluous as you. So for those people,
    needing those declarations is a nuisance.

    It is for simplicity. I don't want an extra pass generating internal
    data structures when I can just generate source text as I go. But
    then some details are not known until later.


    I don't care.  This is a limitation of your own making, and there is
    no good technical reason for that limitation.  So your "solution" to
    your own invented problem does not provide any evidence or
    justification for why declaring after usage is a useful feature in a
    language.  Of course you can make your own language the way you
    prefer, and use it the way you want - but don't imagine that it is
    "better" in any sense, or that others would also like it.  Similarly,
    I don't claim to know that others would /not/ like it - I can only
    answer for myself.  And I can only point to the strong bias of real-
    world imperative languages where "declare before use" is the norm as
    an indication that people see that as a better way for a language to
    work.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From fir@profesor.fir@gmail.com to comp.lang.c on Mon May 18 16:39:16 2026
    From Newsgroup: comp.lang.c

    Bart pisze:
    also annoying if you cant foo( float x,y,z) {}  (this ia somewhat
    disputable if its allowed but x, y are ints but im not sure if it
    work in today c>> also annoying is that foo() {} has return type int
    instead of void
    (though this could be eventually disputable)

    Not sure what either of these mean.

    i simply mean that repeating that float is annoying


    Something(float x1, float y1, float x2, float y2 ) {}

    should be

    Something(float x1,y1,x2,y2 ) {}

    esp if those last 3 are not ints by default in present c - it was in
    old, but in present i dont remember

    checked they are not

    _WAVE_SAMPLES.C:62:18: error: unknown type name 'y'
    void foo(float x,y,z) {}

    in old c it would be disputable if foo(a,b,c) {} having a b c ints it is
    not good but if it not work then thise repeating floats are annoying

    one of two should work

    void foo(float x,y,z) {}

    either x y z are all floats or x float y z ints..

    present compile error is bad

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From fir@profesor.fir@gmail.com to comp.lang.c on Mon May 18 16:59:26 2026
    From Newsgroup: comp.lang.c

    Bart pisze:

    AND ye i forgot

    b instead of *a.b THAT is annoying (esp im not sure does not a.*b
    is notjust avaliable?)

    m is equivalent to (*P).m. However, (**Q).m cannot be reduced to
    (*Q)->m; -> only fixes one level!

    A post-fix '*' operator would mean being able to type P*.m and Q**.m.
    You wouldn't really need -> here.

    (My language uses post-fix '^' for derefs. However, the language allows
    you to drop the explicit '^'; it will add in the derefs as needed.

    That means those examples can be written as P.m and Q.m. This is very nice.

    I was against allowing P->m in C to be written as P.m in C at one time,
    but I've changed my mind; the cleaner code is too big an advantage.)



    from sorta cpmpatibility only to repair flaws imo *a.b should work as
    (*a).b and a.*b should work as *(a.b) i guess

    if to skip pointers at all

    void foo(int*a)
    {
    a=2; print(a); //woring as *a=2; print(*a);
    }

    im maybe not 100% sure but it seem im like 75% convinced that those
    pointers in fact should be dropped

    (i was writing back then on this but i dont remember fully my conclusions)

    probbaly you should use labels a, and p instead of present *a, *p
    and only at lace of definition it should be noted as pointer

    float x = 3.4;
    float* xf = &x;
    xf+=0.1;


    the usage of *xf has probably not much sense becouse you much more
    operate on values even if you use pointers than on pointers itself

    i know people often use

    char *P = "akjsnjksnk";

    while(*p++!=0) something;

    but still imo use by value is more common

    so it would be more

    while(p!=0) { &p++; something; }

    assuming you still use int*p; as a definition of pointer
    and &p as deference to its addres value (which is not obvious becouse eventually one could use *p as a deference to pointer value - just
    oposite it is now

    int *P; - definition
    p - pointer value,
    *p - value

    where it could be

    int *P; - definition
    p - value
    *p - pointer value


    this "swap" is probably quite sane









    annoying are also things that are not in c like "int x, y =
    foo(2,3);" but those dont belong to that list

    Huh? This is legal C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From fir@profesor.fir@gmail.com to comp.lang.c on Mon May 18 17:04:33 2026
    From Newsgroup: comp.lang.c

    Bart pisze:


    annoying are also things that are not in c like "int x, y =
    foo(2,3);" but those dont belong to that list

    Huh? This is legal C.

    ye i know , but it is probably not work like i would have

    you can deturn structure like int2{int x, y;}

    but as thsi structure is not builtin it makes a SIN/FLAW
    of making unnecesary dependency (on structure definition)
    alos makes unnecesary names and so on

    you know that ** unnecesary dependencies are bad **, and
    ** unnesesary things (objects ) are bad **


    (of i would like to return two values and assign it to two separate
    variables)

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Mon May 18 16:12:26 2026
    From Newsgroup: comp.lang.c

    On 18/05/2026 15:39, fir wrote:
    Bart pisze:
    also annoying if you cant foo( float x,y,z) {}  (this ia somewhat
    disputable if its allowed but x, y are ints but im not sure if it
    work in today c>> also annoying is that foo() {} has return type int
    instead of void
    (though this could be eventually disputable)

    Not sure what either of these mean.

    i simply mean that repeating that float is annoying


    Something(float x1, float y1, float x2, float y2 ) {}

    should be

    Something(float x1,y1,x2,y2 ) {}

    esp if those last 3 are not ints by default in present c - it was in
    old, but in present i dont remember

    checked they are not

    _WAVE_SAMPLES.C:62:18: error: unknown type name 'y'
     void foo(float x,y,z) {}

    in old c it would be disputable if foo(a,b,c) {} having a b c ints it is
    not good but if it not work then thise repeating floats are annoying

    one of two should work

     void foo(float x,y,z) {}

    either x y z are all floats or x float y z ints..

    present compile error is bad

    OK, this actually what I posted about nearly 2 weeks ago:


    BC:
    Huh. This is C:

    uint64_t F(uint64_t s, uint64_t t, uint64_t u, uint64_t v) ...

    This is my language:

    func F(u64 s, t, u, v)u64 ...

    This is what Dan Cross said about that last line:

    That's awful.

    This is the typical attitude here to any such ideas.


    As for the return type, I think that needs to be specified. As you
    showed, it wasn't clear what it is, which is bad.

    C is not high enough level to do type inference, but even if it was,
    users would need to apply the same algorithms, and do the same analysis,
    to figure out what some return type actually was. It's better to just
    state it.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Mon May 18 16:25:00 2026
    From Newsgroup: comp.lang.c

    On 18/05/2026 15:59, fir wrote:
    Bart pisze:

    AND ye i forgot

    b instead of *a.b THAT is annoying (esp im not sure does not a.*b
    is notjust avaliable?)

    m is equivalent to (*P).m. However, (**Q).m cannot be reduced to
    (*Q)->m; -> only fixes one level!

    A post-fix '*' operator would mean being able to type P*.m and Q**.m.
    You wouldn't really need -> here.

    (My language uses post-fix '^' for derefs. However, the language
    allows you to drop the explicit '^'; it will add in the derefs as needed.

    That means those examples can be written as P.m and Q.m. This is very
    nice.

    I was against allowing P->m in C to be written as P.m in C at one
    time, but I've changed my mind; the cleaner code is too big an
    advantage.)



    from sorta cpmpatibility only to repair flaws imo *a.b should work as
    (*a).b and a.*b should work as *(a.b) i guess

    That would need to apply also to *a[i]. It means completely changing the relative precedences of deref '*' and of "." and of "[]" (I don't know
    about "()").

    It would also means half of all type declarations needing to be updated,
    if there was to be the same relation between declarations and expressions.

    Unless the context here is a new, incompatible version of C? Then you
    can do what you like.


    if to skip pointers at all

    void foo(int*a)
    {
      a=2; print(a); //woring as *a=2; print(*a);
    }

    The 'a=2' might be OK. Since the LHS has type 'pointer to int', and RHS
    has 'int', it can make it apply the right number of derefs.

    (This is how Algol68 works, the language I based by own syntax on. But I
    only copied syntax, not semantics since that had a lot of complex
    behaviour.)

    The 'print(a)' is harder, as you can't tell if you want to print the
    value of the pointer, or what is points to.

    The places I drop it in my language are these:

    P^[i] -> P[i]
    Q^.m -> Q.m
    R^(x) -> R()

    since there is no ambiguity. P by itself is the pointer, and P^ is what
    it points to.


    im maybe not 100% sure but it seem im like 75% convinced that those
    pointers in fact should be dropped

    (i was writing back then on this but i dont remember fully my conclusions)

    probbaly you should use labels a, and p instead of present *a, *p
    and only at lace of definition it should be noted as pointer

    float x = 3.4;
    float* xf = &x;
       xf+=0.1;

    Tricky one. Of that was 1 on the RHS, then 'xf += 1' might increment the pointer, or it might increment the float it points to. So ambiguous.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From fir@profesor.fir@gmail.com to comp.lang.c on Mon May 18 17:29:09 2026
    From Newsgroup: comp.lang.c

    fir pisze:
    Bart pisze:

    AND ye i forgot

    b instead of *a.b THAT is annoying (esp im not sure does not a.*b
    is notjust avaliable?)

    m is equivalent to (*P).m. However, (**Q).m cannot be reduced to
    (*Q)->m; -> only fixes one level!

    A post-fix '*' operator would mean being able to type P*.m and Q**.m.
    You wouldn't really need -> here.

    (My language uses post-fix '^' for derefs. However, the language
    allows you to drop the explicit '^'; it will add in the derefs as needed.

    That means those examples can be written as P.m and Q.m. This is very
    nice.

    I was against allowing P->m in C to be written as P.m in C at one
    time, but I've changed my mind; the cleaner code is too big an
    advantage.)



    from sorta cpmpatibility only to repair flaws imo *a.b should work as
    (*a).b and a.*b should work as *(a.b) i guess

    if to skip pointers at all

    void foo(int*a)
    {
      a=2; print(a); //woring as *a=2; print(*a);
    }

    im maybe not 100% sure but it seem im like 75% convinced that those
    pointers in fact should be dropped

    (i was writing back then on this but i dont remember fully my conclusions)

    probbaly you should use labels a, and p instead of present *a, *p
    and only at lace of definition it should be noted as pointer

    float x = 3.4;
    float* xf = &x;
       xf+=0.1;


    the usage of *xf has probably not much sense becouse you much more
    operate on values  even if you use pointers than on pointers itself

    i know people often use

    char *P = "akjsnjksnk";

    while(*p++!=0) something;

    but still imo use by value is more common

    so it would be more

    while(p!=0) { &p++; something; }

    assuming you still use int*p; as a definition of pointer
    and &p as deference to its addres value (which is not obvious becouse eventually one could use *p as a deference to pointer value - just
    oposite it is now

    int *P; - definition
    p - pointer value,
     *p - value

    where it could be

    int *P; - definition
    p - value
     *p - pointer value


    this "swap" is probably quite sane




    this would hovewer reduce arrays arithmetic becouse
    it tab is an array then tab would mean value of an array
    (block of many bytes)

    then tab+2 has no sense (unles treating it as tab block plus one byte or
    int more, which is no nonsense imo .. like tab + "something", tab +1.0+'a'+0xff; )

    *tab+2 then has sense as pointer to tab increased by 2 but then there is
    no imediate way to drop the pointer to get value, unles introducing
    special syntax or assigning it to value

    char * p = *tab+2; p is a third byte of tab


    as for now this swapped pointer seem more logical to me that this from
    old c probbaly - that would get rid of many pointer seen in c code i guess

    wonder if then if i got this eswpapped pointers

    int* p ;

    then p could/should be name as reference (is such swpapped pointer a reference?)

    btw

    int* p = 2; would mean p value is 2

    so question does it much differ form

    int p =2; ??

    int *p = a; would mean p value is a

    but when a changes also value of p changes

    if some has normal

    int a = 3;

    then it eventually could be adding those pointer
    operators *a++ //a points to ram after normal a

    - but this probably shouldnt be legal as most of
    a usage is fixed..so maybe changing a 'pointer value' should compile fail









    annoying are also things that are not in c like "int x, y =
    foo(2,3);" but those dont belong to that list

    Huh? This is legal C.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Mon May 18 16:31:40 2026
    From Newsgroup: comp.lang.c

    On 18/05/2026 16:04, fir wrote:
    Bart pisze:


    annoying are also things that are not in c like "int x, y =
    foo(2,3);" but those dont belong to that list

    Huh? This is legal C.

    ye i know , but it is probably not work like i would have

    you can deturn structure like int2{int x, y;}

    but as thsi structure is not builtin it makes a SIN/FLAW
    of making unnecesary dependency (on structure definition)
    alos makes unnecesary names and so on

    you know that ** unnecesary dependencies are bad **, and
    ** unnesesary things (objects ) are bad **


    (of i would like to return two values and assign it to two separate variables)


    OK, I used to have such a feature, but dropped it because it wasn't used enough; it wasn't worth the extra complication. But it worked like this:

    func foo:int, int =
    return (10, 20)
    end

    It was used like this:

    int x, y
    (x, y) := foo()

    (You can't combine with the declaration.) It also worked like this:

    x := foo() # discard 2nd value
    foo() # discard both

    The implementation didn't use a struct, more of a tuple, but it simply returned N values (limited to 3/4) in the first N registers.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From fir@profesor.fir@gmail.com to comp.lang.c on Mon May 18 17:35:54 2026
    From Newsgroup: comp.lang.c

    Bart pisze:
    As for the return type, I think that needs to be specified. As you
    showed, it wasn't clear what it is, which is bad.

    C is not high enough level to do type inference, but even if it was,
    users would need to apply the same algorithms, and do the same analysis,
    to figure out what some return type actually was. It's better to just
    state it.

    not sure what you talkin about but sole identifier , like say f tells
    you what it is, you need to look up

    it will tel you it is double, or it is struct of int and char or this is function which returns 2 foats and tahe 3 ints as an input - pure
    identifier encodes it and i dont thionk you should specify it more than that

    repeating thsi info in ann call-places if you mean that would be bad
    becouse there oftrn may and you need more unnecesary work
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From fir@profesor.fir@gmail.com to comp.lang.c on Mon May 18 17:50:54 2026
    From Newsgroup: comp.lang.c

    Bart pisze:
    float x = 3.4;
    float* xf = &x;
        xf+=0.1;

    Tricky one. Of that was 1 on the RHS, then 'xf += 1' might increment the pointer, or it might increment the float it points to. So ambiguous.

    this aboe may be flawed as it probably should be

    float x = 3.4;
    float* xf = x;
    xf+=0.1;

    here float* xf = x; copies the adress but it also look like it copies a
    value

    this swapped * arithmetic is not ambigious imo - its just swapped

    a meand value of pointer a and *a means adress/pointer value as adress


    this is probably better for all people who do not like to se such many *
    in codes as they would be very reduced

    and when you would see it you would see it in two cases

    1) in definitions
    2) in places where realadres arithmetic is done


    now you see it in places where values are used and sometimes you dont
    see it when dres arithmetic is done - which is sorta swapped to reality
    it seems (becous when you see * you should hae places when pointer
    arithemetic is really done imo)

    in fact you culd ewen get rid it in definitions using this reference
    like in c++

    int& c = a;

    c is value
    *c would mean pointer

    i dont like c++ but it is the option

    option is also drop * and use & in both cases

    int& c = a;

    c is value
    &c would mean pointer

    this is maybe enough to say that

    I. reference (if it behaves how i assumed here above) is a swapped pointer
    II. swapped probably has more sense then normal

    (so eventually they really coud add references to c)

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From fir@profesor.fir@gmail.com to comp.lang.c on Mon May 18 18:12:39 2026
    From Newsgroup: comp.lang.c

    Bart pisze:
    On 18/05/2026 16:04, fir wrote:
    Bart pisze:


    annoying are also things that are not in c like "int x, y =
    foo(2,3);" but those dont belong to that list

    Huh? This is legal C.

    ye i know , but it is probably not work like i would have

    you can deturn structure like int2{int x, y;}

    but as thsi structure is not builtin it makes a SIN/FLAW
    of making unnecesary dependency (on structure definition)
    alos makes unnecesary names and so on

    you know that ** unnecesary dependencies are bad **, and
    ** unnesesary things (objects ) are bad **


    (of i would like to return two values and assign it to two separate
    variables)


    OK, I used to have such a feature, but dropped it because it wasn't used enough; it wasn't worth the extra complication. But it worked like this:

      func foo:int, int =
          return (10, 20)
      end

    It was used like this:

      int x, y
      (x, y) := foo()

    (You can't combine with the declaration.) It also worked like this:

      x := foo()    # discard 2nd value
      foo()         # discard both

    The implementation didn't use a struct, more of a tuple, but it simply returned N values (limited to 3/4) in the first N registers.

    returning many vaules is logical so it shouldnt be dropped

    you may use my conclusions resulted form deducing what should be
    improved in c to be better - for ma its more valuable to makin such conclusions than writing a compiler

    for example you could take such clear piece of syntax

    ¯z ¯b foo _x _y { z¯ x+y, b¯ x-y }

    _x mean delare int x
    x_ means assign int x

    upper _ means that this variable is output one

    this is good for ints (very clean and dynamic syntax imo) hovever i have trouble how to add other types)


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From fir@profesor.fir@gmail.com to comp.lang.c on Mon May 18 18:21:10 2026
    From Newsgroup: comp.lang.c

    fir pisze:
    Bart pisze:
    float x = 3.4;
    float* xf = &x;
        xf+=0.1;

    Tricky one. Of that was 1 on the RHS, then 'xf += 1' might increment
    the pointer, or it might increment the float it points to. So ambiguous.

    this aboe may be flawed as it probably should be

     float x = 3.4;
     float* xf = x;
         xf+=0.1;

    here  float* xf = x; copies the adress but it also look like it copies a value

    this swapped * arithmetic is not ambigious imo - its just swapped

    a meand value of pointer a and *a means adress/pointer value as adress


    this is probably better for all people who do not like to se such many *
    in codes as they would be very reduced

    and when you would see it you would see it in two cases

    1) in definitions
    2) in places where realadres arithmetic is done


    now you see it in places where values are used and sometimes you dont
    see it when dres arithmetic is done - which is sorta swapped to reality
    it seems (becous when you see * you should hae places when pointer arithemetic is really done imo)

    in fact you culd ewen get rid it in definitions using this reference
    like in c++

    int& c = a;

       c is value
       *c would mean pointer

    i dont like c++ but it is the option

    option is also drop * and use & in both cases

    int& c = a;

       c is value
       &c would mean pointer

    this is maybe enough to say that

    I.  reference (if it behaves how i assumed here above) is a swapped pointer II. swapped probably has more sense then normal

    (so eventually they really coud add references to c)


    weird thing is those normal pointers and swapped ones (references) can
    be in language parrallel

    i love c bot im not much big fan of pointers so maybe i should try to
    use c++ and write "only references" c (?)

    im not using them so i dont quite aware if they work fully as i would
    see them in above thoughts
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From fir@profesor.fir@gmail.com to comp.lang.c on Mon May 18 18:24:06 2026
    From Newsgroup: comp.lang.c

    Bart pisze:
    On 18/05/2026 16:04, fir wrote:
    Bart pisze:


    annoying are also things that are not in c like "int x, y =
    foo(2,3);" but those dont belong to that list

    Huh? This is legal C.

    ye i know , but it is probably not work like i would have

    you can deturn structure like int2{int x, y;}

    but as thsi structure is not builtin it makes a SIN/FLAW
    of making unnecesary dependency (on structure definition)
    alos makes unnecesary names and so on

    you know that ** unnecesary dependencies are bad **, and
    ** unnesesary things (objects ) are bad **


    (of i would like to return two values and assign it to two separate
    variables)


    OK, I used to have such a feature, but dropped it because it wasn't used enough; it wasn't worth the extra complication. But it worked like this:

      func foo:int, int =
          return (10, 20)
      end

    It was used like this:

      int x, y
      (x, y) := foo()

    (You can't combine with the declaration.) It also worked like this:

      x := foo()    # discard 2nd value
      foo()         # discard both

    The implementation didn't use a struct, more of a tuple, but it simply returned N values (limited to 3/4) in the first N registers.

    reeturning many values is definitelly logical so you should not drop it (question is how it would be implemented in s i think starting
    convention would be to pass to pointers to in and out structures

    struct in (int,int,int)
    struct out {int , int }

    foo(&out, &in)

    so the function should use pointers to acces it and physically this ram
    should belong to caller


    you could use any of conclusions i for example personally em talking about becuse i prefer deducing what should be done in languages which is more
    fun to me than write compiler (i wrote a half but stopped)

    you could mayge take this syntax

    ¯z ¯b foo _x _y { z¯ x+y, b¯ x-y }

    its unusually clean _x mean "input int x "
    ¯z means" output int z"

    z¯ x+y, means assign output z by a+b

    got a problem how to put floats hovever

    (sorry its redundant - but it not sent properly i thought it vanished so
    i wrote another one than found it in editor, so i post it again)
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From antispam@antispam@fricas.org (Waldek Hebisch) to comp.lang.c on Mon May 18 16:47:02 2026
    From Newsgroup: comp.lang.c

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

    Sure. But in the C world, pre-C99 code is often written in a style with local variables all declared at the top of the function - after C99, it
    is common to declare them when you need them. So as a C programmer, I
    see declaring local variables in one clump together as archaic.

    I agree about style. But AFAICS even in very early C there is block
    structure and one can put variable declarations in the middle of
    sequence, just at the cost of introducing extra blocks. So C99
    looks nicer as there is no need for extra blocks, but pre C99
    already one could keep scopes tight and initialize variables in
    declarations.
    --
    Waldek Hebisch
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Mon May 18 18:58:44 2026
    From Newsgroup: comp.lang.c

    On 18/05/2026 18:47, Waldek Hebisch wrote:
    David Brown <david.brown@hesbynett.no> wrote:

    Sure. But in the C world, pre-C99 code is often written in a style with
    local variables all declared at the top of the function - after C99, it
    is common to declare them when you need them. So as a C programmer, I
    see declaring local variables in one clump together as archaic.

    I agree about style. But AFAICS even in very early C there is block structure and one can put variable declarations in the middle of
    sequence, just at the cost of introducing extra blocks. So C99
    looks nicer as there is no need for extra blocks, but pre C99
    already one could keep scopes tight and initialize variables in
    declarations.


    Certainly you can do that. But it quickly gets ugly and inconvenient if
    you have a lot of extra blocks. It's fair enough if you have a block
    already, from a loop or conditional.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Mon May 18 18:35:04 2026
    From Newsgroup: comp.lang.c

    On 18/05/2026 09:35, David Brown wrote:
    On 18/05/2026 09:22, Janis Papanagnou wrote:
    On 2026-05-18 08:56, David Brown wrote:
    [...]

    Much better, IMHO, is to use a language that lets you mix
    declarations and statements as needed.

    Indeed. But not "mixing" as a value per se, but to keep declarations
    locally is a good thing, IMO.


    Yes - it is not the mixing itself that is good, it is what it allows.
    You get to keep your scopes small, you don't need to declare variables
    until you have an initial value for them (who cares if reading an uninitialised variable is UB if you never have them!), and you can often declare your variables as const.  That means it's easy to know what the variable holds because it is only set once, and never changed.

    I see declaring your local variables in a list at the top of a
    function - or, far worse, at the bottom - as archaic style.

    Well, "archaic" expresses a time-related qualification. But even in
    earlier times we saw, depending on the actual programming language,
    both styles existing.

    Sure.  But in the C world, pre-C99 code is often written in a style with local variables all declared at the top of the function - after C99, it
    is common to declare them when you need them.  So as a C programmer, I
    see declaring local variables in one clump together as archaic.

    I hate dealing with code that just declares variables higgledy-piggledy
    all over the place; it is so lazy. I'm not into block scopes either.

    I /like/ to have them all in one place for easy reference. Then the code itself will have less clutter.

    There are several places in C where it is common to declare stuff in one place:

    * In parameter lists (at least we always have that, except when
    parameter 'x' is used, and you see 'x' in the body, your practice means
    I have to scan backwards from that point to see if it is parameter 'x',
    or some other 'x' in a nested scope that shadows it).

    * #includes
    * Imported variables
    * Imported functions
    * Forward function declarations

    Imagine how annoying it would be if any of those last were used inside a
    block within a function. Well, that's how annoying local declarations
    mixed with code are.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.lang.c on Mon May 18 18:13:55 2026
    From Newsgroup: comp.lang.c

    In article <10uffq4$m4se$1@paganini.bofh.team>,
    Waldek Hebisch <antispam@fricas.org> wrote:
    David Brown <david.brown@hesbynett.no> wrote:

    Sure. But in the C world, pre-C99 code is often written in a style with
    local variables all declared at the top of the function - after C99, it
    is common to declare them when you need them. So as a C programmer, I
    see declaring local variables in one clump together as archaic.

    I agree about style. But AFAICS even in very early C there is block >structure and one can put variable declarations in the middle of
    sequence, just at the cost of introducing extra blocks. [snip]

    Maybe. But on projects based on older variants of C, it was
    common as a matter of local policy to mandate that locals were
    declared at the top of a function; this enabled readers to get
    a sense of how much stack space was required at a glance.

    Fortunately, those bad days old are _mostly_ gone.

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Mon May 18 18:37:34 2026
    From Newsgroup: comp.lang.c

    cross@spitfire.i.gajendra.net (Dan Cross) writes:
    In article <10uffq4$m4se$1@paganini.bofh.team>,
    Waldek Hebisch <antispam@fricas.org> wrote:
    David Brown <david.brown@hesbynett.no> wrote:

    Sure. But in the C world, pre-C99 code is often written in a style with >>> local variables all declared at the top of the function - after C99, it >>> is common to declare them when you need them. So as a C programmer, I
    see declaring local variables in one clump together as archaic.

    I agree about style. But AFAICS even in very early C there is block >>structure and one can put variable declarations in the middle of
    sequence, just at the cost of introducing extra blocks. [snip]

    Maybe. But on projects based on older variants of C, it was
    common as a matter of local policy to mandate that locals were
    declared at the top of a function; this enabled readers to get
    a sense of how much stack space was required at a glance.

    That mandate also facilitated the use of the vi '[['
    sequence to easily move to the local declarations,
    and '``' would return to the previous location.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Mon May 18 21:24:35 2026
    From Newsgroup: comp.lang.c

    On 18/05/2026 19:35, Bart wrote:
    On 18/05/2026 09:35, David Brown wrote:
    On 18/05/2026 09:22, Janis Papanagnou wrote:
    On 2026-05-18 08:56, David Brown wrote:
    [...]

    Much better, IMHO, is to use a language that lets you mix
    declarations and statements as needed.

    Indeed. But not "mixing" as a value per se, but to keep declarations
    locally is a good thing, IMO.


    Yes - it is not the mixing itself that is good, it is what it allows.
    You get to keep your scopes small, you don't need to declare variables
    until you have an initial value for them (who cares if reading an
    uninitialised variable is UB if you never have them!), and you can
    often declare your variables as const.  That means it's easy to know
    what the variable holds because it is only set once, and never changed.

    I see declaring your local variables in a list at the top of a
    function - or, far worse, at the bottom - as archaic style.

    Well, "archaic" expresses a time-related qualification. But even in
    earlier times we saw, depending on the actual programming language,
    both styles existing.

    Sure.  But in the C world, pre-C99 code is often written in a style
    with local variables all declared at the top of the function - after
    C99, it is common to declare them when you need them.  So as a C
    programmer, I see declaring local variables in one clump together as
    archaic.

    I hate dealing with code that just declares variables higgledy-piggledy
    all over the place; it is so lazy. I'm not into block scopes either.

    Um, okay. You seem to define a lot of your programming life around
    hating things a large proportion of programmers consider good practice.
    But you are free to have your preferences.


    I /like/ to have them all in one place for easy reference. Then the code itself will have less clutter.

    There are several places in C where it is common to declare stuff in one place:

    * In parameter lists (at least we always have that, except when
    parameter 'x' is used, and you see 'x' in the body, your practice means
    I have to scan backwards from that point to see if it is parameter 'x',
    or some other 'x' in a nested scope that shadows it).

    That's a typical (for you) strawman argument. I don't know of anyone
    who considers it good practice to shadow function parameters with local variables.


    * #includes

    It is common in most languages to use includes, imports, uses, or
    whatever "module import" mechanism the language supports, near the top
    of source files.

    * Imported variables
    * Imported functions

    I'm not sure what you are trying to say here. People tend to group
    things that logically hang together - though there is often more than
    one way to do so.


    * Forward function declarations

    Imagine how annoying it would be if any of those last were used inside a block within a function. Well, that's how annoying local declarations
    mixed with code are.


    Some people do like to declare imported (i.e., "extern") functions or
    data within the functions that use them. I don't, but different people
    have different preferences.

    I appreciate that /you/ find declarations after statements to be
    annoying - yet you sing the praises of your own language because it
    allows declarations to occur anywhere mixed with statements, including
    after their usage. I suppose it is annoying when someone else does it,
    yet innovative and god's gift to the programming world when /you/ do it.

    People who mix declarations and statements in their C programming do so knowing full well that they could put their local variable declarations
    all at the top of the function - but they feel that mixing them gives
    the best quality code, with the least risk of mistakes and the clearest,
    most understandable and most maintainable results.

    Or perhaps they only do so specifically to annoy you.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Mon May 18 21:48:49 2026
    From Newsgroup: comp.lang.c

    On 18/05/2026 20:24, David Brown wrote:
    On 18/05/2026 19:35, Bart wrote:

    I hate dealing with code that just declares variables higgledy-
    piggledy all over the place; it is so lazy. I'm not into block scopes
    either.

    Um, okay.  You seem to define a lot of your programming life around
    hating things a large proportion of programmers consider good practice.
    But you are free to have your preferences.

    I have to deal with C quite a lot.

    People who mix declarations and statements in their C programming do so knowing full well that they could put their local variable declarations
    all at the top of the function - but they feel that mixing them gives
    the best quality code, with the least risk of mistakes and the clearest, most understandable and most maintainable results.

    Or perhaps they only do so specifically to annoy you.

    Every time I do a survey of C codebases, I tend to get about the same
    set of results:

    * On average functions have only around three local variables

    * 80% of all functions have 4 locals or fewer

    I don't find such stats a compelling reason for block scopes, or why
    people want to hide a function's handful of locals within the executable
    code.

    (Stats vary but for STB_IMAGE.H, a library I use, and tested after I
    wrote the above, they are:

    Number of Functions: 207
    Average Locals: 3.4
    Funcs with <= 4 Locals: 170 (82%)
    Most common # of locals: 1 (57/207; next most common is 0: 56/207)
    )


    I appreciate that /you/ find declarations after statements to be
    annoying - yet you sing the praises of your own language because it
    allows declarations to occur anywhere mixed with statements, including
    after their usage. I suppose it is annoying when someone else does it,
    yet innovative and god's gift to the programming world when /you/ do it.

    Yes I allow them anywhere, but 99% of the time in a finished program
    they are at the top of a function. Mostly they are used for temporary code.

    (If other people were to use my language, they may do things
    differently. I would still find that annoying if I had to deal with it.

    Remember that I don't have block scopes, so if I see a declaration in
    the middle, I know that applies function-wide.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Mon May 18 14:12:35 2026
    From Newsgroup: comp.lang.c

    Bart <bc@freeuk.com> writes:
    [...]
    You know, all it takes is to admit that out-of-order definitions can
    be a desirable and convenient language feature. It's one that other
    languages have, it's not something that only exists in my language
    (though I take it a bit further).

    I acknowledge that out-of-order definitions *can* be a a desirable
    and convenient language feature. (I used the word "acknowledge"
    because it's not an admission. It doesn't contradict anything I've
    said before, or as far as I can tell anything anyone else here has
    said before.)

    You said that's all it takes. What do we win?
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Mon May 18 14:23:03 2026
    From Newsgroup: comp.lang.c

    Bart <bc@freeuk.com> writes:
    [...]
    I hate dealing with code that just declares variables
    higgledy-piggledy all over the place; it is so lazy. I'm not into
    block scopes either.

    I would probably hate such code myself. Nobody advocates just
    declarating variables "higgledy-piggledy all over the place".

    Something that is widely considered good programming style is
    to define variables close to where they're used. In a medium to
    large sized function, that can result in something that you might
    perceive as "higgledy-piggled", but it's not. I'd explain further
    if I thought you'd be interested.

    Yes, it's less of an issue if functions are short.

    I /like/ to have them all in one place for easy reference. Then the
    code itself will have less clutter.

    I acknowledge your preference. Others do not share it.

    [...]
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Mon May 18 14:40:24 2026
    From Newsgroup: comp.lang.c

    Bart <bc@freeuk.com> writes:
    [...]
    I think you would have liked my language! That does have char
    constants up to 'ABCDEFGH' (When I supported 128-bit types, up to 'ABCDEFGHIJKLMNOP'.)

    C will support them up to 'int' width, which means 'ABCD' for 32-bit
    int. In theory a C with a 64-bit int would allow longer constants, but
    that's not going to happen on any common platforms.

    C does not specify the maximum width of a multi-character constant.

    As it is, even anything above 'A' is implementation-defined, unless
    C23 has changed that.

    It hasn't. For that matter, the value of 'A' is
    implementation-defined. (It's 65 on most implementations these days,
    but EBCDIC isn't completely dead.)

    Multi-character constants are largely a legacy feature, though the
    standard doesn't say so. I've seen code that uses them and depends
    on the values specified by a particular implementation, but they're
    of little use in portable code. It's not even guaranteed that
    'aa' and 'ab' have distinct values (Pelles C ignores all but the
    first character).

    also annoying is thet if {} has its own scope
    [...]

    annoying are also things that are not in c like "int x, y =
    foo(2,3);" but those dont belong to that list

    Huh? This is legal C.

    It is, but perhaps fir wanted both x and y to be initialized.
    Of course you could write
    int x = foo(2, 3);
    int y = x;
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Mon May 18 16:31:28 2026
    From Newsgroup: comp.lang.c

    On 5/18/2026 12:02 AM, David Brown wrote:
    On 18/05/2026 01:30, Lawrence D’Oliveiro wrote:
    On Sun, 17 May 2026 14:43:28 -0000 (UTC), Lew Pitcher wrote:

    Bart, in reality, a smart developer almost never has to "split one
    source file up into two or more". Instead, they /plan/ for isolation
    and encapsulation of the functional parts of their code, and
    /intentionally/ develop multiple source files from the start. That's
    the way professionals do it.

    Meanwhile, back in the real world, programs evolve and need to adapt
    to changes in the problems they were written to solve.

    What starts out as one source file might later grow to two or more.
    For instance, I start out with one program for performing the main
    functions of my time-and-billing system, then later discover the need
    for additional utilities driven by the same config settings: so this
    necessitates breaking out some common routines into a new module that
    is common to all the executables.

    It’s called “refactoring”.

    Yes, I think some people have been overstating their claims that they
    rarely or never split a source file into two or more parts.

    Fwiw, I have split source files apart in the past in my personal code,
    and its not all that difficult and once your done, everything still
    works. But, its more modular. You can create one single source file that
    is already modular in nature. That alone makes the task oh so much easier.

      Certainly
    with good planing you reduce the likelihood of having to split code
    later, but it's rare that you start a project with a clear enough specification and that never changes during the lifetime of the code.

    Right! For some experimental code I take note of the lines in a single
    file, take note of the modular setup, and say. okay, this is getting
    pretty big. its time to separate these. Iiirc, MISRA has a limit on the
    number of lines in a function?




    But I also think Bart is wildly overstating his claims of how much of an effort it is.  Usually it's just something you do, without needing much extra effort or risk - the thought and planning effort going in to how
    you are doing your re-structure is the time-consuming part, not the mechanical copy-and-paste or adding a couple of extern declarations to a
    new header.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From gazelle@gazelle@shell.xmission.com (Kenny McCormack) to comp.lang.c on Tue May 19 05:48:01 2026
    From Newsgroup: comp.lang.c

    In article <10ufgg4$2l9ge$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    ...
    I agree about style. But AFAICS even in very early C there is block
    structure and one can put variable declarations in the middle of
    sequence, just at the cost of introducing extra blocks. So C99
    looks nicer as there is no need for extra blocks, but pre C99
    already one could keep scopes tight and initialize variables in
    declarations.

    Certainly you can do that. But it quickly gets ugly and inconvenient if
    you have a lot of extra blocks. It's fair enough if you have a block >already, from a loop or conditional.

    I have always liked this feature of C - that you can create an "extra"
    block anywhere and then have locals declared there, like this:

    ...
    puts("This is regular code");
    {
    int i = 10;
    ...
    }
    puts("More code here");
    ...

    This is useful if you are modifying code you didn't write and don't
    understand, but you just want to add some new feature (and declare some variables for your own use), without disturbing (i.e., conflicting with) anything that is already there. And, as noted, this works in all versions
    of C, going back ...
    --
    Christianity is not a religion.

    - Rick C Hodgin -

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From gazelle@gazelle@shell.xmission.com (Kenny McCormack) to comp.lang.c on Tue May 19 05:49:41 2026
    From Newsgroup: comp.lang.c

    In article <10ufkt3$aqd$1@reader1.panix.com>,
    Dan Cross <cross@spitfire.i.gajendra.net> wrote:
    ...
    Maybe. But on projects based on older variants of C, it was
    common as a matter of local policy to mandate that locals were
    declared at the top of a function; this enabled readers to get
    a sense of how much stack space was required at a glance.

    Oh oh. You said "The S word". Leader Keith is not going to like this.
    --
    I love the poorly educated.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Tue May 19 08:18:10 2026
    From Newsgroup: comp.lang.c

    On 19/05/2026 07:48, Kenny McCormack wrote:
    In article <10ufgg4$2l9ge$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    ...
    I agree about style. But AFAICS even in very early C there is block
    structure and one can put variable declarations in the middle of
    sequence, just at the cost of introducing extra blocks. So C99
    looks nicer as there is no need for extra blocks, but pre C99
    already one could keep scopes tight and initialize variables in
    declarations.

    Certainly you can do that. But it quickly gets ugly and inconvenient if
    you have a lot of extra blocks. It's fair enough if you have a block
    already, from a loop or conditional.

    I have always liked this feature of C - that you can create an "extra"
    block anywhere and then have locals declared there, like this:

    ...
    puts("This is regular code");
    {
    int i = 10;
    ...
    }
    puts("More code here");
    ...

    This is useful if you are modifying code you didn't write and don't understand, but you just want to add some new feature (and declare some variables for your own use), without disturbing (i.e., conflicting with) anything that is already there. And, as noted, this works in all versions
    of C, going back ...


    I agree it can sometimes be useful. It is not often needed - especially
    in C99 onwards - but sometimes it can be the neatest way to structure code.

    I think probably the most common situation where I create "extra" blocks
    would be in switch cases. It lets you pretend that "switch" is more structured than it really is.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Tue May 19 06:58:07 2026
    From Newsgroup: comp.lang.c

    On Mon, 18 May 2026 18:35:04 +0100, Bart wrote:

    I hate dealing with code that just declares variables
    higgledy-piggledy all over the place; it is so lazy. I'm not into
    block scopes either.

    Tight control over scope reduces the chances of unexpected
    interactions across different parts of the code.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Tue May 19 09:18:55 2026
    From Newsgroup: comp.lang.c

    On 2026-05-19 01:31, Chris M. Thomasson wrote:

    [...] For some experimental code I take note of the lines in a single
    file, take note of the modular setup, and say. okay, this is getting
    pretty big. its time to separate these. Iiirc, MISRA has a limit on the number of lines in a function?

    (I don't know about MISRA.)

    Determining such numbers to get characteristics of source code can
    certainly be useful.

    Limiting these numbers (and rejecting such code), OTOH, appears to
    me to be quite stupid.

    Janis

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Tue May 19 10:00:21 2026
    From Newsgroup: comp.lang.c

    On 19/05/2026 09:18, Janis Papanagnou wrote:
    On 2026-05-19 01:31, Chris M. Thomasson wrote:

    [...] For some experimental code I take note of the lines in a single
    file, take note of the modular setup, and say. okay, this is getting
    pretty big. its time to separate these. Iiirc, MISRA has a limit on
    the number of lines in a function?

    (I don't know about MISRA.)

    Determining such numbers to get characteristics of source code can
    certainly be useful.

    Limiting these numbers (and rejecting such code), OTOH, appears to
    me to be quite stupid.


    I don't recall reading a rule in MISRA that limits the length of a
    function. But there are a number of different editions of the MISRA
    coding standards.

    Generally, it is a good idea to keep functions fairly short. But
    sometimes it is clearer to have a longer function than to break it into
    many small functions. And occasionally you come across something that
    is best handled as one very large function, if the structure is regular
    and clear (such as a large switch statement). Guidelines about sizes
    can be a good idea, but fixed rules rarely are.

    (MISRA compliance does allow for many of its rules and guidelines to be broken, if appropriately commented and justified.)

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Tue May 19 10:34:25 2026
    From Newsgroup: comp.lang.c

    On 2026-05-19 10:00, David Brown wrote:
    On 19/05/2026 09:18, Janis Papanagnou wrote:
    On 2026-05-19 01:31, Chris M. Thomasson wrote:

    [...] For some experimental code I take note of the lines in a single
    file, take note of the modular setup, and say. okay, this is getting
    pretty big. its time to separate these. Iiirc, MISRA has a limit on
    the number of lines in a function?

    (I don't know about MISRA.)

    Determining such numbers to get characteristics of source code can
    certainly be useful.

    Limiting these numbers (and rejecting such code), OTOH, appears to
    me to be quite stupid.

    I don't recall reading a rule in MISRA that limits the length of a function.  But there are a number of different editions of the MISRA
    coding standards.

    Generally, it is a good idea to keep functions fairly short.

    Actually, the first time I've seen such a formulation was from
    the context of "C". (Maybe even from K&R ? - Don't recall.)

    I'm coming more from the camp saying that plain numbers of lines
    are per se not an asset, not considering that a primary criterion
    for programming. (Large functions may or may not be an indication
    for bad style or bad software design, as short functions may be or
    not, likewise.)

    There's a lot more dimensions to organize code (classes, modules)
    and other factors like how broad an interface is, depth of calls,
    structures, function signatures, etc. - you can continue that list
    ad nauseam. - There's tools that may provide hints based on such
    criteria.

    But
    sometimes it is clearer to have a longer function than to break it into
    many small functions.  And occasionally you come across something that
    is best handled as one very large function, if the structure is regular
    and clear (such as a large switch statement).  Guidelines about sizes
    can be a good idea, but fixed rules rarely are.

    (MISRA compliance does allow for many of its rules and guidelines to be broken, if appropriately commented and justified.)

    We've invented such guidelines as well, back then. Making sensible
    rules, forcing some, suggesting others, and demand to comment and
    justify any deviations. - Yeah, it's nice to see that established
    procedures and practices mostly seem to be handled sensibly.

    Janis

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Tue May 19 11:55:18 2026
    From Newsgroup: comp.lang.c

    On 19/05/2026 07:58, Lawrence D’Oliveiro wrote:
    On Mon, 18 May 2026 18:35:04 +0100, Bart wrote:

    I hate dealing with code that just declares variables
    higgledy-piggledy all over the place; it is so lazy. I'm not into
    block scopes either.

    Tight control over scope reduces the chances of unexpected
    interactions across different parts of the code.

    At the same time, it makes it harder to see what interactions there
    might be, because you don't know whether the 'x' appearing here is the
    same one used earlier and/or the one used later.

    You need to very carefully trace the declaration for each instance.

    And also (this is what I hate), you have a bloody great declaration
    right in the middle of what would have been your nice clean code.

    There is also the odd way that scopes work:

    int a; // a1
    .... // a1 in scope
    {
    print(a); // a1 in scope
    double a; // a2
    print(a); // a2 in scope
    }
    .... // a1 in scope

    They don't need to be delimited by {...}; here that block has a1 in
    first half and a2 in second half, but both are 'a'.

    Here:
    ....
    double b = a, a;
    ....

    'b' is initialised from a1, then it declares a2, however since both a1
    and a2 are called 'a', it is a touch confusing!

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Richard Harnden@richard.nospam@gmail.invalid to comp.lang.c on Tue May 19 12:15:57 2026
    From Newsgroup: comp.lang.c

    On 19/05/2026 11:55, Bart wrote:
    On 19/05/2026 07:58, Lawrence D’Oliveiro wrote:
    On Mon, 18 May 2026 18:35:04 +0100, Bart wrote:

    I hate dealing with code that just declares variables
    higgledy-piggledy all over the place; it is so lazy. I'm not into
    block scopes either.

    Tight control over scope reduces the chances of unexpected
    interactions across different parts of the code.

    At the same time, it makes it harder to see what interactions there
    might be, because you don't know whether the 'x' appearing here is the
    same one used earlier and/or the one used later.

    You need to very carefully trace the declaration for each instance.

    And also (this is what I hate), you have a bloody great declaration
    right in the middle of what would have been your nice clean code.

    There is also the odd way that scopes work:

        int a;             // a1
        ....               // a1 in scope
        {
           print(a);       // a1 in scope
           double a;       // a2
           print(a);       // a2 in scope
        }
        ....               // a1 in scope

    They don't need to be delimited by {...}; here that block has a1 in
    first half and a2 in second half, but both are 'a'.

    Yes they do. How else does 'a' stop being a2 and revert to a1?


    Here:
       ....
       double b = a, a;
       ....

    'b' is initialised from a1, then it declares a2, however since both a1
    and a2 are called 'a', it is a touch confusing!

    So, don't do that.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Tue May 19 13:29:15 2026
    From Newsgroup: comp.lang.c

    On 19/05/2026 12:55, Bart wrote:
    On 19/05/2026 07:58, Lawrence D’Oliveiro wrote:
    On Mon, 18 May 2026 18:35:04 +0100, Bart wrote:

    I hate dealing with code that just declares variables
    higgledy-piggledy all over the place; it is so lazy. I'm not into
    block scopes either.

    Tight control over scope reduces the chances of unexpected
    interactions across different parts of the code.

    At the same time, it makes it harder to see what interactions there
    might be, because you don't know whether the 'x' appearing here is the
    same one used earlier and/or the one used later.

    With small scopes, the definition of "x" is likely to be very close to
    where you are using it - so there is much less to search.

    No one is suggesting writing huge functions with multiple re-uses of the
    same variable name in different scopes, with lots of code between
    declarations and uses. You are imagining problems that don't exist in
    most well-written code.

    And you are pretending that potential issues with finding the right declaration applies specially when declarations are small and local in
    scope - ignoring that without small scope declarations you often have to
    look further to find declarations, and that variables are not
    necessarily declared in the function at all.


    You need to very carefully trace the declaration for each instance.

    And also (this is what I hate), you have a bloody great declaration
    right in the middle of what would have been your nice clean code.


    Seriously? You call /that/ an argument?

    There is also the odd way that scopes work:

        int a;             // a1
        ....               // a1 in scope
        {
           print(a);       // a1 in scope
           double a;       // a2
           print(a);       // a2 in scope
        }
        ....               // a1 in scope


    It's not odd, it is perfectly clear and logical. Within a new scope, a
    new declaration shadows the previous one. It would only be difficult if
    you have the daft idea of allowing declarations to come after usage and
    affect the whole function, so that there is no top-to-bottom ordering.

    They don't need to be delimited by {...}; here that block has a1 in
    first half and a2 in second half, but both are 'a'.

    Here:
       ....
       double b = a, a;
       ....

    'b' is initialised from a1, then it declares a2, however since both a1
    and a2 are called 'a', it is a touch confusing!


    Thus nobody writes such code.

    In the great majority of cases, when you declare variables in between statements, you do so with an initialiser and you do so one variable at
    a time.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Tue May 19 12:48:15 2026
    From Newsgroup: comp.lang.c

    On 19/05/2026 12:15, Richard Harnden wrote:
    On 19/05/2026 11:55, Bart wrote:
    On 19/05/2026 07:58, Lawrence D’Oliveiro wrote:
    On Mon, 18 May 2026 18:35:04 +0100, Bart wrote:

    I hate dealing with code that just declares variables
    higgledy-piggledy all over the place; it is so lazy. I'm not into
    block scopes either.

    Tight control over scope reduces the chances of unexpected
    interactions across different parts of the code.

    At the same time, it makes it harder to see what interactions there
    might be, because you don't know whether the 'x' appearing here is the
    same one used earlier and/or the one used later.

    You need to very carefully trace the declaration for each instance.

    And also (this is what I hate), you have a bloody great declaration
    right in the middle of what would have been your nice clean code.

    There is also the odd way that scopes work:

         int a;             // a1
         ....               // a1 in scope
         {
            print(a);       // a1 in scope
            double a;       // a2
            print(a);       // a2 in scope
         }
         ....               // a1 in scope

    They don't need to be delimited by {...}; here that block has a1 in
    first half and a2 in second half, but both are 'a'.

    Yes they do.  How else does 'a' stop being a2 and revert to a1?

    a1 turned into a2 without crossing '{'.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Tue May 19 14:23:17 2026
    From Newsgroup: comp.lang.c

    On 19/05/2026 13:48, Bart wrote:
    On 19/05/2026 12:15, Richard Harnden wrote:
    On 19/05/2026 11:55, Bart wrote:
    On 19/05/2026 07:58, Lawrence D’Oliveiro wrote:
    On Mon, 18 May 2026 18:35:04 +0100, Bart wrote:

    I hate dealing with code that just declares variables
    higgledy-piggledy all over the place; it is so lazy. I'm not into
    block scopes either.

    Tight control over scope reduces the chances of unexpected
    interactions across different parts of the code.

    At the same time, it makes it harder to see what interactions there
    might be, because you don't know whether the 'x' appearing here is
    the same one used earlier and/or the one used later.

    You need to very carefully trace the declaration for each instance.

    And also (this is what I hate), you have a bloody great declaration
    right in the middle of what would have been your nice clean code.

    There is also the odd way that scopes work:

         int a;             // a1
         ....               // a1 in scope
         {
            print(a);       // a1 in scope
            double a;       // a2
            print(a);       // a2 in scope
         }
         ....               // a1 in scope

    They don't need to be delimited by {...}; here that block has a1 in
    first half and a2 in second half, but both are 'a'.

    Yes they do.  How else does 'a' stop being a2 and revert to a1?

    a1 turned into a2 without crossing '{'.


    It did so at the new declaration of "a". How could that possibly be surprising or "odd" ?

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Tue May 19 14:08:19 2026
    From Newsgroup: comp.lang.c

    On 19/05/2026 13:23, David Brown wrote:
    On 19/05/2026 13:48, Bart wrote:
    On 19/05/2026 12:15, Richard Harnden wrote:
    On 19/05/2026 11:55, Bart wrote:
    On 19/05/2026 07:58, Lawrence D’Oliveiro wrote:
    On Mon, 18 May 2026 18:35:04 +0100, Bart wrote:

    I hate dealing with code that just declares variables
    higgledy-piggledy all over the place; it is so lazy. I'm not into
    block scopes either.

    Tight control over scope reduces the chances of unexpected
    interactions across different parts of the code.

    At the same time, it makes it harder to see what interactions there
    might be, because you don't know whether the 'x' appearing here is
    the same one used earlier and/or the one used later.

    You need to very carefully trace the declaration for each instance.

    And also (this is what I hate), you have a bloody great declaration
    right in the middle of what would have been your nice clean code.

    There is also the odd way that scopes work:

         int a;             // a1
         ....               // a1 in scope
         {
            print(a);       // a1 in scope
            double a;       // a2
            print(a);       // a2 in scope
         }
         ....               // a1 in scope

    They don't need to be delimited by {...}; here that block has a1 in
    first half and a2 in second half, but both are 'a'.

    Yes they do.  How else does 'a' stop being a2 and revert to a1?

    a1 turned into a2 without crossing '{'.


    It did so at the new declaration of "a".  How could that possibly be surprising or "odd" ?


    How could it possibly not be?!

    {...} is a block in C, and C has block scopes. Given that, people might
    expect a scope to be delimited by the enclosing spaces.

    But names don't come into existence until declared, which can be in the
    middle of a block. Until then, the outer version of 'a' is still in scope.

    The point at each that happens, in a busy section of code with lots of declarations, can be unclear, with overlaps:

    double a;
    .... // a2 b1 c1
    double b;
    .... // a2 b2 c1
    double c;
    .... // a2 b2 c2

    These all shadow a, b, c in an outer scope, but at different times.

    The simplest scoping rule in C is for labels: they have function-wide
    scope, regardless of block nesting label.

    That would almost be as simple as how my own scopes work, except C just
    has to still be quirky:

    double c; c: goto c;

    (Labels of course have their own namespace, for some obscure reason. I'm
    sure 99% of C programmers don't know that.)
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Tue May 19 15:40:13 2026
    From Newsgroup: comp.lang.c

    On 19/05/2026 15:08, Bart wrote:
    On 19/05/2026 13:23, David Brown wrote:
    On 19/05/2026 13:48, Bart wrote:
    On 19/05/2026 12:15, Richard Harnden wrote:
    On 19/05/2026 11:55, Bart wrote:
    On 19/05/2026 07:58, Lawrence D’Oliveiro wrote:
    On Mon, 18 May 2026 18:35:04 +0100, Bart wrote:

    I hate dealing with code that just declares variables
    higgledy-piggledy all over the place; it is so lazy. I'm not into >>>>>>> block scopes either.

    Tight control over scope reduces the chances of unexpected
    interactions across different parts of the code.

    At the same time, it makes it harder to see what interactions there >>>>> might be, because you don't know whether the 'x' appearing here is
    the same one used earlier and/or the one used later.

    You need to very carefully trace the declaration for each instance.

    And also (this is what I hate), you have a bloody great declaration >>>>> right in the middle of what would have been your nice clean code.

    There is also the odd way that scopes work:

         int a;             // a1
         ....               // a1 in scope
         {
            print(a);       // a1 in scope
            double a;       // a2
            print(a);       // a2 in scope
         }
         ....               // a1 in scope

    They don't need to be delimited by {...}; here that block has a1 in >>>>> first half and a2 in second half, but both are 'a'.

    Yes they do.  How else does 'a' stop being a2 and revert to a1?

    a1 turned into a2 without crossing '{'.


    It did so at the new declaration of "a".  How could that possibly be
    surprising or "odd" ?


    How could it possibly not be?!

    {...} is a block in C, and C has block scopes. Given that, people might expect a scope to be delimited by the enclosing spaces.

    People expect scopes to begin with a declaration, and end at the end of
    the block (or file, for file-scope identifiers). This is the normal
    handling of scoping in most imperative programming languages. There are
    some languages (such as Python) where scopes begin when a variable is
    first assigned, and continue to the end of the function rather than the
    block. And declarative programming languages may do things differently
    - they do many things differently.

    Languages may differ in the details - given "int a = foo(a);", for
    example, the scope of the new "a" in C begins after "int a" - in some languages, it may not begin until the end of the semicolon. But that's
    a small detail, rarely relevant in real code. (For the record, I'd have preferred if the scope of variables in C did not begin until the end of
    the declaration / definition.)

    Can you give me an example of a real-world programming language in which
    the scope of a local variable begins at the start of the enclosing
    block, rather than at its declaration / definition?

    Perhaps you are mixing up "scope" and "lifetime" ? These are not the
    same things.


    But names don't come into existence until declared, which can be in the middle of a block. Until then, the outer version of 'a' is still in scope.


    Yes. Scope is all about the names.

    The point at each that happens, in a busy section of code with lots of declarations, can be unclear, with overlaps:

       double a;
       ....       // a2 b1 c1
       double b;
       ....       // a2 b2 c1
       double c;
       ....       // a2 b2 c2

    These all shadow a, b, c in an outer scope, but at different times.

    Yes. (Although this is again a strawman argument - people don't
    normally write code that shadows outer scope variables.)


    The simplest scoping rule in C is for labels: they have function-wide
    scope, regardless of block nesting label.


    The rule for C labels exists because you have to be able to jump
    backwards and forwards for "goto" to be of much use. In C programming,
    labels (excluding case labels) are rarely used except sometimes for
    error handling. I don't think I've written "goto" in C more than a
    couple of times in my programming career.

    C labels are unstructured. This is not a good thing in a programming
    language - it is an unfortunate necessary evil. Other identifiers have clearer and simpler scoping - scope starts with the declaration, and
    ends with the end of the enclosing block.

    That would almost be as simple as how my own scopes work, except C just
    has to still be quirky:

       double c; c: goto c;

    (Labels of course have their own namespace, for some obscure reason. I'm sure 99% of C programmers don't know that.)

    Since we are pulling numbers out of the air, 99% of C programmers don't
    care. If any C programmer writes code where this is important, because
    they use the same identifier for a variable and a goto label, they
    should not be programming.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Tue May 19 13:56:21 2026
    From Newsgroup: comp.lang.c

    Bart <bc@freeuk.com> writes:
    On 19/05/2026 07:58, Lawrence D’Oliveiro wrote:
    On Mon, 18 May 2026 18:35:04 +0100, Bart wrote:

    I hate dealing with code that just declares variables
    higgledy-piggledy all over the place; it is so lazy. I'm not into
    block scopes either.

    Tight control over scope reduces the chances of unexpected
    interactions across different parts of the code.

    At the same time, it makes it harder to see what interactions there
    might be, because you don't know whether the 'x' appearing here is the
    same one used earlier and/or the one used later.

    The last major language that only allowed single character
    identifiers was early Basic.

    Declaring 'x' twice in the same C function (or even once!) is
    foolish.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Tue May 19 14:04:55 2026
    From Newsgroup: comp.lang.c

    David Brown <david.brown@hesbynett.no> writes:
    On 19/05/2026 07:48, Kenny McCormack wrote:
    In article <10ufgg4$2l9ge$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    ...
    I agree about style. But AFAICS even in very early C there is block
    structure and one can put variable declarations in the middle of
    sequence, just at the cost of introducing extra blocks. So C99
    looks nicer as there is no need for extra blocks, but pre C99
    already one could keep scopes tight and initialize variables in
    declarations.

    Certainly you can do that. But it quickly gets ugly and inconvenient if >>> you have a lot of extra blocks. It's fair enough if you have a block
    already, from a loop or conditional.

    I have always liked this feature of C - that you can create an "extra"
    block anywhere and then have locals declared there, like this:

    ...
    puts("This is regular code");
    {
    int i = 10;
    ...
    }
    puts("More code here");
    ...

    This is useful if you are modifying code you didn't write and don't
    understand, but you just want to add some new feature (and declare some
    variables for your own use), without disturbing (i.e., conflicting with)
    anything that is already there. And, as noted, this works in all versions >> of C, going back ...


    I agree it can sometimes be useful. It is not often needed - especially
    in C99 onwards - but sometimes it can be the neatest way to structure code.

    I think probably the most common situation where I create "extra" blocks >would be in switch cases. It lets you pretend that "switch" is more >structured than it really is.

    I often declare local variables within
    a compound statement introduced by a do/while/for/case construct.

    In pre or post C99 code, I do not create compound statements willy-nilly just to hide a prior defined local variable with the same name (a maintenance nightmare, to be sure) or to locate the declaration closer to the use
    in a function.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Tue May 19 16:41:35 2026
    From Newsgroup: comp.lang.c

    On 2026-05-19 15:40, David Brown wrote:
    [...]

    Can you give me an example of a real-world programming language in which
    the scope of a local variable begins at the start of the enclosing
    block, rather than at its declaration / definition?

    If I'm asking Google it tells me that Python and Javascript (when
    using 'var') would be two prominent examples.

    Janis

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Tue May 19 16:46:45 2026
    From Newsgroup: comp.lang.c

    On 2026-05-19 13:29, David Brown wrote:
    On 19/05/2026 12:55, Bart wrote:

    At the same time, it makes it harder to see what interactions there
    might be, because you don't know whether the 'x' appearing here is the
    same one used earlier and/or the one used later.

    With small scopes, the definition of "x" is likely to be very close to
    where you are using it - so there is much less to search.

    Indeed.

    No one is suggesting writing huge functions with multiple re-uses of the same variable name in different scopes, with lots of code between declarations and uses.  You are imagining problems that don't exist in
    most well-written code.

    For sure.

    And you are pretending that potential issues with finding the right declaration applies specially when declarations are small and local in
    scope - ignoring that without small scope declarations you often have to look further to find declarations, and that variables are not
    necessarily declared in the function at all.

    Well, he'd run into a duplicate-declared-name error then. Or have
    one technical variable re-used for two different semantic cases.
    (I'm not sure what I consider worse in Bart's way of programming.)

    Janis

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Tue May 19 17:07:12 2026
    From Newsgroup: comp.lang.c

    On 19/05/2026 16:41, Janis Papanagnou wrote:
    On 2026-05-19 15:40, David Brown wrote:
    [...]

    Can you give me an example of a real-world programming language in
    which the scope of a local variable begins at the start of the
    enclosing block, rather than at its declaration / definition?

    If I'm asking Google it tells me that Python and Javascript (when
    using 'var') would be two prominent examples.

    Janis


    I don't know about Javascript, but it's wrong about Python.

    def foo() :
    print(a)
    a = 1
    print(a)

    "a" is not in scope until the line "a = 1". Try it and see.

    It is possible that "a" blocks access to a global "a" before it is
    declared - since programmers rarely intentionally shadow other
    variables, and even more rarely try to access variables before they are defined, it's not a situation that is going to turn up in real code.
    Such subtle details are best checked in a Python newsgroup.

    Note also that Python doesn't really have a concept of declaring a
    variable. You initialise a reference to an object, you don't declare a variable as such. (So "a = 1" here creates a constant integer object
    with the value 1 on the heap, or increases a reference count to an
    existing such object, and creates "a" as an identifier that references
    that object. "a" is not a variable in the sense used in compiled
    imperative languages.)

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Tue May 19 17:23:53 2026
    From Newsgroup: comp.lang.c

    On 2026-05-19 17:07, David Brown wrote:
    On 19/05/2026 16:41, Janis Papanagnou wrote:
    On 2026-05-19 15:40, David Brown wrote:
    [...]

    Can you give me an example of a real-world programming language in
    which the scope of a local variable begins at the start of the
    enclosing block, rather than at its declaration / definition?

    If I'm asking Google it tells me that Python and Javascript (when
    using 'var') would be two prominent examples.

    I don't know about Javascript,

    I did JS programming but never tried to put the declaration behind
    the point of use of the declared items.

    I mean that is the whole point; even if some language (for whatever
    reason) would allow that, why should a sane programmer make use of
    such obfuscation!

    but it's wrong about Python.

    I don't know the details of and also don't program in python.


    def foo() :
        print(a)
        a = 1
        print(a)

    "a" is not in scope until the line "a = 1".  Try it and see.

    $ python
    ksh93: python: not found

    I trust your words. :-)

    [snip]

    (I was just quoting search results. Never mind.)

    Janis

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Tue May 19 11:43:59 2026
    From Newsgroup: comp.lang.c

    On 2026-05-17 10:43, Lew Pitcher wrote:
    On Sun, 17 May 2026 06:48:06 -0700, Tim Rentsch wrote:

    Bart <bc@freeuk.com> writes:

    On 17/05/2026 02:21, Keith Thompson wrote:

    Bart <bc@freeuk.com> writes:
    ...
    I mentioned something like this a week ago, suggesting that in C it
    was harder work than necessary to split one source file up into two or >>>>> more.

    Bart, in reality, a smart developer almost never has to "split one source file up into two or more". Instead, they /plan/ for isolation and encapsulation
    of the functional parts of their code, and /intentionally/ develop
    multiple source files from the start. That's the way professionals do it.

    Possibly it's because I was working on mostly scientific software, but
    in my experience, changes in requirements are routine, with
    corresponding changes in the design. If scientists knew from the
    beginning exactly what they needed to do with the data they're
    collecting, they often wouldn't need to collect it. Understanding is a
    product of the scientific method, not a required input.
    Adding a new feature that interacts with existing features often
    requires splitting of source code files.

    And you offered no evidence for your claim, not even telling us
    that you had tried it and found it difficult.

    Everyone here will know what is involved. But nobody wants to admit
    that it can be onerous.

    It could be onerous. The point is, in actual practice it almost
    never is onerous.

    Having done it apparently more often than you do, I fully agree. The
    difficult work is implementing the new feature. Reorganizing the code to
    fit the new feature in requires some careful thought, but is in general
    not very onerous.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Tue May 19 17:58:42 2026
    From Newsgroup: comp.lang.c

    On 19/05/2026 17:23, Janis Papanagnou wrote:
    On 2026-05-19 17:07, David Brown wrote:
    On 19/05/2026 16:41, Janis Papanagnou wrote:
    On 2026-05-19 15:40, David Brown wrote:
    [...]

    Can you give me an example of a real-world programming language in
    which the scope of a local variable begins at the start of the
    enclosing block, rather than at its declaration / definition?

    If I'm asking Google it tells me that Python and Javascript (when
    using 'var') would be two prominent examples.

    I don't know about Javascript,

    I did JS programming but never tried to put the declaration behind
    the point of use of the declared items.

    I mean that is the whole point; even if some language (for whatever
    reason) would allow that, why should a sane programmer make use of
    such obfuscation!

    Agreed. I've done a little Javascript, but not enough to know if the
    language allows such constructions that I'd never imagine using.


    but it's wrong about Python.

    I don't know the details of and also don't program in python.


    def foo() :
         print(a)
         a = 1
         print(a)

    "a" is not in scope until the line "a = 1".  Try it and see.

    $ python
    ksh93: python: not found


    Way OT - but what kind of OS are you running that does not have Python installed? Even if you don't program in Python, there must surely be
    some Python programs on your system. (Of the 3900 files in my /usr/bin,
    some 211 of them are in Python. I don't program in Perl, but I have
    perl on my system for the 169 Perl programs in my /usr/bin.)

    Maybe you don't have it as "python", but as "python3" (or "python2") ?

    I trust your words. :-)

    [snip]

    (I was just quoting search results. Never mind.)


    Well, the challenge is really for Bart.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Tue May 19 17:31:21 2026
    From Newsgroup: comp.lang.c

    On 19/05/2026 14:40, David Brown wrote:
    On 19/05/2026 15:08, Bart wrote:

    {...} is a block in C, and C has block scopes. Given that, people
    might expect a scope to be delimited by the enclosing spaces.

    People expect scopes to begin with a declaration, and end at the end of
    the block (or file, for file-scope identifiers).  This is the normal handling of scoping in most imperative programming languages.  There are some languages (such as Python) where scopes begin when a variable is
    first assigned, and continue to the end of the function rather than the block.  And declarative programming languages may do things differently
    - they do many things differently.

    Languages may differ in the details - given "int a = foo(a);", for
    example, the scope of the new "a" in C begins after "int a" - in some languages, it may not begin until the end of the semicolon.  But that's
    a small detail, rarely relevant in real code.  (For the record, I'd have preferred if the scope of variables in C did not begin until the end of
    the declaration / definition.)

    Can you give me an example of a real-world programming language in which
    the scope of a local variable begins at the start of the enclosing
    block, rather than at its declaration / definition?

    In Algo68:

    BEGIN
    print((a, newline));
    INT a=777;
    print((a, newline));
    END

    This prints 0 then 777. If I comment out the declaration, it says 'a'
    has not been declared.

    Here, 'a' is a named constant; if I use a:=777 instead, which defines a variable, it says the first 'a' has not been initialised (I guess 'INT
    a' creates a reference to a local), a different error from not having
    the INT line at all.

    Now, you said real-world not mainstream, so all mine currently do that. Anything declared in a function body has function-wide scope, including
    to any depth of nested block.

    Outside of a function, it has module-wide scope. If also declared
    'global', it has sub-program-wide scope (although other modules can
    shadow it).

    Here, obviously, exactly where in a module a name has been defined, is irrelevant.

    In C, the same of thing happens when you introduced a forward
    declaration of a function: you move its lexical scope to near the top.
    In this case, it wouldn't be shadowing anything; there can't be anything
    at an outer scope. So there wouldn't be a problem in scope starting from
    the top of the file.

    Perhaps you are mixing up "scope" and "lifetime" ?  These are not the
    same things.

    I'm talking about static, lexical code, which can be determined at compile-time.


    But names don't come into existence until declared, which can be in
    the middle of a block. Until then, the outer version of 'a' is still
    in scope.


    Yes.  Scope is all about the names.

    The point at each that happens, in a busy section of code with lots of
    declarations, can be unclear, with overlaps:

        double a;
        ....       // a2 b1 c1
        double b;
        ....       // a2 b2 c1
        double c;
        ....       // a2 b2 c2

    These all shadow a, b, c in an outer scope, but at different times.

    Yes.  (Although this is again a strawman argument - people don't
    normally write code that shadows outer scope variables.)


    Funny you should say that:

    Lua sources: 190
    SQLite sources: 300
    Tiny C sources 30
    MZlib sources: 80

    The number shown is the number of times that the same local variable
    name occurs more than once in the same function, shadowing or not. I
    count only one instance per unique name per function.

    Maybe 10% of the time, types are different. In some cases, local
    variable exist that differ only in letter case (not counted above).

    (Note many of these arise from macro expansions.)


    The simplest scoping rule in C is for labels: they have function-wide
    scope, regardless of block nesting label.


    The rule for C labels exists because you have to be able to jump
    backwards and forwards for "goto" to be of much use.  In C programming, labels (excluding case labels) are rarely used except sometimes for
    error handling.  I don't think I've written "goto" in C more than a
    couple of times in my programming career.

    C labels are unstructured.  This is not a good thing in a programming language - it is an unfortunate necessary evil.

    A good thing in this case; imagine the same L23 label occurring half a
    dozen times in the same function, even if labels had block scopes.



    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Tue May 19 18:31:40 2026
    From Newsgroup: comp.lang.c

    On 2026-05-19 17:58, David Brown wrote:
    On 19/05/2026 17:23, Janis Papanagnou wrote:

    $ python
    ksh93: python: not found

    Way OT - but what kind of OS are you running that does not have Python installed?  Even if you don't program in Python, there must surely be
    some Python programs on your system.  (Of the 3900 files in my /usr/bin, some 211 of them are in Python.  I don't program in Perl, but I have
    perl on my system for the 169 Perl programs in my /usr/bin.)

    Maybe you don't have it as "python", but as "python3" (or "python2") ?

    Yes, I saw that 'man python' works, and there's also '/usr/bin/python3' existing.


    I'll certainly never use it!

    $ python3
    Python 3.12.3 (main, Mar 23 2026, 19:04:32) [GCC 13.3.0] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    help
    Type help() for interactive help, or help(object) for help about object.
    quit
    Use quit() or Ctrl-D (i.e. EOF) to exit



    Really?

    It first suggests to type "help" and then reports that I should type
    "help()" instead?

    It prints that I should type "quit()" instead of "quit", or Ctrl-D,
    but it will not _just do_ that quit.

    Is that contemporary software interface ergonomy? Sensible design?
    Useful interactive information? - I mean, we're obviously already
    in the 3rd major release, and there's still such quality exhibited.

    (I'm really getting angry if I encounter such interfaces.)


    Yes, way OT, as you said, and nothing I'd like to dispute about.

    Janis

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Tue May 19 12:35:50 2026
    From Newsgroup: comp.lang.c

    Bart <bc@freeuk.com> writes:
    [...]
    I hate dealing with code that just declares variables
    higgledy-piggledy all over the place; it is so lazy. I'm not into
    block scopes either.

    I hate code that declares variables higgledy piggledy too. I just
    disagree with you about which code qualifies for that description. I
    dislike code that lazily declares variables at the top. I greatly prefer
    code which declares each variable with the most restricted scope that
    allows it to perform its job (without creating scopes solely for that
    purpose), minimizing the potential for conflict with other variables. In particular, I prefer to have them declared by a variable definition that initializes the variable with a valid value that might get used, where possible. This is particularly useful if the delayed initialization of
    the variable allows me to initialize it with the only value it will ever
    hold during it's lifetime, allowing me to declare it "const", enabling
    warning messages if I ever try to change the value. If such messages
    come up, I will either have to remove the "const", or remove the change
    in value, whichever was the mistake.

    I /like/ to have them all in one place for easy reference. Then the
    code itself will have less clutter.

    I find code less cluttered when variables are defined close to place of
    first use.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Tue May 19 18:38:32 2026
    From Newsgroup: comp.lang.c

    On 19/05/2026 18:31, Janis Papanagnou wrote:
    On 2026-05-19 17:58, David Brown wrote:
    On 19/05/2026 17:23, Janis Papanagnou wrote:

    $ python
    ksh93: python: not found

    Way OT - but what kind of OS are you running that does not have Python
    installed?  Even if you don't program in Python, there must surely be
    some Python programs on your system.  (Of the 3900 files in my /usr/
    bin, some 211 of them are in Python.  I don't program in Perl, but I
    have perl on my system for the 169 Perl programs in my /usr/bin.)

    Maybe you don't have it as "python", but as "python3" (or "python2") ?

    Yes, I saw that 'man python' works, and there's also '/usr/bin/python3' existing.


    I'll certainly never use it!

    $ python3
    Python 3.12.3 (main, Mar 23 2026, 19:04:32) [GCC 13.3.0] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    help
    Type help() for interactive help, or help(object) for help about object.
    quit
    Use quit() or Ctrl-D (i.e. EOF) to exit



    Really?

    It first suggests to type "help" and then reports that I should type
    "help()" instead?

    It prints that I should type "quit()" instead of "quit", or Ctrl-D,
    but it will not _just do_ that quit.


    It does not sound unreasonable to me. It's a programming language.
    "quit" and "help" are functions. If you try to write 'printf "Hello
    world!" ' in C code, you would not expect the compiler to treat it as
    though you'd included the parentheses. But you'd be happy if the
    compiler gave you helpful hints in its error messages. It's the same here.

    Of course, Python could have had "quit" as a statement (Python 2 had
    "print" as a statement, and changed it to a function in Python 3 -
    backwards compatibility is not as important between Python major
    versions as in the C world). But given that it is a function, I expect
    to have to use function syntax to call it.

    (And just like a discussion on C, an explanation of why things are the
    way they are in Python is not necessarily an endorsement of any given
    design decision.)

    Is that contemporary software interface ergonomy? Sensible design?
    Useful interactive information? - I mean, we're obviously already
    in the 3rd major release, and there's still such quality exhibited.

    (I'm really getting angry if I encounter such interfaces.)


    Yes, way OT, as you said, and nothing I'd like to dispute about.

    Janis


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Tue May 19 17:44:12 2026
    From Newsgroup: comp.lang.c

    On 19/05/2026 16:07, David Brown wrote:
    On 19/05/2026 16:41, Janis Papanagnou wrote:
    On 2026-05-19 15:40, David Brown wrote:
    [...]

    Can you give me an example of a real-world programming language in
    which the scope of a local variable begins at the start of the
    enclosing block, rather than at its declaration / definition?

    If I'm asking Google it tells me that Python and Javascript (when
    using 'var') would be two prominent examples.

    Janis


    I don't know about Javascript, but it's wrong about Python.

    def foo() :
        print(a)
        a = 1
        print(a)

    "a" is not in scope until the line "a = 1".  Try it and see.

    It is possible that "a" blocks access to a global "a" before it is
    declared - since programmers rarely intentionally shadow other
    variables, and even more rarely try to access variables before they are defined, it's not a situation that is going to turn up in real code.
    Such subtle details are best checked in a Python newsgroup.

    Note also that Python doesn't really have a concept of declaring a variable.  You initialise a reference to an object, you don't declare a variable as such.  (So "a = 1" here creates a constant integer object
    with the value 1 on the heap, or increases a reference count to an
    existing such object, and creates "a" as an identifier that references
    that object.  "a" is not a variable in the sense used in compiled imperative languages.)


    In Lua:

    function fred()
    print(a)
    local a
    a=1
    print(a)
    end

    a=999
    fred()
    print(a)

    This outputs 999 1 999. So the global a is visible from that first
    'print' line; its scope must be start earlier than its initialisation.

    However these are both dynamic languages where you might find that that static-looking 'function' is really something executed at runtime. If I
    try the same in mine I get Void 1 999.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Tue May 19 18:48:24 2026
    From Newsgroup: comp.lang.c

    On 19/05/2026 18:31, Bart wrote:
    On 19/05/2026 14:40, David Brown wrote:
    On 19/05/2026 15:08, Bart wrote:

    {...} is a block in C, and C has block scopes. Given that, people
    might expect a scope to be delimited by the enclosing spaces.

    People expect scopes to begin with a declaration, and end at the end
    of the block (or file, for file-scope identifiers).  This is the
    normal handling of scoping in most imperative programming languages.
    There are some languages (such as Python) where scopes begin when a
    variable is first assigned, and continue to the end of the function
    rather than the block.  And declarative programming languages may do
    things differently - they do many things differently.

    Languages may differ in the details - given "int a = foo(a);", for
    example, the scope of the new "a" in C begins after "int a" - in some
    languages, it may not begin until the end of the semicolon.  But
    that's a small detail, rarely relevant in real code.  (For the record,
    I'd have preferred if the scope of variables in C did not begin until
    the end of the declaration / definition.)

    Can you give me an example of a real-world programming language in
    which the scope of a local variable begins at the start of the
    enclosing block, rather than at its declaration / definition?

    In Algo68:

       BEGIN
           print((a, newline));
           INT a=777;
           print((a, newline));
       END

    This prints 0 then 777. If I comment out the declaration, it says 'a'
    has not been declared.

    Here, 'a' is a named constant; if I use a:=777 instead, which defines a variable, it says the first 'a' has not been initialised (I guess 'INT
    a' creates a reference to a local), a different error from not having
    the INT line at all.

    OK. So there was a real language that worked that way, half a century ago.


    Now, you said real-world not mainstream, so all mine currently do that. Anything declared in a function body has function-wide scope, including
    to any depth of nested block.


    I don't count your language as "real world". But if you prefer the term "mainstream", I'll go along with that, and then skip your descriptions
    of it. Note that Algol68 is not mainstream either (though it once was).

    Outside of a function, it has module-wide scope. If also declared
    'global', it has sub-program-wide scope (although other modules can
    shadow it).

    Here, obviously, exactly where in a module a name has been defined, is irrelevant.

    In C, the same of thing happens when you introduced a forward
    declaration of a function: you move its lexical scope to near the top.

    Yes, C is exactly the same but for the minor detail of being completely different.

    In this case, it wouldn't be shadowing anything; there can't be anything
    at an outer scope. So there wouldn't be a problem in scope starting from
    the top of the file.

    Perhaps you are mixing up "scope" and "lifetime" ?  These are not the
    same things.

    I'm talking about static, lexical code, which can be determined at compile-time.

    Again, I think you are mixing up "scope" and "lifetime". Can you
    demonstrate that you understand the difference? It would be
    particularly nice if you could show you know the difference for local variables in C.

    C labels are unstructured.  This is not a good thing in a programming
    language - it is an unfortunate necessary evil.

    A good thing in this case; imagine the same L23 label occurring half a
    dozen times in the same function, even if labels had block scopes.


    Imagine, instead, that most people who program know how to write
    sensible code. Not everyone does, but most people do. Worrying about
    how badly people could misuse any given feature is not helpful, and of
    course there are endless ways to write terrible code using your language
    or any other language.

    If I told you I like the green colour of my car, you'd tell me that your
    car is red, and red is the only good colour for a car. After all, if
    green aliens hid my car in a field of broccoli and surrounded it with
    green frogs it would be hard to find, while your red car would be
    clearly visible.

    Please cut down on the inane straw man arguments.




    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Tue May 19 18:54:07 2026
    From Newsgroup: comp.lang.c

    On 2026-05-19 18:38, David Brown wrote:
    On 19/05/2026 18:31, Janis Papanagnou wrote:
    [ python3 interface stuff ]

    It does not sound unreasonable to me.  It's a programming language.

    It's an _interface_ not conforming to any basic interface standards
    I'd expect. (I was not discussing the language, and neither intend
    to discuss the Python language nor the interface of the Python tool.)

    Janis

    [...]

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Tue May 19 18:59:45 2026
    From Newsgroup: comp.lang.c

    On 19/05/2026 18:44, Bart wrote:
    On 19/05/2026 16:07, David Brown wrote:
    On 19/05/2026 16:41, Janis Papanagnou wrote:
    On 2026-05-19 15:40, David Brown wrote:
    [...]

    Can you give me an example of a real-world programming language in
    which the scope of a local variable begins at the start of the
    enclosing block, rather than at its declaration / definition?

    If I'm asking Google it tells me that Python and Javascript (when
    using 'var') would be two prominent examples.

    Janis


    I don't know about Javascript, but it's wrong about Python.

    def foo() :
         print(a)
         a = 1
         print(a)

    "a" is not in scope until the line "a = 1".  Try it and see.

    It is possible that "a" blocks access to a global "a" before it is
    declared - since programmers rarely intentionally shadow other
    variables, and even more rarely try to access variables before they
    are defined, it's not a situation that is going to turn up in real
    code. Such subtle details are best checked in a Python newsgroup.

    Note also that Python doesn't really have a concept of declaring a
    variable.  You initialise a reference to an object, you don't declare
    a variable as such.  (So "a = 1" here creates a constant integer
    object with the value 1 on the heap, or increases a reference count to
    an existing such object, and creates "a" as an identifier that
    references that object.  "a" is not a variable in the sense used in
    compiled imperative languages.)


    In Lua:

      function fred()
          print(a)
          local a
          a=1
          print(a)
      end

      a=999
      fred()
      print(a)

    This outputs 999 1 999. So the global a is visible from that first
    'print' line; its scope must be start earlier than its initialisation.

    However these are both dynamic languages where you might find that that static-looking 'function' is really something executed at runtime. If I
    try the same in mine I get Void 1 999.


    You have explained it yourself. Although Lua has another effect - any
    attempt to access a missing variable is considered a declaration of the variable with value "nil".

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Tue May 19 18:47:16 2026
    From Newsgroup: comp.lang.c

    On 19/05/2026 17:48, David Brown wrote:
    On 19/05/2026 18:31, Bart wrote:
    On 19/05/2026 14:40, David Brown wrote:

    Can you give me an example of a real-world programming language in
    which the scope of a local variable begins at the start of the
    enclosing block, rather than at its declaration / definition?

    In Algo68:

        BEGIN
            print((a, newline));
            INT a=777;
            print((a, newline));
        END

    This prints 0 then 777. If I comment out the declaration, it says 'a'
    has not been declared.

    Here, 'a' is a named constant; if I use a:=777 instead, which defines
    a variable, it says the first 'a' has not been initialised (I guess
    'INT a' creates a reference to a local), a different error from not
    having the INT line at all.

    OK.  So there was a real language that worked that way, half a century ago.

    While I don't care for it, a LOT of thought went into its design, by
    some very clever people.


    Now, you said real-world not mainstream, so all mine currently do
    that. Anything declared in a function body has function-wide scope,
    including to any depth of nested block.


    I don't count your language as "real world".  But if you prefer the term "mainstream", I'll go along with that, and then skip your descriptions
    of it.

    You would dismiss potentially good ideas for petty reasons?

    There is a fuzzy area in programming lanuages when you declare things in
    the middle of a block.

    You declare X in the middle, and its scope lasts until the end of the
    block. But that happens between the start of the block and its declaration?

    If this new X is not visible, then what happens if you try and use 'X'?

    Apparently in C, you just get whatever outer X happens to be visible
    from an outer scope. There are other choices, including hiding outer Xs
    but now allowing you to access this new X either.

    If you want the current behaviour, you can create a new {} block, then
    there are fewer surprises.

    In C, the same of thing happens when you introduced a forward
    declaration of a function: you move its lexical scope to near the top.

    Yes, C is exactly the same but for the minor detail of being completely different.

    So, having functions, variables, types and macro generally declared near
    the top of a source file, has no similarity with declaring local
    variables near the top of a function?

    OK..

    Perhaps you are mixing up "scope" and "lifetime" ?  These are not the
    same things.

    I'm talking about static, lexical [scope], which can be determined at
    compile-time.

    Again, I think you are mixing up "scope" and "lifetime".

    I think you are. I said this can be determined at /compile-time/. It is
    purely to do with visibility from a particular spot in source code.

      Can you
    demonstrate that you understand the difference?  It would be
    particularly nice if you could show you know the difference for local variables in C.

    Why don't you explain the difference?

    Imagine, instead, that most people who program know how to write
    sensible code.

    I suspect I've seen more reams of nightmare C source code than you have.


      Not everyone does, but most people do.  Worrying about
    how badly people could misuse any given feature is not helpful,

    People like to push languages to the limits. They like to show off and
    be clever. They will abuse any feature.

    and of
    course there are endless ways to write terrible code using your language
    or any other language.

    In my language they can't have have 64 variations of the same 'abcdef' identifier IN THE SAME SCOPE by varying case. They can't have unlimited unrelated instances of even the exact same 'abcdef' identifier IN THE
    SAME FUNCTION, thanks to block scope.

    They can't have one block sharing two instances of the same 'abcdef'
    name with the second being declared part-way through.

    There's only one 'abcdef' identifier per function, whatever case mix is
    used, and that's your lot.

    Sure, they can /deliberately/ write bad code in my language, but they
    have to work much harder than in C, where you can do it without trying.

    If I told you I like the green colour of my car, you'd tell me that your
    car is red, and red is the only good colour for a car.  After all, if
    green aliens hid my car in a field of broccoli and surrounded it with
    green frogs it would be hard to find, while your red car would be
    clearly visible.

    And yet, my now white car (it used to be red), is next to impossible to
    spot in a car-park where 50% of cars are white. I have to identify it
    from the number-plate.

    So if I told you that, I'd have a valid point.

    (I still don't know why C has a separate namespace for labels.)
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Tue May 19 21:58:44 2026
    From Newsgroup: comp.lang.c

    On 19/05/2026 19:47, Bart wrote:
    On 19/05/2026 17:48, David Brown wrote:
    On 19/05/2026 18:31, Bart wrote:
    On 19/05/2026 14:40, David Brown wrote:

    Can you give me an example of a real-world programming language in
    which the scope of a local variable begins at the start of the
    enclosing block, rather than at its declaration / definition?

    In Algo68:

        BEGIN
            print((a, newline));
            INT a=777;
            print((a, newline));
        END

    This prints 0 then 777. If I comment out the declaration, it says 'a'
    has not been declared.

    Here, 'a' is a named constant; if I use a:=777 instead, which defines
    a variable, it says the first 'a' has not been initialised (I guess
    'INT a' creates a reference to a local), a different error from not
    having the INT line at all.

    OK.  So there was a real language that worked that way, half a century
    ago.

    While I don't care for it, a LOT of thought went into its design, by
    some very clever people.

    And a lot of the very clever people that worked on the design of Algol68 concluded that in the end, the language was a disaster. Clever people
    rarely agree - appeals to authority are not good arguments.



    Now, you said real-world not mainstream, so all mine currently do
    that. Anything declared in a function body has function-wide scope,
    including to any depth of nested block.


    I don't count your language as "real world".  But if you prefer the
    term "mainstream", I'll go along with that, and then skip your
    descriptions of it.

    You would dismiss potentially good ideas for petty reasons?


    No, but I think it's useful to know what features have been
    battle-tested. (A single user of their own language does not count.)

    There is a fuzzy area in programming lanuages when you declare things in
    the middle of a block.

    You declare X in the middle, and its scope lasts until the end of the
    block. But that happens between the start of the block and its declaration?

    If you understood the difference between lifetime and scope, you might
    know. The details may differ between languages - obviously it is C that
    is important here. The lifetime of a local variable (excluding VLAs) in
    C begins when the block containing it is entered, and ends at the end of
    the block. The scope begins at the declaration of the variable and ends
    at the end of the enclosing block. Usually this does not make much difference, but it can be relevant if you use goto and jump earlier in
    the block.

    (There is also the difference that when you call a function inside the
    scope of a local variable, the variable's lifetime continues but it is
    not in the scope of the called function - hopefully that is obvious.)


    If this new X is not visible, then what happens if you try and use 'X'?

    If it has been assigned a value at some point, then you can use it (but
    not via the name). Scoping is about the name, lifetime is about the
    object itself.


    Apparently in C, you just get whatever outer X happens to be visible
    from an outer scope.

    Yes. That is how scopes work.

    There are other choices, including hiding outer Xs
    but now allowing you to access this new X either.


    A language could make that choice, but it is common in compiled
    imperative languages for local scopes to overrule outer scopes.
    Otherwise adding a new file-scope variable could break existing working functions, which would be a daft idea.

    If you want the current behaviour, you can create a new {} block, then
    there are fewer surprises.


    You are not making sense. If you are surprised by having a new variable declaration hide the scope of an outer scope variable, why would it be
    more or less of a surprise depending on the braces?

    In C, the same of thing happens when you introduced a forward
    declaration of a function: you move its lexical scope to near the top.

    Yes, C is exactly the same but for the minor detail of being
    completely different.

    So, having functions, variables, types and macro generally declared near
    the top of a source file, has no similarity with declaring local
    variables near the top of a function?

    OK..

    You prefer to move the goalposts than to read my comment? You said that
    C is just like your language in that you can define functions in any
    order - as long as you put forward declarations at the top of the file.
    The point is that in C, /declarations/ have scope extending from the declaration, wherever that might be - scopes are not global across the file.


    Perhaps you are mixing up "scope" and "lifetime" ?  These are not
    the same things.

    I'm talking about static, lexical [scope], which can be determined at
    compile-time.

    Again, I think you are mixing up "scope" and "lifetime".

    I think you are. I said this can be determined at /compile-time/. It is purely to do with visibility from a particular spot in source code.


    Scope is a compile-time property of the name of a variable. Lifetime is
    a run-time property of the object itself.

      Can you demonstrate that you understand the difference?  It would be
    particularly nice if you could show you know the difference for local
    variables in C.

    Why don't you explain the difference?


    See above. Or, perhaps, shock everyone by looking in the C standards.

    Imagine, instead, that most people who program know how to write
    sensible code.

    I suspect I've seen more reams of nightmare C source code than you have.


    Try waking up. I am getting tired of your imagined horrors that C
    allows, while pretending that your own language does not allow bad code.
    And I'm tired of your cherry-picking the worst examples you can find.
    Please do not respond by giving more of them.


      Not everyone does, but most people do.  Worrying about how badly
    people could misuse any given feature is not helpful,

    People like to push languages to the limits. They like to show off and
    be clever. They will abuse any feature.

    and of course there are endless ways to write terrible code using your
    language or any other language.

    In my language they can't have have 64 variations of the same 'abcdef' identifier IN THE SAME SCOPE by varying case. They can't have unlimited unrelated instances of even the exact same 'abcdef' identifier IN THE
    SAME FUNCTION, thanks to block scope.


    That's fantastic! Amazing! If only other language designers had a
    fraction of your genius - then software bugs would be a thing of the past.

    You have marginally reduced the possibility of certain kinds of errors
    while simultaneously making it harder to write good code so we get other errors instead.

    They can't have one block sharing two instances of the same 'abcdef'
    name with the second being declared part-way through.

    There's only one 'abcdef' identifier per function, whatever case mix is used, and that's your lot.

    Sure, they can /deliberately/ write bad code in my language, but they
    have to work much harder than in C, where you can do it without trying.

    If I told you I like the green colour of my car, you'd tell me that
    your car is red, and red is the only good colour for a car.  After
    all, if green aliens hid my car in a field of broccoli and surrounded
    it with green frogs it would be hard to find, while your red car would
    be clearly visible.

    And yet, my now white car (it used to be red), is next to impossible to
    spot in a car-park where 50% of cars are white. I have to identify it
    from the number-plate.

    So if I told you that, I'd have a valid point.

    (I still don't know why C has a separate namespace for labels.)

    There's a lot you don't know about C. (Some of it, of course, doesn't
    matter. But it's strange how you wear your ignorance like a merit badge
    while simultaneously claiming to be an expert in language design who has implemented a C compiler.)



    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Tue May 19 22:16:25 2026
    From Newsgroup: comp.lang.c

    On 19/05/2026 20:58, David Brown wrote:
    On 19/05/2026 19:47, Bart wrote:

    (I still don't know why C has a separate namespace for labels.)

    There's a lot you don't know about C.

    It sounds like you don't know either.

    But it's strange how you wear your ignorance like a merit badge
    while simultaneously claiming to be an expert in language design

    Implementing C's three namespaces doesn't require knowing why they
    exist, only that they do. Plus of course an infinite number of block
    scopes in a language which averages 3 local variables per function.



    who has
    implemented a C compiler.)

    I implemented a C-subset compiler in 2017 (written in a language I
    designed and implemented) which was tested on some half million lines of
    open source C code, much of it brutal.

    That actually taught me quite a lot, but mostly that C was an even worse language than I'd thought. I also got to see a LOT of ugly code.

    The code it processes is definitely C.

    I'm not sure why you say I'm claiming to have written one; you don't
    believe me? OK.

    I can see why it might annoy some people here.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Tue May 19 14:19:59 2026
    From Newsgroup: comp.lang.c

    On 5/19/2026 1:34 AM, Janis Papanagnou wrote:
    On 2026-05-19 10:00, David Brown wrote:
    On 19/05/2026 09:18, Janis Papanagnou wrote:
    On 2026-05-19 01:31, Chris M. Thomasson wrote:

    [...] For some experimental code I take note of the lines in a
    single file, take note of the modular setup, and say. okay, this is
    getting pretty big. its time to separate these. Iiirc, MISRA has a
    limit on the number of lines in a function?

    (I don't know about MISRA.)

    Determining such numbers to get characteristics of source code can
    certainly be useful.

    Limiting these numbers (and rejecting such code), OTOH, appears to
    me to be quite stupid.

    I don't recall reading a rule in MISRA that limits the length of a
    function.  But there are a number of different editions of the MISRA
    coding standards.

    Generally, it is a good idea to keep functions fairly short.

    Actually, the first time I've seen such a formulation was from
    the context of "C". (Maybe even from K&R ? - Don't recall.)

    I'm coming more from the camp saying that plain numbers of lines
    are per se not an asset, not considering that a primary criterion
    for programming. (Large functions may or may not be an indication
    for bad style or bad software design, as short functions may be or
    not, likewise.)

    There's a lot more dimensions to organize code (classes, modules)
    and other factors like how broad an interface is, depth of calls,
    structures, function signatures, etc. - you can continue that list
    ad nauseam. - There's tools that may provide hints based on such
    criteria.

    But sometimes it is clearer to have a longer function than to break it
    into many small functions.  And occasionally you come across something
    that is best handled as one very large function, if the structure is
    regular and clear (such as a large switch statement).  Guidelines
    about sizes can be a good idea, but fixed rules rarely are.

    (MISRA compliance does allow for many of its rules and guidelines to
    be broken, if appropriately commented and justified.)

    We've invented such guidelines as well, back then. Making sensible
    rules, forcing some, suggesting others, and demand to comment and
    justify any deviations. - Yeah, it's nice to see that established
    procedures and practices mostly seem to be handled sensibly.

    it has some rather interesting guidelines:

    https://www.stroustrup.com/JSF-AV-rules.pdf


    Fwiw, think of a single file getting big, you have your:

    raw data can be a plane of pixels, voxels, ect...

    canvas (can be ppm (easy portable), cairo, ect... abstraction)

    povray canvas (dumps out files that povray can read)

    STL canvas dumps out stl files ready for printing

    plotter can use the canvas to draw shapes, ect...

    geometry base

    mesh iso builder

    volume renderer

    fractal renderer

    field renderer

    koch seeder

    ect...


    all in one file! BUT! This file is being separated in and of itself as a modular thing anyway. So, now its time to make all of these systems in
    their own files. Fair enough?
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Tue May 19 14:27:54 2026
    From Newsgroup: comp.lang.c

    Bart <bc@freeuk.com> writes:
    On 19/05/2026 07:58, Lawrence D’Oliveiro wrote:
    On Mon, 18 May 2026 18:35:04 +0100, Bart wrote:
    I hate dealing with code that just declares variables
    higgledy-piggledy all over the place; it is so lazy. I'm not into
    block scopes either.
    Tight control over scope reduces the chances of unexpected
    interactions across different parts of the code.

    At the same time, it makes it harder to see what interactions there
    might be, because you don't know whether the 'x' appearing here is the
    same one used earlier and/or the one used later.

    You need to very carefully trace the declaration for each instance.

    And also (this is what I hate), you have a bloody great declaration
    right in the middle of what would have been your nice clean code.

    I acknowledge that you hate it. It doesn't bother me. I think of both declarations and statements as "code".

    There is also the odd way that scopes work:

    int a; // a1
    .... // a1 in scope
    {
    print(a); // a1 in scope
    double a; // a2
    print(a); // a2 in scope
    }
    .... // a1 in scope

    They don't need to be delimited by {...}; here that block has a1 in
    first half and a2 in second half, but both are 'a'.

    The rule for block scopes is simple. For an identifier declared
    within a block, its scope (the region of program text in which
    it's visible) begins at the declaration and ends at the end of
    the enclosing block. (It does not begin at the beginning of the
    enclosing block.) An inner declaration of the same identifier can
    hide an outer declaration; this is sometimes called "shadowing".
    This is well defined, is not usually much of a problem, and can be straightforwardly avoided if it is a problem.

    (I won't ask what "print" is supposed to be.)

    But rather than citing and understanding the rule, you show us badly
    written code designed to make it appear more confusing that it is.

    I hope there aren't any inexperienced C programmers here reading
    your posts and taking them seriously.

    Here:
    ....
    double b = a, a;
    ....

    'b' is initialised from a1, then it declares a2, however since both a1
    and a2 are called 'a', it is a touch confusing!

    Doctor, it hurts when I do this!
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Tue May 19 14:34:09 2026
    From Newsgroup: comp.lang.c

    On 5/19/2026 6:40 AM, David Brown wrote:
    On 19/05/2026 15:08, Bart wrote:
    On 19/05/2026 13:23, David Brown wrote:
    On 19/05/2026 13:48, Bart wrote:
    On 19/05/2026 12:15, Richard Harnden wrote:
    On 19/05/2026 11:55, Bart wrote:
    On 19/05/2026 07:58, Lawrence D’Oliveiro wrote:
    On Mon, 18 May 2026 18:35:04 +0100, Bart wrote:

    I hate dealing with code that just declares variables
    higgledy-piggledy all over the place; it is so lazy. I'm not into >>>>>>>> block scopes either.

    Tight control over scope reduces the chances of unexpected
    interactions across different parts of the code.

    At the same time, it makes it harder to see what interactions
    there might be, because you don't know whether the 'x' appearing
    here is the same one used earlier and/or the one used later.

    You need to very carefully trace the declaration for each instance. >>>>>>
    And also (this is what I hate), you have a bloody great
    declaration right in the middle of what would have been your nice >>>>>> clean code.

    There is also the odd way that scopes work:

         int a;             // a1
         ....               // a1 in scope
         {
            print(a);       // a1 in scope
            double a;       // a2
            print(a);       // a2 in scope
         }
         ....               // a1 in scope

    They don't need to be delimited by {...}; here that block has a1
    in first half and a2 in second half, but both are 'a'.

    Yes they do.  How else does 'a' stop being a2 and revert to a1?

    a1 turned into a2 without crossing '{'.


    It did so at the new declaration of "a".  How could that possibly be
    surprising or "odd" ?


    How could it possibly not be?!

    {...} is a block in C, and C has block scopes. Given that, people
    might expect a scope to be delimited by the enclosing spaces.

    People expect scopes to begin with a declaration, and end at the end of
    the block (or file, for file-scope identifiers).  This is the normal handling of scoping in most imperative programming languages.  There are some languages (such as Python) where scopes begin when a variable is
    first assigned, and continue to the end of the function rather than the block.  And declarative programming languages may do things differently
    - they do many things differently.

    Languages may differ in the details - given "int a = foo(a);", for
    example, the scope of the new "a" in C begins after "int a" - in some languages, it may not begin until the end of the semicolon.  But that's
    a small detail, rarely relevant in real code.  (For the record, I'd have preferred if the scope of variables in C did not begin until the end of
    the declaration / definition.)

    Can you give me an example of a real-world programming language in which
    the scope of a local variable begins at the start of the enclosing
    block, rather than at its declaration / definition?

    Perhaps you are mixing up "scope" and "lifetime" ?  These are not the
    same things.

    Well, the far away land of C++, use a scope to define a lifetime?

    {
    foo x(whatever);

    {
    froboz m(zing);

    x.bar(m);
    x.baz(m);
    }

    x.compute();
    }

    ?









    But names don't come into existence until declared, which can be in
    the middle of a block. Until then, the outer version of 'a' is still
    in scope.


    Yes.  Scope is all about the names.

    The point at each that happens, in a busy section of code with lots of
    declarations, can be unclear, with overlaps:

        double a;
        ....       // a2 b1 c1
        double b;
        ....       // a2 b2 c1
        double c;
        ....       // a2 b2 c2

    These all shadow a, b, c in an outer scope, but at different times.

    Yes.  (Although this is again a strawman argument - people don't
    normally write code that shadows outer scope variables.)


    The simplest scoping rule in C is for labels: they have function-wide
    scope, regardless of block nesting label.


    The rule for C labels exists because you have to be able to jump
    backwards and forwards for "goto" to be of much use.  In C programming, labels (excluding case labels) are rarely used except sometimes for
    error handling.  I don't think I've written "goto" in C more than a
    couple of times in my programming career.

    C labels are unstructured.  This is not a good thing in a programming language - it is an unfortunate necessary evil.  Other identifiers have clearer and simpler scoping - scope starts with the declaration, and
    ends with the end of the enclosing block.

    That would almost be as simple as how my own scopes work, except C
    just has to still be quirky:

        double c; c: goto c;

    (Labels of course have their own namespace, for some obscure reason.
    I'm sure 99% of C programmers don't know that.)

    Since we are pulling numbers out of the air, 99% of C programmers don't care.  If any C programmer writes code where this is important, because they use the same identifier for a variable and a goto label, they
    should not be programming.



    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Tue May 19 15:01:03 2026
    From Newsgroup: comp.lang.c

    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    On 5/19/2026 6:40 AM, David Brown wrote:
    [...]
    Can you give me an example of a real-world programming language in
    which the scope of a local variable begins at the start of the
    enclosing block, rather than at its declaration / definition?
    Perhaps you are mixing up "scope" and "lifetime" ?  These are not
    the same things.

    Well, the far away land of C++, use a scope to define a lifetime?

    {
    foo x(whatever);

    {
    froboz m(zing);

    x.bar(m);
    x.baz(m);
    }

    x.compute();
    }

    Chris, what exactly is your point?

    The same scope and lifetime rules apply in C as in C++. A difference
    is that an object reaching the end of its lifetime in C++ can have
    visible effects, whereas in C it generally just means that its memory
    can be deallocated (though the actual deallocation can be delayed).

    There are also different lifetime rules for heap-allocated objects,
    in both C and C++. (Scope doesn't apply, since heap-allocated
    objects don't have names.)

    David is entirely correct that scope and lifetime are two different
    things. The scope of an identifier is the region of program text
    in which it's visible. The lifetime of an object is the time during
    program execution when the object logically exists. Did you intend
    to express disagreement?

    Yes, scope and lifetime are related. For an object defined within
    a block, its identifier's scope ends at the "}" of the block,
    and its lifetime ends when execution logically reaches that same "}".

    [...]

    Chris, you have yet again refused to trim a large amount of quoted
    text when posting a followup. I see that you use Thunderbird, which
    as far as I know doesn't make it particularly difficult to do that.

    Why do you do this?

    If you do not start trimming irrelevant quoted text, expect this
    to be the last time I interact with you.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Tue May 19 15:15:45 2026
    From Newsgroup: comp.lang.c

    On 5/19/2026 3:01 PM, Keith Thompson wrote:
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    On 5/19/2026 6:40 AM, David Brown wrote:
    [...]
    Can you give me an example of a real-world programming language in
    which the scope of a local variable begins at the start of the
    enclosing block, rather than at its declaration / definition?
    Perhaps you are mixing up "scope" and "lifetime" ?  These are not
    the same things.

    Well, the far away land of C++, use a scope to define a lifetime?

    {
    foo x(whatever);

    {
    froboz m(zing);

    x.bar(m);
    x.baz(m);
    }

    x.compute();
    }

    Chris, what exactly is your point?

    The same scope and lifetime rules apply in C as in C++.


    A difference
    is that an object reaching the end of its lifetime in C++ can have
    visible effects, whereas in C it generally just means that its memory
    can be deallocated (though the actual deallocation can be delayed).

    There are also different lifetime rules for heap-allocated objects,
    in both C and C++. (Scope doesn't apply, since heap-allocated
    objects don't have names.)

    froboz can create threads, x.bar(m), x.baz(m), use them, the all the
    threads are joined in the dtor of m. So, this can be in C as well, just
    using manual function calls before the end of the scope. I have to admit
    that I do like C99's way to not have to declare everything up front.
    Declare where you need it.

    I prefer:

    {
    foo x(whatever);

    {
    froboz m(zing);

    x.bar(m);
    x.baz(m);
    }

    x.compute();
    }

    over:

    {
    foo x(whatever);
    froboz m(zing);

    x.bar(m);
    x.baz(m);

    x.compute();
    }


    David is entirely correct that scope and lifetime are two different
    things. The scope of an identifier is the region of program text
    in which it's visible. The lifetime of an object is the time during
    program execution when the object logically exists. Did you intend
    to express disagreement?

    No.


    Yes, scope and lifetime are related. For an object defined within
    a block, its identifier's scope ends at the "}" of the block,
    and its lifetime ends when execution logically reaches that same "}".

    [...]

    Chris, you have yet again refused to trim a large amount of quoted
    text when posting a followup. I see that you use Thunderbird, which
    as far as I know doesn't make it particularly difficult to do that.

    Why do you do this?

    Forgot.


    If you do not start trimming irrelevant quoted text, expect this
    to be the last time I interact with you.

    Okay.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Wed May 20 04:09:44 2026
    From Newsgroup: comp.lang.c

    On 2026-05-19 23:19, Chris M. Thomasson wrote:
    On 5/19/2026 1:34 AM, Janis Papanagnou wrote:
    On 2026-05-19 10:00, David Brown wrote:
    On 19/05/2026 09:18, Janis Papanagnou wrote:
    On 2026-05-19 01:31, Chris M. Thomasson wrote:

    [...] For some experimental code I take note of the lines in a
    single file, take note of the modular setup, and say. okay, this is >>>>> getting pretty big. its time to separate these. Iiirc, MISRA has a
    limit on the number of lines in a function?

    (I don't know about MISRA.)

    Determining such numbers to get characteristics of source code can
    certainly be useful.

    Limiting these numbers (and rejecting such code), OTOH, appears to
    me to be quite stupid.

    I don't recall reading a rule in MISRA that limits the length of a
    function.  But there are a number of different editions of the MISRA
    coding standards.

    Generally, it is a good idea to keep functions fairly short.

    Actually, the first time I've seen such a formulation was from
    the context of "C". (Maybe even from K&R ? - Don't recall.)

    I'm coming more from the camp saying that plain numbers of lines
    are per se not an asset, not considering that a primary criterion
    for programming. (Large functions may or may not be an indication
    for bad style or bad software design, as short functions may be or
    not, likewise.)

    There's a lot more dimensions to organize code (classes, modules)
    and other factors like how broad an interface is, depth of calls,
    structures, function signatures, etc. - you can continue that list
    ad nauseam. - There's tools that may provide hints based on such
    criteria.

    But sometimes it is clearer to have a longer function than to break
    it into many small functions.  And occasionally you come across
    something that is best handled as one very large function, if the
    structure is regular and clear (such as a large switch statement).
    Guidelines about sizes can be a good idea, but fixed rules rarely are.

    (MISRA compliance does allow for many of its rules and guidelines to
    be broken, if appropriately commented and justified.)

    We've invented such guidelines as well, back then. Making sensible
    rules, forcing some, suggesting others, and demand to comment and
    justify any deviations. - Yeah, it's nice to see that established
    procedures and practices mostly seem to be handled sensibly.

    it has some rather interesting guidelines:

    https://www.stroustrup.com/JSF-AV-rules.pdf

    Thanks for the link. (In a quiet moment I may have a look into
    it.) - For a coding standard 140 pages may be considered to be
    borderline, though. (We tried to keep it as terse as possible.)

    BTW, concerning a recent operator precedence discussion; this
    document contains a table (pp. 137f). To quote from it:

    "Parentheses should be used to clarify operator precedence
    rules to enhance readability and reduce mistakes. However,
    overuse of parentheses can clutter an expression thereby
    reducing readability."

    And there are some sensible examples of good style, overused,
    and mandatory parenthesis presented (p. 136).


    Fwiw, think of a single file getting big, you have your:

    [snip some list]
    ect...


    all in one file! BUT! This file is being separated in and of itself as a modular thing anyway. So, now its time to make all of these systems in
    their own files. Fair enough?

    I cannot really parse your post or identify your point(s). Sorry.

    Concerning some keywords here: with C++ I have semantic entities
    separated (or collected) in classes, for their re-use I have the
    declaration in a header file and the implementation separated.

    Janis

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Wed May 20 06:24:00 2026
    From Newsgroup: comp.lang.c

    On 2026-05-19 18:31, Bart wrote:
    On 19/05/2026 14:40, David Brown wrote:
    [...]

    Can you give me an example of a real-world programming language in
    which the scope of a local variable begins at the start of the
    enclosing block, rather than at its declaration / definition?

    In Algo68:

       BEGIN
           print((a, newline));
           INT a=777;
           print((a, newline));
       END

    This prints 0 then 777. If I comment out the declaration, it says 'a'
    has not been declared.

    1. The original post was talking about "variables" (not about identity declarations).

    2. Are you sure that above code is valid (error-free) Algol 68 code?

    I'm not sure about that. - My old textbook says that you can use these identifiers *after* you've set them (not before). And skimming through
    the standard document, the Revised Report, I could not find anything.
    If there is; can you point me to the relevant chapter, please?

    It could also just not have been defined in the standard. (The above
    code or Genie's behavior would then not tell anything relevant about
    the language. - It would just expose yet another example of code that
    an experienced programmer would just not write that way.)

    You seem to have tested that with the Genie interpreter? - This would
    not say anything about what the Algol 68 standard says, mind. - I've
    sent a mail to Marcel to clarify that, i.e. the behavior of Genie, and
    whether that's standard behavior, undefined, or an oversight or bug.


    Here, 'a' is a named constant; if I use a:=777 instead, which defines a variable, it says the first 'a' has not been initialised (I guess 'INT
    a' creates a reference to a local), a different error from not having
    the INT line at all.

    This is expected behavior, as we know it and also expect it from other languages. - It's actually reflecting the point that David made.

    Janis

    [...]

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Wed May 20 06:38:54 2026
    From Newsgroup: comp.lang.c

    On 2026-05-19 18:48, David Brown wrote:
    On 19/05/2026 18:31, Bart wrote:
    [...]

    OK.  So there was a real language that worked that way, half a century ago.

    It's not that clear, IMO; all I see in his post is the result of a
    specific tool he uses, and for a specific case (not for variables).
    (See my recent post a few minutes ago for the details.)

    BTW, that language still _works_ (not "worked"). - Whether with the
    detail of behavior Bart showed, or more sensibly defined, as we'd
    expect it to be for a thoroughly designed language like Algol 68.

    (I wouldn't talk too pejorative about that language. There's a high
    quality and actively maintained interpreter (a68g), and there's the
    actual GNU project to add Algol 68 support to their compiler suite,
    the 'ga68' tool. - I haven't checked the latter; it appears to me to
    be still work in progress and unfinished. - That the user community
    is small is hardly a sensible valuation of its language concepts.)

    Janis

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Wed May 20 08:59:07 2026
    From Newsgroup: comp.lang.c

    On 19/05/2026 23:16, Bart wrote:
    On 19/05/2026 20:58, David Brown wrote:
    On 19/05/2026 19:47, Bart wrote:

    (I still don't know why C has a separate namespace for labels.)

    There's a lot you don't know about C.

    It sounds like you don't know either.

    I don't know for sure - but I don't care that they are in separate
    namespaces, and I don't care about why other than for curiosity. I
    think any code for which it matters, and code shares the same identifier
    for a label and a variable, is hopelessly badly written or extremely
    niche. I have almost never had need of "goto" or labels (excluding
    switch case labels, of course), and don't expect ever to do so in the
    future.


    But it's strange how you wear your ignorance like a merit badge while
    simultaneously claiming to be an expert in language design

    Implementing C's three namespaces doesn't require knowing why they
    exist, only that they do. Plus of course an infinite number of block
    scopes in a language which averages 3 local variables per function.


    Okay.



    who has implemented a C compiler.)

    I implemented a C-subset compiler in 2017 (written in a language I
    designed and implemented) which was tested on some half million lines of open source C code, much of it brutal.

    That actually taught me quite a lot, but mostly that C was an even worse language than I'd thought. I also got to see a LOT of ugly code.

    The code it processes is definitely C.

    I'm not sure why you say I'm claiming to have written one; you don't
    believe me? OK.


    I find it hard to reconcile accurately writing a C compiler with your misconceptions and misunderstandings about the language, and your
    reluctance to actually look at how the language is defined. I believe
    you have written a compiler that compiles the language you think C is,
    and that is a close enough match to C for it to work (or appear to work)
    with a fair bit of C code. I have no doubt that it works fine for the C
    code you personally write or generate.

    I can see why it might annoy some people here.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Wed May 20 09:24:32 2026
    From Newsgroup: comp.lang.c

    On 20/05/2026 00:15, Chris M. Thomasson wrote:
    On 5/19/2026 3:01 PM, Keith Thompson wrote:
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    On 5/19/2026 6:40 AM, David Brown wrote:
    [...]
    Can you give me an example of a real-world programming language in
    which the scope of a local variable begins at the start of the
    enclosing block, rather than at its declaration / definition?
    Perhaps you are mixing up "scope" and "lifetime" ?  These are not
    the same things.

    Well, the far away land of C++, use a scope to define a lifetime?

    {
         foo x(whatever);

         {
            froboz m(zing);

            x.bar(m);
            x.baz(m);
         }

         x.compute();
    }

    Chris, what exactly is your point?

    The same scope and lifetime rules apply in C as in C++.


    A difference
    is that an object reaching the end of its lifetime in C++ can have
    visible effects, whereas in C it generally just means that its memory
    can be deallocated (though the actual deallocation can be delayed).


    There is another major difference between C and C++. C++ makes a much stronger distinction between "lifetimes" and "storage durations". In C,
    the "lifetime" of an object is just the time for which its "storage" is guaranteed to exist and be reserved for the object (subject, as always,
    to "as if" rules). Thus if a compiler generates code for a function
    which sets up space on a stack at entry to a function, then the storage duration of a local variable might run from function entry to function
    exit, while the lifetime is only within the inner block containing it. (Equally, it's fine for the storage space on the stack to be be
    allocated at the start of the inner block rather than the start of the function - the lifetime requirements determine the minimum storage
    duration.)

    In C++, storage duration is the same as in C. But lifetimes are a
    little different, because the start and end of an object's lifetime in
    C++ can be visible via constructors and destructors. The lifetime of a
    local object in C++ starts, AFAICS, at its declaration (ask down the
    hall in c.l.c++ for more details). And lifetime starting and ending is precisely ordered in C++, while in C the order has no visible effects.

    There are also different lifetime rules for heap-allocated objects,
    in both C and C++.  (Scope doesn't apply, since heap-allocated
    objects don't have names.)

    Right. Such objects have storage duration and lifetime (which are the
    same in C, but can be different in C++ - storage duration is about the
    memory allocation, while lifetime is about the constructor/destructor),
    but they have no names and therefore there is nothing to have a scope.


    froboz can create threads, x.bar(m), x.baz(m), use them, the all the
    threads are joined in the dtor of m. So, this can be in C as well, just using manual function calls before the end of the scope. I have to admit that I do like C99's way to not have to declare everything up front.
    Declare where you need it.

    I prefer:

    {
        foo x(whatever);

        {
           froboz m(zing);

           x.bar(m);
           x.baz(m);
        }

        x.compute();
    }

    over:

    {
        foo x(whatever);
        froboz m(zing);

        x.bar(m);
        x.baz(m);

        x.compute();
    }


    It is unusual to see such extra blocks in C, but they turn up in C++
    when you are using the lifetime of a local variable actively. I had
    such usage yesterday, with a lock-guard variable inside a small block.
    It is generally the lifetime that is important in such cases, rather
    than the scope (though they will be the same thing in most cases, baring function calls within the block that leave the scope but not the
    lifetime of the variable).



    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Wed May 20 03:26:14 2026
    From Newsgroup: comp.lang.c

    David Brown <david.brown@hesbynett.no> writes:
    [...]
    There is another major difference between C and C++. C++ makes a much stronger distinction between "lifetimes" and "storage durations". In
    C, the "lifetime" of an object is just the time for which its
    "storage" is guaranteed to exist and be reserved for the object
    (subject, as always, to "as if" rules). Thus if a compiler generates
    code for a function which sets up space on a stack at entry to a
    function, then the storage duration of a local variable might run from function entry to function exit, while the lifetime is only within the
    inner block containing it. (Equally, it's fine for the storage space
    on the stack to be be allocated at the start of the inner block rather
    than the start of the function - the lifetime requirements determine
    the minimum storage duration.)

    Let's say we have an object defined in an inner block within a function:

    void func()
    {
    puts("n does not yet exist");
    {
    puts("n exists but has not been initialized");
    int n = 42;
    puts("n exists and has been initialized");
    }
    puts("n no longer exists");
    }

    An object's *lifetime* is a portion of program execution, a span
    of time. Its *storage duration* is one of "static", "thread",
    "automatic", and "allocated".

    The lifetime of n is only the execution of the inner block.
    The compiler may well have generated code to allocate and deallocate
    storage on entry and exit to func(), the language has nothing to say
    about that; the 1st and 4th puts() calls are outside n's lifetime.

    [...]

    It is unusual to see such extra blocks in C, but they turn up in C++
    when you are using the lifetime of a local variable actively. I had
    such usage yesterday, with a lock-guard variable inside a small
    block. It is generally the lifetime that is important in such cases,
    rather than the scope (though they will be the same thing in most
    cases, baring function calls within the block that leave the scope but
    not the lifetime of the variable).

    Lifetime and scope are never "the same thing". Lifetime is a range
    of time during program execution. Scope is a region of text in
    program source code. They can be related for some objects in the
    sense that an object might have a lifetime that extends from the
    moment when execution enters a block to when it leaves it, while
    the object's identifier might have a scope that extends to the end
    of the same block.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Wed May 20 11:55:52 2026
    From Newsgroup: comp.lang.c

    On 20/05/2026 05:24, Janis Papanagnou wrote:
    On 2026-05-19 18:31, Bart wrote:
    On 19/05/2026 14:40, David Brown wrote:
    [...]

    Can you give me an example of a real-world programming language in
    which the scope of a local variable begins at the start of the
    enclosing block, rather than at its declaration / definition?

    In Algo68:

        BEGIN
            print((a, newline));
            INT a=777;
            print((a, newline));
        END

    This prints 0 then 777. If I comment out the declaration, it says 'a'
    has not been declared.

    1. The original post was talking about "variables" (not about identity declarations).

    The whole sub-topic is about the scope of identifiers, no matter what
    they are for. (It excluded tag and label namespaces.)

    Part of it was about what is in scope in the first half of this block:

    int a=1234;
    {
    ... // ?
    int a=777;
    ...
    }

    I was asked which real programming languages have the scope of that
    second 'a' extend back to the start of its block.

    This is a fuller Algol68 program which runs with A68G:

    INT a=1234;

    PROC fred = (INT n)INT:
    BEGIN
    print((a, newline));
    INT a=777;
    print((a, newline));

    a
    END;

    INT x;
    x:=fred(23);

    print(x)

    All the extra bits are to avoid error messages. It seems that the scope
    of that 'a' which is set to 777 does indeed go back to that BEGIN, or
    rather its name, if not its value.

    Try this version:

    BEGIN
    print((a, newline));
    a
    END

    This shows '1234'. Now add this declaration:

    BEGIN
    print((a, newline));
    INT a:=777;
    a
    END

    Now it says that 'a' is uninitialised. The looks very strongly as the
    'a' it attempts to print is the 'a' in the following declaration, even
    this is usually a coding error.


    2. Are you sure that above code is valid (error-free) Algol 68 code?

    I'm not sure about that. - My old textbook says that you can use these identifiers *after* you've set them (not before). And skimming through
    the standard document, the Revised Report, I could not find anything.
    If there is; can you point me to the relevant chapter, please?

    It could also just not have been defined in the standard. (The above
    code or Genie's behavior would then not tell anything relevant about
    the language. - It would just expose yet another example of code that
    an experienced programmer would just not write that way.)

    You seem to have tested that with the Genie interpreter? - This would
    not say anything about what the Algol 68 standard says, mind.

    You seem determined to prove Bart wrong, aren't you?


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Wed May 20 14:20:34 2026
    From Newsgroup: comp.lang.c

    On 20/05/2026 07:59, David Brown wrote:
    On 19/05/2026 23:16, Bart wrote:
    On 19/05/2026 20:58, David Brown wrote:
    On 19/05/2026 19:47, Bart wrote:

    (I still don't know why C has a separate namespace for labels.)

    There's a lot you don't know about C.

    It sounds like you don't know either.

    I don't know for sure - but I don't care that they are in separate namespaces, and I don't care about why other than for curiosity.  I
    think any code for which it matters, and code shares the same identifier
    for a label and a variable, is hopelessly badly written or extremely
    niche.

    So it /is/ pointless to have that separate namespace.

      I have almost never had need of "goto" or labels (excluding
    switch case labels, of course), and don't expect ever to do so in the future.

    Is this what people here like to call a 'non sequitur'? Whether /you/
    goto use it is not relevant. If I test some codebases (not many are kept
    lying around these days):

    SQLite3 645 gotos
    Lua 5.4 40
    Tiny C 300
    MZLib 40
    bcc 9000 (when transpiled to low-level C)
    Linux kernel 150000 (iirc)

    (The low-level C has no high-level control flow, only goto and if-goto.

    The Linux figure is from some years ago when the line count was only
    21MLoc, and it was estimated via string processing, not actual compilation.)


    I'm not sure why you say I'm claiming to have written one; you don't
    believe me? OK.


    I find it hard to reconcile accurately writing a C compiler with your misconceptions and misunderstandings about the language, and your
    reluctance to actually look at how the language is defined.

    There are lots of different qualities of general purpose C compilers, if
    you look outside the big 3 (gcc, clang, msvc).

    I used to use DMC, Pelles C, lccwin32 extensively, and still use Tiny C.

    There are also lots of lesser known ones that don't pretend to be full, conforming implementations. Pico C for example (an interpreter); Smlr C;
    Small C; I forget the names.

    The point is that there are a vast number of products that purport to
    run or compile C code.

    Mine is just another one of those, and these days has a number of
    use-cases outside of just compiling C (including quickly doing ad hoc surveys).


      I believe
    you have written a compiler that compiles the language you think C is,
    and that is a close enough match to C for it to work (or appear to work) with a fair bit of C code.

    Yes. Especially C90 applications such as Lua source code. But since 2017
    a lot more open source code has started to use C99 features I did't
    support, such as compound literals, designated initialisers, runtime expressions in {...} initialisers, VLAs and so on.

    I'm not planning to support those; many are poorly documented IMO and unintuitive to understand, hard to implement, and may have hidden depths
    of complexity.

      I have no doubt that it works fine for the C
    code you personally write or generate.

    It works perfectly, except that it targets x64 with Win64 ABI, and
    doesn't optimise. It's when I want to target non-Windows OSes, and/or
    have optimised code, that I use the C transpiler!

    However it is still useful as an instant test of the generated code,
    which otherwise takes 30-40 times longer with gcc-O0.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Wed May 20 16:22:28 2026
    From Newsgroup: comp.lang.c

    On 20/05/2026 15:20, Bart wrote:
    On 20/05/2026 07:59, David Brown wrote:
    On 19/05/2026 23:16, Bart wrote:
    On 19/05/2026 20:58, David Brown wrote:
    On 19/05/2026 19:47, Bart wrote:

    (I still don't know why C has a separate namespace for labels.)

    There's a lot you don't know about C.

    It sounds like you don't know either.

    I don't know for sure - but I don't care that they are in separate
    namespaces, and I don't care about why other than for curiosity.  I
    think any code for which it matters, and code shares the same
    identifier for a label and a variable, is hopelessly badly written or
    extremely niche.

    So it /is/ pointless to have that separate namespace.

    I did not say that, and I can't fathom how you would conclude that -
    either from your own lack of relevant knowledge, or from what I wrote.

    I would imagine that having them as a separate namespace would be more convenient in a compiler - the scoping and lookup rules are
    significantly different from the namespace for variables and functions,
    and having separate namespaces means compilers don't have to check for conflicts. There may be other good reasons for the separation, or it
    might be a historical artifact inherited from a predecessor language, or
    it might be that the C designers preferred separate namespaces and would
    only have combined them if they had good reason to do so.


      I have almost never had need of "goto" or labels (excluding switch
    case labels, of course), and don't expect ever to do so in the future.

    Is this what people here like to call a 'non sequitur'? Whether /you/
    goto use it is not relevant.

    It is highly relevant to my "I don't care".



      I believe you have written a compiler that compiles the language you
    think C is, and that is a close enough match to C for it to work (or
    appear to work) with a fair bit of C code.

    Yes. Especially C90 applications such as Lua source code. But since 2017
    a lot more open source code has started to use C99 features I did't
    support, such as compound literals, designated initialisers, runtime expressions in {...} initialisers, VLAs and so on.

    I'm not planning to support those; many are poorly documented IMO and unintuitive to understand, hard to implement, and may have hidden depths
    of complexity.


    You can make up your own mind about how difficult these features are to implement in your own compiler, though I question the reality of these concerns - I think you just like to complain that they are hard. And
    you can have the opinion that they are poorly documented, but we both
    know the reality is that you haven't bothered to try to read the documentation. The fact that people use these features should indicate
    that they are well enough documented and understood for C programmers to
    use. So are you trying to claim that you are particularly inept at
    reading and understanding about C language features? I doubt that.

    I'd be happier with an honest reason - such as you don't like these
    features (for some non-technical reason), you don't find them useful
    yourself (fair enough), and you therefore can't be bothered implementing
    them in your own tools (again, fair enough). You made your C compiler
    for fun, no one else uses it, and you have no obligations to anyone
    else. It's entirely up to you to pick the features you choose to
    support (as long as you don't make any claims to conformity). You don't
    have to make up bullshit excuses for choosing not to implement C99 features.

      I have no doubt that it works fine for the C code you personally
    write or generate.

    It works perfectly, except that it targets x64 with Win64 ABI, and
    doesn't optimise. It's when I want to target non-Windows OSes, and/or
    have optimised code, that I use the C transpiler!

    However it is still useful as an instant test of the generated code,
    which otherwise takes 30-40 times longer with gcc-O0.



    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Wed May 20 10:40:11 2026
    From Newsgroup: comp.lang.c

    On 19/05/2026 23:16, Bart wrote:
    On 19/05/2026 20:58, David Brown wrote:
    On 19/05/2026 19:47, Bart wrote:
    ...
    Implementing C's three namespaces doesn't require knowing why they
    exist, only that they do.

    "namespaces" are a C++ feature, which means something quite different
    from "name spaces". Both C and C++ have name spaces. C++ has only three: labels, macros, and ordinary identifiers. However, C has 4 name spaces
    and an unlimited number of members of each of two families of name spaces:

    1. Label Names
    2. Struct, union, and enumeration tags
    3. Each struct or uninon has a namespace for it's members
    4. Standard attributes and attribute specifiers
    5. Each attribute prefix has a namespace for it's trailing identifiers
    6. All other identifiers.

    The reason for the different name spaces is primarily syntactic. Each
    name space has different syntax for defining and using the corresponding identifiers, so you can never be unclear about which name space an
    identifier should be from.

    Label names are declared by following them with ":" at the start of a
    logical line in block scope, and are used only in a goto statement.
    Neither of those are locations where any of the other kinds of
    identifiers are legal.

    Similarly, in C, tags are always immediately preceded by one of the
    "struct", "union" or "enum" keywords, something that cannot be done with
    any other kind of identifier. They can occur without those keywords in
    C++, which is why C++ doesn't have a separate name space for them.

    Struct and union member names can only appear in struct or union
    identifiers or as the right operand of member selection operators for
    and expression of the type of the struct or union they are members of.
    No other types of identifiers can appear in those locations.

    Attributes can only be used inside of [[]], and trailing identifiers can
    only occur immediately after the corresponding attribute. Again, no
    other identifiers can occur in those locations.

    who has implemented a C compiler.)
    ...
    I'm not sure why you say I'm claiming to have written one; you don't
    believe me? OK.
    As far as I can tell, C doesn't have any feature too simple for you to misunderstand it. It's hard to believe that you have correctly
    implemented C despite having such an awful understand of what it takes
    to correctly implement it.

    In particular, your claim that C has only three name spaces argues
    against you having correctly implemented that fact in your C compiler.
    C's attributes are a relatively new feature, and I could imagine that
    you haven't bothered implementing that feature. However, the other four
    items in that list go all the way back to the first C standard, one of
    which is an unlimited family of name spaces.
    Possibly you have correctly handled all of the name spaces correctly,
    despite not realizing that they are called name spaces, just because
    each name space other than the ordinary identifiers name space has it's
    own unique syntax in which it is relevant.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.lang.c on Wed May 20 14:56:24 2026
    From Newsgroup: comp.lang.c

    In article <10uijv9$3i6dl$1@dont-email.me>, Bart <bc@freeuk.com> wrote:
    On 19/05/2026 20:58, David Brown wrote:
    On 19/05/2026 19:47, Bart wrote:

    (I still don't know why C has a separate namespace for labels.)

    There's a lot you don't know about C.

    It sounds like you don't know either.

    It's fine not to know everything about something. I doubt
    anyone posting here regularly knows *every* detail of C.

    The difference is being able to admit that it's ok to consult a
    reference before loudly proclaiming how much something about C
    sucks.

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.lang.c on Wed May 20 15:18:25 2026
    From Newsgroup: comp.lang.c

    In article <10ujm3r$3pnbb$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    [snip] I have almost never had need of "goto" or labels (excluding
    switch case labels, of course), and don't expect ever to do so in the >future.

    While I generally try to avoid it, there are times (in C in
    particular) when it really is the right tool; the inventors of C
    understood that. From the Plan 9 `fortunes` file: "If you want
    to go somewhere, goto is the best way to get there. K Thompson"

    Breaking out of nested loops without a lot of unnecessary
    ceremony is sort of an obvious example; jumping to common error
    handling code is another that is often cited (though see the
    Apple, "goto fail" bug for an example of how this can go bad).

    It's interesting to me that after Dijkstra's famous March 1968,
    "Go To Statement Considered Harmful" letter in CACM vol 11 no 3,
    language designers seemed to address the issue by identifying
    the primary useful patterns of `goto` use and codifying them as
    first-class constructs in new(er) languages: labeled loops (and
    corresponding elaborations on `break` etc) and exceptions are
    obvious, and Go's `defer` statement is a newer example.

    C came a few years after Dijkstra's letter, and I'm sure they
    were aware of it. But while it inherited `break` from BCPL, the
    only addition in this area is `continue`. DMR seemed to eschew
    a more complex language for one that included `goto`, presumably
    because it was intended for expert programmers judicious with
    its use.

    Once it escaped the lab, however....

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Wed May 20 17:38:14 2026
    From Newsgroup: comp.lang.c

    On 20/05/2026 17:18, Dan Cross wrote:
    In article <10ujm3r$3pnbb$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    [snip] I have almost never had need of "goto" or labels (excluding
    switch case labels, of course), and don't expect ever to do so in the
    future.

    While I generally try to avoid it, there are times (in C in
    particular) when it really is the right tool; the inventors of C
    understood that. From the Plan 9 `fortunes` file: "If you want
    to go somewhere, goto is the best way to get there. K Thompson"


    Certainly there are situations where people feel the most clear, simple, reliable and efficient way to handle a particular bit of code is with a "goto". Fair enough. Personally, I have almost never felt I am in that situation. I can think of perhaps four reasons why I might have fewer
    goto's than some other people :

    1. I stick to optimising compilers in almost all cases - some people may
    use goto rather than an extra bool flag or too, for efficiency purposes.
    I am in the happy position of letting the compiler generate the goto's.

    2. I almost never use dynamic memory, and thus don't have much use for a typical "goto error" idiom for handling failed malloc.

    3. My code is typically not meant to be portable, so I can use things
    like gcc's "cleanup" attribute that can replace the need of "goto error"
    or other handling when breaking out of inner loops.

    4. With C99 "inline" and modern tools that do inlining and other inter-procedural optimisations, there is often less benefit in nested
    loops that might need a multi-level break - you can split the code into separate functions and use "return" to break out of the middle of loops.

    So I don't suggest that nobody has need of "goto", or that all "gotos"
    are bad - merely that I very rarely have use of them myself.

    Breaking out of nested loops without a lot of unnecessary
    ceremony is sort of an obvious example; jumping to common error
    handling code is another that is often cited (though see the
    Apple, "goto fail" bug for an example of how this can go bad).

    It's interesting to me that after Dijkstra's famous March 1968,
    "Go To Statement Considered Harmful" letter in CACM vol 11 no 3,
    language designers seemed to address the issue by identifying
    the primary useful patterns of `goto` use and codifying them as
    first-class constructs in new(er) languages: labeled loops (and
    corresponding elaborations on `break` etc) and exceptions are
    obvious, and Go's `defer` statement is a newer example.

    C came a few years after Dijkstra's letter, and I'm sure they
    were aware of it. But while it inherited `break` from BCPL, the
    only addition in this area is `continue`. DMR seemed to eschew
    a more complex language for one that included `goto`, presumably
    because it was intended for expert programmers judicious with
    its use.

    Once it escaped the lab, however....


    There's more than one feature of C, and of every other programming
    language, that we'd all like to put back into Pandora's box. Of course,
    we'd all disagree strongly on which features those are!

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Wed May 20 16:41:45 2026
    From Newsgroup: comp.lang.c

    On 20/05/2026 15:22, David Brown wrote:
    On 20/05/2026 15:20, Bart wrote:
    On 20/05/2026 07:59, David Brown wrote:
    On 19/05/2026 23:16, Bart wrote:
    On 19/05/2026 20:58, David Brown wrote:
    On 19/05/2026 19:47, Bart wrote:

    (I still don't know why C has a separate namespace for labels.)

    There's a lot you don't know about C.

    It sounds like you don't know either.

    I don't know for sure - but I don't care that they are in separate
    namespaces, and I don't care about why other than for curiosity.  I
    think any code for which it matters, and code shares the same
    identifier for a label and a variable, is hopelessly badly written or
    extremely niche.

    So it /is/ pointless to have that separate namespace.

    I did not say that, and I can't fathom how you would conclude that -

    You said that code which depends on it is 'hopelessly badly written' or 'extremely niche'. That implies the vast majority of decent code will
    never need that feature. Ergo it is pointless.

    Unless you can think of a use-case where it would be essential?

    either from your own lack of relevant knowledge, or from what I wrote.

    What exactly is lacking from that knowledge? Do you even know yourself?

    I would imagine that having them as a separate namespace would be more convenient in a compiler - the scoping and lookup rules are
    significantly different from the namespace for variables and functions,
    and having separate namespaces means compilers don't have to check for conflicts.  There may be other good reasons for the separation, or it
    might be a historical artifact inherited from a predecessor language, or
    it might be that the C designers preferred separate namespaces and would only have combined them if they had good reason to do so.

    Having such an extra namespace for labels because it makes a compiler
    simpler does not make that useful for users.

    Yes, maybe the namespace trick makes it a little simpler to check for duplicates of labels and locals.

    But it also relies on label names only appearing in certain contexts.
    That means extensions such as gcc's label pointers need special syntax
    such as &&L, whereas function names can become pointers without even one
    '&' needed.

    Example (using gnu extension):

    void* P = &&fred;
    goto *P;

    puts("One");
    fred:
    puts("Two");

    In my language:

    ref label P := fred
    goto P

    println "One"
    fred:
    println "Two"

    Cleaner code /because/ label names are not special. And more typesafe:
    my language needs a label pointer; C is happy to jump to a float* pointer!

    Oh, I forgot, C *must* be superior here because I know nothing about
    these things. And you never use 'goto' anyway, not even computed goto,
    so this is irrelevant to you.

    I'm not planning to support those; many are poorly documented IMO and
    unintuitive to understand, hard to implement, and may have hidden
    depths of complexity.


    You can make up your own mind about how difficult these features are to implement in your own compiler, though I question the reality of these concerns - I think you just like to complain that they are hard.

    Try it yourself first then your opinions will carry more weight.


    And
    you can have the opinion that they are poorly documented, but we both
    know the reality is that you haven't bothered to try to read the documentation.

    I have, that's why I can say they are poorly documented.

    (A classic example is the C preprocessor, which is covered in a few paragraphs, but a full treatment would need 20 pages. And it shows: in
    2017 when I did my project, there were all kinds of corner cases
    involving the preprocessor that would give different results in
    different compilers.

    Now they are more standardised, although I suspect that implementations
    are sharing the one working version!)

      The fact that people use these features should indicate
    that they are well enough documented and understood for C programmers to use.

    Sure, especially VLAs: most uses I've seen of these seem to be
    inadvertent such as using N in 'const int N = 10' as the dimension.

    Meanwhile, how many know that you can write:

    {.p.y = 200, p.x = 100}

    as well as:

    {.p = {.x = 100, .y = 200}}

    So to any depth, in any order and with any number of duplicates.

      So are you trying to claim that you are particularly inept at
    reading and understanding about C language features?  I doubt that.

    I'd be happier with an honest reason - such as you don't like these
    features (for some non-technical reason), you don't find them useful yourself (fair enough),

    These too.

    and you therefore can't be bothered implementing
    them in your own tools (again, fair enough).  You made your C compiler
    for fun, no one else uses it, and you have no obligations to anyone
    else.  It's entirely up to you to pick the features you choose to
    support (as long as you don't make any claims to conformity).  You don't have to make up bullshit excuses for choosing not to implement C99
    features.

    I remember it took quite a few years for C99 features to become
    widespread. I imagine they would have been known about well in advance
    of 1999 too.

    So it can't have been that trivial.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Wed May 20 19:51:12 2026
    From Newsgroup: comp.lang.c

    On 20/05/2026 17:41, Bart wrote:
    On 20/05/2026 15:22, David Brown wrote:
    On 20/05/2026 15:20, Bart wrote:
    On 20/05/2026 07:59, David Brown wrote:
    On 19/05/2026 23:16, Bart wrote:
    On 19/05/2026 20:58, David Brown wrote:
    On 19/05/2026 19:47, Bart wrote:

    (I still don't know why C has a separate namespace for labels.)

    There's a lot you don't know about C.

    It sounds like you don't know either.

    I don't know for sure - but I don't care that they are in separate
    namespaces, and I don't care about why other than for curiosity.  I
    think any code for which it matters, and code shares the same
    identifier for a label and a variable, is hopelessly badly written
    or extremely niche.

    So it /is/ pointless to have that separate namespace.

    I did not say that, and I can't fathom how you would conclude that -

    You said that code which depends on it is 'hopelessly badly written' or 'extremely niche'. That implies the vast majority of decent code will
    never need that feature. Ergo it is pointless.

    No.

    First, a niche use is not pointless.

    Second, there can be reasons for defining a language in a particular
    way, even if users never take advantage of it. It can, for example,
    mean the implementation is easier. That's the case for a fair amount of
    what you might call "compile-time undefined behaviour" in C - some
    things are defined the way they are because it makes it easier for the implementation. If local variables and labels shared the same name
    space, then compilers would have to check for conflicts - as it stands,
    they do not.


    Unless you can think of a use-case where it would be essential?


    I can't think of one. But then, I almost never use labels and gotos.
    Maybe someone else can think of one.

    either from your own lack of relevant knowledge, or from what I wrote.

    What exactly is lacking from that knowledge? Do you even know yourself?


    You don't know why the name spaces are separate. I don't know either,
    as I have said.

    I would imagine that having them as a separate namespace would be more
    convenient in a compiler - the scoping and lookup rules are
    significantly different from the namespace for variables and
    functions, and having separate namespaces means compilers don't have
    to check for conflicts.  There may be other good reasons for the
    separation, or it might be a historical artifact inherited from a
    predecessor language, or it might be that the C designers preferred
    separate namespaces and would only have combined them if they had good
    reason to do so.

    Having such an extra namespace for labels because it makes a compiler simpler does not make that useful for users.


    So?

    Clearly, usefulness for users is more important than convenience for implementers. But if there is no harm to users either way, then simpler
    for implementers is a good idea. (Again, I do not know if there are
    other reasons for the separate name spaces. Maybe they do make things
    easier for users, or did so long ago when the design decision was made.)

    Yes, maybe the namespace trick makes it a little simpler to check for duplicates of labels and locals.

    But it also relies on label names only appearing in certain contexts.

    And that's fine. They can only appear in certain circumstances -
    preceding a colon to define the label, or as the subject of a "goto".

    That means extensions such as gcc's label pointers need special syntax
    such as &&L, whereas function names can become pointers without even one
    '&' needed.

    Oddly enough, I don't think the original designers of the C language
    made their decisions based on what a future compiler would add as an extension. gcc label pointers have no relevance to this discussion.


    And you can have the opinion that they are poorly documented, but we
    both know the reality is that you haven't bothered to try to read the
    documentation.

    I have, that's why I can say they are poorly documented.

    Really? Is this you finally saying you have read a part of one C
    standard version?


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.lang.c on Wed May 20 18:17:18 2026
    From Newsgroup: comp.lang.c

    In article <10ukkh6$2gc8$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    On 20/05/2026 17:18, Dan Cross wrote:
    In article <10ujm3r$3pnbb$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    [snip] I have almost never had need of "goto" or labels (excluding
    switch case labels, of course), and don't expect ever to do so in the
    future.

    While I generally try to avoid it, there are times (in C in
    particular) when it really is the right tool; the inventors of C
    understood that. From the Plan 9 `fortunes` file: "If you want
    to go somewhere, goto is the best way to get there. K Thompson"

    Certainly there are situations where people feel the most clear, simple, >reliable and efficient way to handle a particular bit of code is with a >"goto". Fair enough. Personally, I have almost never felt I am in that >situation. I can think of perhaps four reasons why I might have fewer >goto's than some other people :

    1. I stick to optimising compilers in almost all cases - some people may
    use goto rather than an extra bool flag or too, for efficiency purposes.
    I am in the happy position of letting the compiler generate the goto's.

    2. I almost never use dynamic memory, and thus don't have much use for a >typical "goto error" idiom for handling failed malloc.

    3. My code is typically not meant to be portable, so I can use things
    like gcc's "cleanup" attribute that can replace the need of "goto error"
    or other handling when breaking out of inner loops.

    4. With C99 "inline" and modern tools that do inlining and other >inter-procedural optimisations, there is often less benefit in nested
    loops that might need a multi-level break - you can split the code into >separate functions and use "return" to break out of the middle of loops.

    So I don't suggest that nobody has need of "goto", or that all "gotos"
    are bad - merely that I very rarely have use of them myself.

    I'm with you on almost all of these, though I find the code that
    has to check a boolean or other flag in an outer loop to be hard
    to read compared to the `goto` alternative often enough that I
    avoid it if I can. Of course, that's subjective. And I might
    point out that there are other categories of cleanup one might
    want to do, aside from just freeing memory (in the embedded
    space, perhaps clearing a flag or setting register state in some
    device to reset it...).

    I particularly agree with the use of a static inline to avoid
    some `goto`s. When the `goto fail` bug was announced, as an
    exercise, I rewrote Apple's code to demonstrate how one might
    eliminate the problematic pattern entirely:

    The original was something like:

    int
    thing_that_can_fail(void)
    {
    int err = FAILURE;
    void *p = malloc(...);
    if (p == NULL)
    goto fail;
    void *q = whatever(...);
    if (q == NULL)
    goto fail;
    err = something(p, q);
    if (err != 0)
    goto fail;
    err = otherthing(p, q);
    if (err != 0)
    goto fail;
    goto fail;
    err = thirdthing(p, q);
    if (err != 0)
    goto fail;
    return 0;
    fail:
    if (q != NULL)
    dispose_of_q(q);
    free(p);
    return err;
    }

    But using a small `static` inline function, we can rewrite this
    as two functions that separate resource allocation and whatever
    else from other fallable logic:

    static inline int
    do_thing_that_can_fail(void *p, void *q)
    {
    int err = something(p, q);
    if (err != 0)
    return err;
    err = otherthing(p, q);
    if (err != 0)
    return err;
    return thirdthing(p, q);
    }

    int
    thing_that_can_fail(void)
    {
    void *p = malloc(...);
    if (p == NULL)
    return FAILURE;
    void *q = whatever(...);
    if (q == NULL) {
    free(p);
    return FAILURE;
    }
    int ret = do_thing_that_can_fail(p, q);
    dispose_of_q(q);
    free(p);
    return ret;
    }

    The response (this was amongst a bunch of developers are my last
    company) was, "yeah, but you can't _always_ do that..." and that
    may be true; but you _often_ can. Regardless, it does not
    automatically follow that `goto` is the best option for dealing
    with this kind of failure. The Plan 9 kernel, for example, used
    something equivalent to setjmp/longjmp and a stack of jump
    buffers to provide a primitive exception handling mechanism in
    that system's decidedly non-ISO dialect of C.

    Breaking out of nested loops without a lot of unnecessary
    ceremony is sort of an obvious example; jumping to common error
    handling code is another that is often cited (though see the
    Apple, "goto fail" bug for an example of how this can go bad).

    It's interesting to me that after Dijkstra's famous March 1968,
    "Go To Statement Considered Harmful" letter in CACM vol 11 no 3,
    language designers seemed to address the issue by identifying
    the primary useful patterns of `goto` use and codifying them as
    first-class constructs in new(er) languages: labeled loops (and
    corresponding elaborations on `break` etc) and exceptions are
    obvious, and Go's `defer` statement is a newer example.

    C came a few years after Dijkstra's letter, and I'm sure they
    were aware of it. But while it inherited `break` from BCPL, the
    only addition in this area is `continue`. DMR seemed to eschew
    a more complex language for one that included `goto`, presumably
    because it was intended for expert programmers judicious with
    its use.

    Once it escaped the lab, however....

    There's more than one feature of C, and of every other programming
    language, that we'd all like to put back into Pandora's box. Of course, >we'd all disagree strongly on which features those are!

    That's the great thing about opinions: there are so many of
    them!

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Wed May 20 21:14:14 2026
    From Newsgroup: comp.lang.c

    On 20/05/2026 18:51, David Brown wrote:
    On 20/05/2026 17:41, Bart wrote:

    First, a niche use is not pointless.

    A niche use that takes advantage of some accidental quirk in a language?
    One that wouldn't exist if the quirk wasn't there; that sort of niche use?

    Over 50 years of use, every misfeature of C has been exploited by
    /somebody/. One reason why the language couldn't properly evolve.

    You don't know why the name spaces are separate.  I don't know either,
    as I have said.

    But I can have a good idea of the implications both by there being a
    separate namespace, or not. You snipped my example where it caused a limitation in the syntax.

    Yes, maybe the namespace trick makes it a little simpler to check for
    duplicates of labels and locals.

    But it also relies on label names only appearing in certain contexts.

    And that's fine.  They can only appear in certain circumstances -
    preceding a colon to define the label, or as the subject of a "goto".

    As I said, limitations; 'goto (L)' is not allowed, for example, but
    '(F)()' is as well as 'case (A):'.

    I have, that's why I can say they are poorly documented.

    Really?  Is this you finally saying you have read a part of one C
    standard version?

    I've read plenty of the standard especially in 2017. Information for implementing C and providing headers had to be gleaned from multiple
    sources. There is also testing: the C standard doesn't provide a
    test-suite to verify an implementation.

    Since then, then no, I don't routinely look inside it.

    So what? People can have opinions on languages, compare one to another, speculate on possible new features or modifying existing ones, etc,
    based on their long personal experiences as /users/.

    Some may also have experience as developers, and some even of developing languages in a similar field.

    I also admit my implementation was casual. I had a particular set of
    aims, which were largely achieved.

    (The first language I implemented, not one of mine, didn't have a formal standard. You just picked it up from examples. But it was also a machine-oriented language, so it was adapted to the target to a certain extent.

    That also was the case for other languages I used in the 70s, in that
    the implementation for a particular platform became the standard, and
    you used reference manuals for that version.


    TLDR: you guys place too much importance on 'the C standard', and mainly
    use it to batter me over the head with.

    You don't 'own' C. There is no copyright on it. Anyone can use it as
    casually or as intensely as they like. Anyone can choose to create as professional or as casual a version as they like. Anyone choose to
    pontificate on things they like or dislike.

    Anyone can choose to fork C and create a language that is slightly
    different, or a lot different, but they would ideally make that clear.

    Hmm, still a bit long! In that case: TLDR: I like to deal with C
    casually (like every language I use). If you don't like it, then too bad.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Wed May 20 16:39:42 2026
    From Newsgroup: comp.lang.c

    On 5/20/2026 6:20 AM, Bart wrote:
    [...]

    There are lots of different qualities of general purpose C compilers, if
    you look outside the big 3 (gcc, clang, msvc).

    I used to use DMC, Pelles C, lccwin32 extensively, and still use Tiny C.

    I remember conversing with you and installed all of those as well,
    already had Pelles wrt its early support for C11 threading _and_ atomic.
    For my old hash cipher. Btw, thanks for that!

    [...]

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Wed May 20 16:47:59 2026
    From Newsgroup: comp.lang.c

    On 5/20/2026 8:18 AM, Dan Cross wrote:
    In article <10ujm3r$3pnbb$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    [snip] I have almost never had need of "goto" or labels (excluding
    switch case labels, of course), and don't expect ever to do so in the
    future.

    While I generally try to avoid it, there are times (in C in
    particular) when it really is the right tool; the inventors of C
    understood that. From the Plan 9 `fortunes` file: "If you want
    to go somewhere, goto is the best way to get there. K Thompson"

    Oh shit. Side note. Did you ever hear about a nasty race condition in
    Plan 9 wrt some lock-free thing, or even a mutex acquisition? The Plan9 problem. I remember conversing about with with Alex Terekhov way back in comp.programming.threads.

    [...]
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Wed May 20 16:52:03 2026
    From Newsgroup: comp.lang.c

    On 5/20/2026 7:40 AM, James Kuyper wrote:
    On 19/05/2026 23:16, Bart wrote:
    On 19/05/2026 20:58, David Brown wrote:
    On 19/05/2026 19:47, Bart wrote:
    ...
    Implementing C's three namespaces doesn't require knowing why they
    exist, only that they do.

    "namespaces" are a C++ feature, which means something quite different
    from "name spaces". Both C and C++ have name spaces. C++ has only three: labels, macros, and ordinary identifiers. However, C has 4 name spaces
    and an unlimited number of members of each of two families of name spaces:
    [...]

    Namespaces in C:

    ct_vector_field_***

    (namespace)_(feature)_(drill-down)_***


    ? ;^D

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Thu May 21 02:30:32 2026
    From Newsgroup: comp.lang.c

    On 2026-05-20 12:55, Bart wrote:
    On 20/05/2026 05:24, Janis Papanagnou wrote:
    [...]

    2. Are you sure that above code is valid (error-free) Algol 68 code?

    I'm not sure about that. - My old textbook says that you can use these
    identifiers *after* you've set them (not before). And skimming through
    the standard document, the Revised Report, I could not find anything.
    If there is; can you point me to the relevant chapter, please?

    It could also just not have been defined in the standard. (The above
    code or Genie's behavior would then not tell anything relevant about
    the language. - It would just expose yet another example of code that
    an experienced programmer would just not write that way.)

    You seem to have tested that with the Genie interpreter? - This would
    not say anything about what the Algol 68 standard says, mind.

    You seem determined to prove Bart wrong, aren't you?

    You made a claim that looked strange, and I had been asking you
    whether you can point to the standard to clarify that question.
    It's so simple! - Or it would be so simple if you'd not as usual
    wrongly assume malevolent behavior.

    And yes, you have been wrong! (As so often, sadly.) - You falsely
    assumed some semantics in Algol 68 just because you wanted it to
    support your argument. For that you took an arbitrary Algol 68
    interpreter to "prove" the intention of the Algol 68 language
    instead of pointing to the Algol 68 standard where such behavior
    would be defined (or not).

    As opposed to you I was trying to determine the truth about it,
    and, as I wrote - you trimmed that part in the quote of my post
    above:
    "I've sent a mail to Marcel to clarify that, i.e. the behavior
    of Genie, and whether that's standard behavior, undefined, or
    an oversight or bug."
    You obviously trimmed it to make an open question and doubt of
    some facts - and my intention to focus on the fact! - look like
    a personal attack. (You exposed malevolence in communication!)

    Here's the answer of Marcel concerning the behavior of Genie:

    "I could write a long story on this, but I will summarize it -
    this is a bug"

    Janis

    PS: Bart, I would wish for you and for us all if you'd overcome
    your pathological behavior. But only *you* can do something about
    it.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Thu May 21 02:45:38 2026
    From Newsgroup: comp.lang.c

    On 2026-05-20 06:38, Janis Papanagnou wrote:
    On 2026-05-19 18:48, David Brown wrote:
    On 19/05/2026 18:31, Bart wrote:
    [...]

    OK.  So there was a real language that worked that way, half a century
    ago.

    It's not that clear, IMO; all I see in his post is the result of a
    specific tool he uses, and for a specific case (not for variables).
    (See my recent post a few minutes ago for the details.)

    David, the answer of my emailed question arrived; the behavior of
    the Genie interpreter in case of identity relations is just a bug!

    (Bart wrongly assumed and without evidence from the standard that
    the Algol 68 language would support his claim.)

    Algol 68 is a well designed language and the scope rules are as we
    expect sensibly chosen; entities are valid within the block scope
    and their "existence", when you can use them, starts with their
    declaration (not before).

    Most people don't know Algol 68. I suggest to take Bart's opinions
    on that language with the same reservations as his opinions about
    the "C" language. He's widely ignorant and unwilling to understand
    the facts. Take his statements with a grain of salt - and, if in
    doubt, better just ignore him.

    Janis

    [...]

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Thu May 21 02:21:20 2026
    From Newsgroup: comp.lang.c

    On 21/05/2026 01:30, Janis Papanagnou wrote:
    On 2026-05-20 12:55, Bart wrote:
    On 20/05/2026 05:24, Janis Papanagnou wrote:
    [...]

    2. Are you sure that above code is valid (error-free) Algol 68 code?

    I'm not sure about that. - My old textbook says that you can use these
    identifiers *after* you've set them (not before). And skimming through
    the standard document, the Revised Report, I could not find anything.
    If there is; can you point me to the relevant chapter, please?

    It could also just not have been defined in the standard. (The above
    code or Genie's behavior would then not tell anything relevant about
    the language. - It would just expose yet another example of code that
    an experienced programmer would just not write that way.)

    You seem to have tested that with the Genie interpreter? - This would
    not say anything about what the Algol 68 standard says, mind.

    You seem determined to prove Bart wrong, aren't you?

    You made a claim that looked strange, and I had been asking you
    whether you can point to the standard

    To the Algol68 standard? I wouldn't have a clue; probably only a handful
    of people on the planet do.


    to clarify that question.
    It's so simple! - Or it would be so simple if you'd not as usual
    wrongly assume malevolent behavior.

    And yes, you have been wrong! (As so often, sadly.) - You falsely
    assumed some semantics in Algol 68 just because you wanted it to
    support your argument.

    Which were supported by pretty much the only implementation available.

    For that you took an arbitrary Algol 68
    interpreter

    So give me links to some others, or to playgrounds.

    to "prove" the intention of the Algol 68 language
    instead of pointing to the Algol 68 standard where such behavior
    would be defined (or not).


    I was asked if any real-world languages had scopes spanning the entire
    block for a declaration part-way through. I tried Algol68 via A68G and
    it appeared to support that.

    As opposed to you I was trying to determine the truth about it,
    and, as I wrote - you trimmed that part in the quote of my post
    above:
      "I've sent a mail to Marcel to clarify that, i.e. the behavior
       of Genie, and whether that's standard behavior, undefined, or
       an oversight or bug."
    You obviously trimmed it to make an open question and doubt of
    some facts - and my intention to focus on the fact! - look like
    a personal attack. (You exposed malevolence in communication!)

    Why were you so convinced I must be wrong?


    Here's the answer of Marcel concerning the behavior of Genie:

      "I could write a long story on this, but I will summarize it -
       this is a bug"


    So, what should the behaviour be? That is, given:

    INT a:=1; # a1 #
    BEGIN
    ..... # (1) #
    INT a:=2; # a2 #
    .....
    END

    which version of 'a' is in scope in the region marked (1)?

    It can either be a1, a2 or none.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Thu May 21 02:31:39 2026
    From Newsgroup: comp.lang.c

    On 21/05/2026 01:45, Janis Papanagnou wrote:
    On 2026-05-20 06:38, Janis Papanagnou wrote:
    On 2026-05-19 18:48, David Brown wrote:
    On 19/05/2026 18:31, Bart wrote:
    [...]

    OK.  So there was a real language that worked that way, half a
    century ago.

    It's not that clear, IMO; all I see in his post is the result of a
    specific tool he uses, and for a specific case (not for variables).
    (See my recent post a few minutes ago for the details.)

    David, the answer of my emailed question arrived; the behavior of
    the Genie interpreter in case of identity relations is just a bug!

    (Bart wrongly assumed and without evidence from the standard that
    the Algol 68 language would support his claim.)

    Algol 68 is a well designed language and the scope rules are as we
    expect sensibly chosen; entities are valid within the block scope
    and their "existence", when you can use them, starts with their
    declaration (not before).

    So why does this work:

    fred(9999);

    PROC fred = (INT n)VOID: print((n, newline));

    print("end")

    Another bug?

    Most people don't know Algol 68. I suggest to take Bart's opinions
    on that language with the same reservations as his opinions about
    the "C" language. He's widely ignorant and unwilling to understand
    the facts. Take his statements with a grain of salt - and, if in
    doubt, better just ignore him.

    Oh, of course. The fact that A68G has existed for 25 years with this
    'bug' that nobody has detected or bothered to check for is perfectly fine.

    I guess you're going to get the credit for finding this bug rather than me!
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Wed May 20 18:51:16 2026
    From Newsgroup: comp.lang.c

    Bart <bc@freeuk.com> writes:
    On 21/05/2026 01:30, Janis Papanagnou wrote:
    [...]
    You made a claim that looked strange, and I had been asking you
    whether you can point to the standard

    To the Algol68 standard? I wouldn't have a clue; probably only a
    handful of people on the planet do.

    Please take this to comp.lang.misc. There's already some discussion
    of Algol 68 there.

    [SNIP]
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.lang.c on Thu May 21 02:55:21 2026
    From Newsgroup: comp.lang.c

    In article <10ulh7g$coh9$1@dont-email.me>,
    Chris M. Thomasson <chris.m.thomasson.1@gmail.com> wrote:
    On 5/20/2026 8:18 AM, Dan Cross wrote:
    In article <10ujm3r$3pnbb$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    [snip] I have almost never had need of "goto" or labels (excluding
    switch case labels, of course), and don't expect ever to do so in the
    future.

    While I generally try to avoid it, there are times (in C in
    particular) when it really is the right tool; the inventors of C
    understood that. From the Plan 9 `fortunes` file: "If you want
    to go somewhere, goto is the best way to get there. K Thompson"

    Oh shit. Side note. Did you ever hear about a nasty race condition in
    Plan 9 wrt some lock-free thing, or even a mutex acquisition? The Plan9 >problem. I remember conversing about with with Alex Terekhov way back in >comp.programming.threads.

    [...]

    Yes. There were issues discovered with multiprocessor sleep and
    wakeup on x86 during the early Pentium Pro days, due to a
    misunderstanding of the memory model. Russ Cox has a summary
    write-up here: https://swtch.com/spin/

    I have no idea who Alex Terekhov is. This has very, very little
    to do with C.

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Thu May 21 08:45:43 2026
    From Newsgroup: comp.lang.c

    On 20/05/2026 22:14, Bart wrote:
    On 20/05/2026 18:51, David Brown wrote:
    On 20/05/2026 17:41, Bart wrote:

    First, a niche use is not pointless.

    A niche use that takes advantage of some accidental quirk in a language?
    One that wouldn't exist if the quirk wasn't there; that sort of niche use?

    Over 50 years of use, every misfeature of C has been exploited by / somebody/. One reason why the language couldn't properly evolve.


    So do you have an example of where code has been written to take
    advantage of the separate name spaces? I don't - I merely can't rule
    out niche cases. I could imagine some situations - perhaps from machine
    or human translation from other languages, or old implementations with
    very short identifier lengths.

    You seem obsessed with calling every aspect of C a "misfeature". This
    is, IMHO, neither a feature nor a misfeature of the language - it is
    basically irrelevant to almost everyone. I don't know why it upsets you
    so much.

    The C language /has/ evolved, and has evolved some features that are
    mostly liked, a few that are disliked by many, and others that some
    people like and some dislike. But it certainly evolves slowly - the
    standards committee have a strong commitment to backwards compatibility
    of code and to keeping the language stable, only making changes when
    there are very significant benefits. Their aim is not to make a
    "perfect" language or a language for all purposes - rather, it is simply
    to let people continue to use C for purposes for which C works well.

    When you have a home-made language and are the language designer,
    compiler implementer, and sole user, you can muck around with the
    language as you choose - changing things daily. You don't have to
    consider other users. Real-world (sorry, "mainstream") languages are
    not like that.

    You don't know why the name spaces are separate.  I don't know either,
    as I have said.

    But I can have a good idea of the implications both by there being a separate namespace, or not. You snipped my example where it caused a limitation in the syntax.


    Yes, it was irrelevant.

    Yes, maybe the namespace trick makes it a little simpler to check for
    duplicates of labels and locals.

    But it also relies on label names only appearing in certain contexts.

    And that's fine.  They can only appear in certain circumstances -
    preceding a colon to define the label, or as the subject of a "goto".

    As I said, limitations; 'goto (L)' is not allowed, for example, but '(F)
    ()' is as well as 'case (A):'.


    And I can't fit my car in my bedroom cupboard. I don't consider that a limitation of my choice of car.

    I have, that's why I can say they are poorly documented.

    Really?  Is this you finally saying you have read a part of one C
    standard version?

    I've read plenty of the standard especially in 2017. Information for implementing C and providing headers had to be gleaned from multiple sources. There is also testing: the C standard doesn't provide a test-
    suite to verify an implementation.


    All the features you have whined about as "poorly documented" are
    documented in the standards. There are also countless other perfectly
    good resources around describing how to use them. (For an
    implementation, you need to read the standard in detail - for using
    them, tutorials or other references are usually fine for sensible use.)

    The C standards are standards, not test suites. There are plenty of
    test suites and conformance tests available for C compilers - most cost
    money, or are written in connection with compilers.

    Since then, then no, I don't routinely look inside it.

    So what? People can have opinions on languages, compare one to another, speculate on possible new features or modifying existing ones, etc,
    based on their long personal experiences as /users/.


    You can have an opinion from ignorance, as many people have told you.
    You can't expect that opinion to be respected.

    Some may also have experience as developers, and some even of developing languages in a similar field.

    I also admit my implementation was casual. I had a particular set of
    aims, which were largely achieved.


    OK. And that's fine. I've no problem with that. I have a problem with
    you then using it as evidence that you are an expert on languages,
    compilers or C.

    (The first language I implemented, not one of mine, didn't have a formal standard. You just picked it up from examples. But it was also a machine-oriented language, so it was adapted to the target to a certain extent.

    That also was the case for other languages I used in the 70s, in that
    the implementation for a particular platform became the standard, and
    you used reference manuals for that version.


    TLDR: you guys place too much importance on 'the C standard', and mainly
    use it to batter me over the head with.


    C is a language defined by the standards. That makes the standards
    important.

    You don't 'own' C. There is no copyright on it. Anyone can use it as casually or as intensely as they like. Anyone can choose to create as professional or as casual a version as they like. Anyone choose to pontificate on things they like or dislike.

    The C23 draft open on my desktop at the moment says "© ISO 2024 - All
    rights reserved" on every page. It /is/ copyrighted, and it is a
    defined and standardised language.

    People are free to write C code however they want. They are free to
    write compilers for C. They can write a compiler for a language
    somewhat like C, but not fully. But if they try to give the impression
    that it is a "C compiler" - especially if they imply it conforms to a particular standard - then we are free to call them out.


    Anyone can choose to fork C and create a language that is slightly different, or a lot different, but they would ideally make that clear.


    Yes - if they make it clear that the language is not C.

    Hmm, still a bit long! In that case: TLDR: I like to deal with C
    casually (like every language I use). If you don't like it, then too bad.


    I've no problem with people treating C casually. But I /do/ have a
    problem with people claiming C is something that it is not, or
    deliberately lying about it or misrepresenting it (or anything else - it
    just happens that C is the topic of this newsgroup). Getting things
    wrong is not lying - continuing to repeat the misinformation when you
    have been corrected is lying.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Thu May 21 09:56:58 2026
    From Newsgroup: comp.lang.c

    On 20/05/2026 20:17, Dan Cross wrote:
    In article <10ukkh6$2gc8$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    On 20/05/2026 17:18, Dan Cross wrote:
    In article <10ujm3r$3pnbb$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    [snip] I have almost never had need of "goto" or labels (excluding
    switch case labels, of course), and don't expect ever to do so in the
    future.

    While I generally try to avoid it, there are times (in C in
    particular) when it really is the right tool; the inventors of C
    understood that. From the Plan 9 `fortunes` file: "If you want
    to go somewhere, goto is the best way to get there. K Thompson"

    Certainly there are situations where people feel the most clear, simple,
    reliable and efficient way to handle a particular bit of code is with a
    "goto". Fair enough. Personally, I have almost never felt I am in that
    situation. I can think of perhaps four reasons why I might have fewer
    goto's than some other people :

    1. I stick to optimising compilers in almost all cases - some people may
    use goto rather than an extra bool flag or too, for efficiency purposes.
    I am in the happy position of letting the compiler generate the goto's.

    2. I almost never use dynamic memory, and thus don't have much use for a
    typical "goto error" idiom for handling failed malloc.

    3. My code is typically not meant to be portable, so I can use things
    like gcc's "cleanup" attribute that can replace the need of "goto error"
    or other handling when breaking out of inner loops.

    4. With C99 "inline" and modern tools that do inlining and other
    inter-procedural optimisations, there is often less benefit in nested
    loops that might need a multi-level break - you can split the code into
    separate functions and use "return" to break out of the middle of loops.

    So I don't suggest that nobody has need of "goto", or that all "gotos"
    are bad - merely that I very rarely have use of them myself.

    I'm with you on almost all of these, though I find the code that
    has to check a boolean or other flag in an outer loop to be hard
    to read compared to the `goto` alternative often enough that I
    avoid it if I can. Of course, that's subjective. And I might
    point out that there are other categories of cleanup one might
    want to do, aside from just freeing memory (in the embedded
    space, perhaps clearing a flag or setting register state in some
    device to reset it...).

    Agreed.


    I particularly agree with the use of a static inline to avoid
    some `goto`s. When the `goto fail` bug was announced, as an
    exercise, I rewrote Apple's code to demonstrate how one might
    eliminate the problematic pattern entirely:


    (The mixture of tabs and spaces in your post resulted in a bit messed up indentation in the quotation in my reply. I've tried to fix it up to
    match the original indentation, but be warned it might look different
    when you view it or re-quote it.)

    The original was something like:

    int
    thing_that_can_fail(void)
    {
    int err = FAILURE;
    void *p = malloc(...);
    if (p == NULL)
    goto fail;
    void *q = whatever(...);
    if (q == NULL)
    goto fail;
    err = something(p, q);
    if (err != 0)
    goto fail;
    err = otherthing(p, q);
    if (err != 0)
    goto fail;
    goto fail;
    err = thirdthing(p, q);
    if (err != 0)
    goto fail;
    return 0;
    fail:
    if (q != NULL)
    dispose_of_q(q);
    free(p);
    return err;
    }

    But using a small `static` inline function, we can rewrite this
    as two functions that separate resource allocation and whatever
    else from other fallable logic:

    static inline int
    do_thing_that_can_fail(void *p, void *q)
    {
    int err = something(p, q);
    if (err != 0)
    return err;
    err = otherthing(p, q);
    if (err != 0)
    return err;
    return thirdthing(p, q);
    }

    int
    thing_that_can_fail(void)
    {
    void *p = malloc(...);
    if (p == NULL)
    return FAILURE;
    void *q = whatever(...);
    if (q == NULL) {
    free(p);
    return FAILURE;
    }
    int ret = do_thing_that_can_fail(p, q);
    dispose_of_q(q);
    free(p);
    return ret;
    }

    The response (this was amongst a bunch of developers are my last
    company) was, "yeah, but you can't _always_ do that..." and that
    may be true; but you _often_ can.

    Exactly. Some people worry do much about when you can't do something,
    or what might go wrong if things had been different. Use the best
    technique you can for the code at hand, and if you can't use that
    technique in one place, use something else in that one place.

    Regardless, it does not
    automatically follow that `goto` is the best option for dealing
    with this kind of failure. The Plan 9 kernel, for example, used
    something equivalent to setjmp/longjmp and a stack of jump
    buffers to provide a primitive exception handling mechanism in
    that system's decidedly non-ISO dialect of C.


    There are other ways to avoid or catch such errors, without changing the structure or the use of "goto". One is to use static error checking. I
    don't know the age of this code, but from gcc 6 onwards "-Wall" will
    catch the "misleading indentation" of the double "goto fail". (gcc also
    spots that the first "goto fail" skips the initialisation of "q", so it
    is tested and possibly disposed-of when uninitialised. But that might
    be an artifact of your paraphrasing of the original code.) Other static
    error checkers would also no doubt be able to spot the bug.

    There is also the question of brace style. That is something that
    people have lots of different opinions on, but there's no doubt that if
    the author had used a style that required the use of braces even for single-statement blocks, such as the so-called "One True Brace Style",
    then the error could not have occurred.

    Personally, I allow a single short statement on the same line as the
    "if". But if the statement is long, or I think the code is clearer
    having it on a separate line, or if there is an "else" clause, I have it
    in braces :

    if (!p) goto fail;

    if (!p) {
    goto fail;
    }

    For me, this keeps everything simple, consistent, difficult to misread,
    fits well with version control and other line-by-line comparisons, and
    has a good balance between compactness and verbosity.

    Other people, of course, have other style preferences, with different
    pros and cons. OTBS, or my variation, would have made the error in this
    code impossible - but it would not have hindered other kinds of bugs.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Thu May 21 10:16:27 2026
    From Newsgroup: comp.lang.c

    On 21/05/2026 02:45, Janis Papanagnou wrote:
    On 2026-05-20 06:38, Janis Papanagnou wrote:
    On 2026-05-19 18:48, David Brown wrote:
    On 19/05/2026 18:31, Bart wrote:
    [...]

    OK.  So there was a real language that worked that way, half a
    century ago.

    It's not that clear, IMO; all I see in his post is the result of a
    specific tool he uses, and for a specific case (not for variables).
    (See my recent post a few minutes ago for the details.)

    David, the answer of my emailed question arrived; the behavior of
    the Genie interpreter in case of identity relations is just a bug!

    (Bart wrongly assumed and without evidence from the standard that
    the Algol 68 language would support his claim.)

    Algol 68 is a well designed language and the scope rules are as we
    expect sensibly chosen; entities are valid within the block scope
    and their "existence", when you can use them, starts with their
    declaration (not before).

    Most people don't know Algol 68. I suggest to take Bart's opinions
    on that language with the same reservations as his opinions about
    the "C" language. He's widely ignorant and unwilling to understand
    the facts. Take his statements with a grain of salt - and, if in
    doubt, better just ignore him.

    Janis


    OK. I know very little about Algol 68, and don't have the interest to
    bother checking such details myself. Your description makes sense to
    me, both of the way the language is intended to work, and that there is
    an oddity or bug in a particular implementation. "It makes sense to me"
    is, of course, not a particularly strong argument. But I don't think it
    is worth going into more detail here in c.l.c.

    Bart does have a tendency to mix up "this is how the language is
    designed" and "this is what a particular implementation does". With his
    own languages, that's fine - there is only one implementation, and only
    a rough description of the language, so the two coincide. It is also
    how pre-standardisation languages tend to work. But it is not how C
    works, nor, as I understand it, how Algol68 works.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From gazelle@gazelle@shell.xmission.com (Kenny McCormack) to comp.lang.c on Thu May 21 11:46:21 2026
    From Newsgroup: comp.lang.c

    In article <10uloem$e6bl$1@kst.eternal-september.org>,
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    Bart <bc@freeuk.com> writes:
    On 21/05/2026 01:30, Janis Papanagnou wrote:
    [...]
    You made a claim that looked strange, and I had been asking you
    whether you can point to the standard

    To the Algol68 standard? I wouldn't have a clue; probably only a
    handful of people on the planet do.

    Please take this to comp.lang.misc. There's already some discussion
    of Algol 68 there.

    The Lord and Master has spoken. All must obey.

    Yes, even JP.
    --
    Reading any post by Fred Hodgin, you're always faced with the choice of:
    lunatic, moron, or troll.

    I always try to be generous and give benefit of the doubt, by assuming troll. --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Thu May 21 12:56:59 2026
    From Newsgroup: comp.lang.c

    On 21/05/2026 07:45, David Brown wrote:
    On 20/05/2026 22:14, Bart wrote:
    On 20/05/2026 18:51, David Brown wrote:
    On 20/05/2026 17:41, Bart wrote:

    First, a niche use is not pointless.

    A niche use that takes advantage of some accidental quirk in a
    language? One that wouldn't exist if the quirk wasn't there; that sort
    of niche use?

    Over 50 years of use, every misfeature of C has been exploited by /
    somebody/. One reason why the language couldn't properly evolve.


    So do you have an example of where code has been written to take
    advantage of the separate name spaces?

    No. This is why I claimed it was pointless.

    (I did do a survey of my codebases to find examples of label names
    shadowing local identifiers, but found no instances. I didn't post the
    results (afaicr) since the code sample was too small, but it would be a massive job to do a bigger test.)

      I don't - I merely can't rule
    out niche cases.  I could imagine some situations - perhaps from machine
    or human translation from other languages, or old implementations with
    very short identifier lengths.

    Yeah, a C implementation that only has 'a' to 'z' variables, but also
    has a separate set of 'a' to 'z' labels!

    You seem obsessed with calling every aspect of C a "misfeature".

    Yes, it is astounding how much there is. Some can be excused due to its
    age, but also lots could have been fixed long ago.

    Some of it is an actual nuisance, but quite a bit is also aesthetically displeasing.

    You didn't like my example of being able to write '(F)(x)' (or 'int
    (a);') but not 'goto (L)', but the anomaly is there.

    There is also not being able to write 'L:}'; this one bites.


    But I can have a good idea of the implications both by there being a
    separate namespace, or not. You snipped my example where it caused a
    limitation in the syntax.


    Yes, it was irrelevant.

    And it's irrelevant because? It was a minor consequence due to
    restrictions on where labels can appear, in turn due to not sharing the
    same namespace as ordinary identifiers.

    Irrelevant because it's part of a language extension? Those tend to
    become standard.


    You can have an opinion from ignorance, as many people have told you.
    You can't expect that opinion to be respected.

    That's just rubbish, and an attempt to stifle criticism.

    Somebody asks why do I have to do X and not Y? Y makes more sense, it is easier etc.

    The response here will be because the Standard says so. End of discussion.

    Somebody was able to voice that opinion using their own experience and
    common sense. It may be valid; maybe other languages do do Y instead of X.

    However all anyone here wants to suggest that that person is ignorant
    because they haven't read the standard. Well, they might read the
    standard then find that Y is still better!


    You don't 'own' C. There is no copyright on it. Anyone can use it as
    casually or as intensely as they like. Anyone can choose to create as
    professional or as casual a version as they like. Anyone choose to
    pontificate on things they like or dislike.

    The C23 draft open on my desktop at the moment says "© ISO 2024 - All rights reserved" on every page.  It /is/ copyrighted, and it is a
    defined and standardised language.

    The standards document itself might be copyrighted, not the language.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.lang.c on Thu May 21 12:18:00 2026
    From Newsgroup: comp.lang.c

    In article <10umdsa$hnml$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    On 20/05/2026 20:17, Dan Cross wrote:
    [snip]
    I particularly agree with the use of a static inline to avoid
    some `goto`s. When the `goto fail` bug was announced, as an
    exercise, I rewrote Apple's code to demonstrate how one might
    eliminate the problematic pattern entirely:


    (The mixture of tabs and spaces in your post resulted in a bit messed up >indentation in the quotation in my reply. I've tried to fix it up to
    match the original indentation, but be warned it might look different
    when you view it or re-quote it.)

    Apologies for that; I suspect that was my editor trying to be
    "helpful": I used spaces in the example, but usually use tabs;
    I probably should have just stuck with the latter for
    consistency.

    [snip code examples]

    The response (this was amongst a bunch of developers are my last
    company) was, "yeah, but you can't _always_ do that..." and that
    may be true; but you _often_ can.

    Exactly. Some people worry do much about when you can't do something,
    or what might go wrong if things had been different. Use the best
    technique you can for the code at hand, and if you can't use that
    technique in one place, use something else in that one place.

    Yes.

    Regardless, it does not
    automatically follow that `goto` is the best option for dealing
    with this kind of failure. The Plan 9 kernel, for example, used
    something equivalent to setjmp/longjmp and a stack of jump
    buffers to provide a primitive exception handling mechanism in
    that system's decidedly non-ISO dialect of C.

    There are other ways to avoid or catch such errors, without changing the >structure or the use of "goto". One is to use static error checking. I >don't know the age of this code, but from gcc 6 onwards "-Wall" will
    catch the "misleading indentation" of the double "goto fail". (gcc also >spots that the first "goto fail" skips the initialisation of "q", so it
    is tested and possibly disposed-of when uninitialised. But that might
    be an artifact of your paraphrasing of the original code.) Other static >error checkers would also no doubt be able to spot the bug.

    (The uninitialized `q` error was my mistake; this was only meant
    to be illustrative, not actual working code!)

    There is also the question of brace style. That is something that
    people have lots of different opinions on, but there's no doubt that if
    the author had used a style that required the use of braces even for >single-statement blocks, such as the so-called "One True Brace Style",
    then the error could not have occurred.

    Personally, I allow a single short statement on the same line as the
    "if". But if the statement is long, or I think the code is clearer
    having it on a separate line, or if there is an "else" clause, I have it
    in braces :

    if (!p) goto fail;

    if (!p) {
    goto fail;
    }

    For me, this keeps everything simple, consistent, difficult to misread,
    fits well with version control and other line-by-line comparisons, and
    has a good balance between compactness and verbosity.

    Other people, of course, have other style preferences, with different
    pros and cons. OTBS, or my variation, would have made the error in this >code impossible - but it would not have hindered other kinds of bugs.

    This writeup of the original bug is pretty good: https://dwheeler.com/essays/apple-goto-fail.html

    He also recommends several of the techniques you do.

    In this particular case, the bug _should_ have been caught.
    Some combination of rigorous testing, static analysis, and
    manual review ought to have prevented it; that the bug made it
    into production software anyway is a software engineering
    failure.

    I think one can level some reasonable criticism at the language,
    however. The `goto error;` idiom is used in C because there are
    few alternatives for cleanup handling on failure. Modulo what
    we discussed before, that code can _often_ be restructured to
    avoid it, but sometimes it can't, and frequently it just isn't.

    In contrast, newer languages give you more expressive power in
    this regard: Go has the `defer` keyword to register a closure
    that runs when the enclosing function returns:

    ```go
    f, err := os.Open(...)
    if err != nil {
    return err
    }
    defer f.Close()
    ...
    ```

    The list of deferred closures will be run whenever the function
    returns, no matter what path it takes. You can't accidentally
    omit the close.

    Similarly, the RAII idiom prevalent in C++ and Rust uses object
    destructors that are automatically run when something goes out
    of scope to do the cleanup. C++ pairs that with
    exceptions (arguably worse than goto) while Rust represents
    errors with sum types and a little bit of syntactic sugar with
    the `?` operator. In either case, the descriptors run and do
    the cleanup, regardless of whether the return was an error path
    or a success path.

    Other languages feature "linear types", instances of which must
    be used exactly once: failure to cleanup on an error path is a
    compile time error. This means that you can't forget to
    deallocate memory, close a file, or unlock a mutex, for example,
    but I don't know that it directly addresses the issue where you
    just skip over the actual thing the program is supposed to do.
    Still, with more expressive type systems, it is likely one can
    much more easily structure the program so that the type of logic
    that lead to this failure is unnecessary.

    I understand that someone has written a paper proposing adding
    `defer` to C; that would obviate many of these problems.

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Thu May 21 14:55:58 2026
    From Newsgroup: comp.lang.c

    On 21/05/2026 13:56, Bart wrote:
    On 21/05/2026 07:45, David Brown wrote:
    On 20/05/2026 22:14, Bart wrote:
    On 20/05/2026 18:51, David Brown wrote:
    On 20/05/2026 17:41, Bart wrote:

    First, a niche use is not pointless.

    A niche use that takes advantage of some accidental quirk in a
    language? One that wouldn't exist if the quirk wasn't there; that
    sort of niche use?

    Over 50 years of use, every misfeature of C has been exploited by /
    somebody/. One reason why the language couldn't properly evolve.


    So do you have an example of where code has been written to take
    advantage of the separate name spaces?

    No. This is why I claimed it was pointless.

    (I did do a survey of my codebases to find examples of label names
    shadowing local identifiers, but found no instances. I didn't post the results (afaicr) since the code sample was too small, but it would be a massive job to do a bigger test.)

    So in conclusion, we have not seen a case where people have had the same identifier as a label and a variable, typedef or function name. But we
    can't rule out the possibility. It is certainly feasible that it
    happens sometimes by accident or coincidence.

    (The separation of the struct/union/enum tag name space from variable namespace /is/ a feature that people use - it is not uncommon to see
    "struct thing thing;" in code, whether or not either of us thinks it is
    clear coding. And separate name spaces for the members of each struct
    or union is trivially useful.)

    We have no idea of a user benefit from /not/ having separate name spaces
    here. (gcc's extension to let you take the value of a label with the && operator is orthogonal to the separation of the name spaces.)

    As far as we can see, it does not make a difference to users one way or
    the other. But I can easily see how it might be convenient for
    implementers.

    So are you justified in claiming it is pointless? No, you are not -
    because we haven't ruled out any possibility of user benefits, there is
    a definite implementer benefit, and having the opposite choice would be
    less likely to be of any benefit to anyone. But I think we can
    reasonably say it is a very minor matter that makes little practical difference to anyone.


      I don't - I merely can't rule out niche cases.  I could imagine some
    situations - perhaps from machine or human translation from other
    languages, or old implementations with very short identifier lengths.

    Yeah, a C implementation that only has 'a' to 'z' variables, but also
    has a separate set of 'a' to 'z' labels!

    You seem obsessed with calling every aspect of C a "misfeature".

    Yes, it is astounding how much there is. Some can be excused due to its
    age, but also lots could have been fixed long ago.

    Some of it is an actual nuisance, but quite a bit is also aesthetically displeasing.

    You didn't like my example of being able to write '(F)(x)' (or 'int
    (a);') but not 'goto (L)', but the anomaly is there.


    Your example was pointless. The fact that function names in C act as
    pointers and can be used in expressions, and that expressions evaluating
    to function pointers can be used to call functions, does not mean people typically go around writing "(printf)("hello");". Disallowing
    parentheses around the function name, on the other hand, would require additional rules in the grammar of C - so it would not make sense to
    disallow it. Similarly, the extra parentheses in "int (a);" are allowed because parentheses can be useful in complex declarations (perhaps
    mixing pointers, arrays, and function types) - disallowing them in some circumstances would complicate the grammar of the language.

    But labels are inherently simpler - you can only "goto" directly to a
    defined label. Parentheses could be of no benefit, so /allowing/ them
    would complicate the grammar.

    It is not an anomaly when very different things have different rules.

    There is also not being able to write 'L:}'; this one bites.

    I don't see why that "bites", unless it is some weird smiley. I would
    expect most people to have a the close brace on a separate line from the label, but it is legitimate for people to want to jump to the end of a
    block.



    But I can have a good idea of the implications both by there being a
    separate namespace, or not. You snipped my example where it caused a
    limitation in the syntax.


    Yes, it was irrelevant.

    And it's irrelevant because? It was a minor consequence due to
    restrictions on where labels can appear, in turn due to not sharing the
    same namespace as ordinary identifiers.


    It was irrelevant because it is not relevant to standard C, but mostly
    because it is not relevant to the dead horse we've been flogging. gcc
    has a syntax to take the address of a label - it uses "&&label". The
    choice of syntax is not in any way related to the name spaces. Or do
    you think that labels should automatically be treated as constants of
    type "void *" ? Getting the "value" of a label is a highly unusual
    situation - but it's useful in things like byte-code interpreters. It /should/ involve a special syntax that is clearly recognisable.

    Irrelevant because it's part of a language extension? Those tend to
    become standard.

    No, most language extensions do not become standard. This one has been
    in gcc for maybe three decades or more, and is not part of the standard.



    You can have an opinion from ignorance, as many people have told you.
    You can't expect that opinion to be respected.

    That's just rubbish, and an attempt to stifle criticism.

    No, it is perfectly true.


    Somebody asks why do I have to do X and not Y? Y makes more sense, it is easier etc.

    That's okay as far as it goes, but is missing context. Y makes more
    sense /to you/. Y makes more sense /for your specific needs/. Y makes
    more sense /in your personal language/. Your opinions and preferences
    are not fact, and your likes and dislikes do not generalise to all
    programmers and all languages.


    The response here will be because the Standard says so. End of discussion.


    In issues of facts about the C language, yes. You are free to like or
    dislike something the standard says, but not to pretend it doesn't say
    what it does.

    Somebody was able to voice that opinion using their own experience and common sense. It may be valid; maybe other languages do do Y instead of X.

    However all anyone here wants to suggest that that person is ignorant because they haven't read the standard. Well, they might read the
    standard then find that Y is still better!


    As many people have told you countless times, pretty much everybody has dislikes about some aspects of C. We all have things that we think
    would have been better if they had been different. That does not in any
    way change what C /is/, or how the language is defined.

    As has been said, "There are two kinds of languages - the ones people
    complain about, and the ones nobody uses".


    You don't 'own' C. There is no copyright on it. Anyone can use it as
    casually or as intensely as they like. Anyone can choose to create as
    professional or as casual a version as they like. Anyone choose to
    pontificate on things they like or dislike.

    The C23 draft open on my desktop at the moment says "© ISO 2024 - All
    rights reserved" on every page.  It /is/ copyrighted, and it is a
    defined and standardised language.

    The standards document itself might be copyrighted, not the language.


    The point of a language standard, and a language that has a standard, is
    that the standard defines the language. No one is likely to sue you for copyright infringement if you use the term "C" to refer to a different language, but it's not helpful to any discussion.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Thu May 21 14:56:23 2026
    From Newsgroup: comp.lang.c

    On 2026-05-21 13:46, Kenny McCormack wrote:
    In article <10uloem$e6bl$1@kst.eternal-september.org>,
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    Bart <bc@freeuk.com> writes:
    On 21/05/2026 01:30, Janis Papanagnou wrote:
    [...]
    You made a claim that looked strange, and I had been asking you
    whether you can point to the standard

    To the Algol68 standard? I wouldn't have a clue; probably only a
    handful of people on the planet do.

    Please take this to comp.lang.misc. There's already some discussion
    of Algol 68 there.

    The Lord and Master has spoken. All must obey.

    Yes, even JP.

    Curious; why "even"?

    I'm (in that respect) not different to others who post "dirty",
    non-OT-free topics.

    Here it started from "C" and someone generalized some statement,
    asking for non-C examples.

    I think it's fair to rebut wrong claims. It's also fair to point
    to any digression that got too far. So I'm open to suggestions
    concerning termination/relocation of pure OT sub-threads. Some
    folks are more picky than others, though. And the judgement and
    final decision has anyway to be done by the individual poster(s).

    Personally I skip (don't even read) followups if a topic is too
    far off, or if everything had already been said, or if there's
    some pathological poster who's meaningless answer (or squirming,
    or red herrings, or rhetorical moves) can anyway be anticipated.

    Cheers! :-)

    Janis

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Thu May 21 15:08:57 2026
    From Newsgroup: comp.lang.c

    On 2026-05-21 13:56, Bart wrote:
    [...]

    Some of it is an actual nuisance, but quite a bit is also aesthetically displeasing.

    (I'm not interested in what you find a nuisance, pleasing, aesthetic;
    you have a very specific and "sharply limited" and strong opinion it
    seems, so no good base for discussions which ask for a somewhat more
    open mind.)

    [...]
    There is also not being able to write 'L:}'; this one bites.

    But, being curious, what is that supposed to mean? - A label at the
    end of a block? - Seems to work for me (with my GNU C-compiler)...

    int main (void)
    {
    {
    int a = 42;
    goto end;
    return a;
    end:
    }
    return 0;
    }

    (although I wouldn't have been astonished if there'd be a requirement
    to allow labels at statements only, thus at least requiring something
    like an empty statement as in

    ...
    end: ;
    }

    But all fine in my book. (Especially since I'm anyway rarely - actually
    not at all - using 'goto' labels.)

    Janis

    [...]

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Thu May 21 15:16:23 2026
    From Newsgroup: comp.lang.c

    On 21/05/2026 14:18, Dan Cross wrote:
    In article <10umdsa$hnml$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    On 20/05/2026 20:17, Dan Cross wrote:
    [snip]
    I particularly agree with the use of a static inline to avoid
    some `goto`s. When the `goto fail` bug was announced, as an
    exercise, I rewrote Apple's code to demonstrate how one might
    eliminate the problematic pattern entirely:


    (The mixture of tabs and spaces in your post resulted in a bit messed up
    indentation in the quotation in my reply. I've tried to fix it up to
    match the original indentation, but be warned it might look different
    when you view it or re-quote it.)

    Apologies for that; I suspect that was my editor trying to be
    "helpful": I used spaces in the example, but usually use tabs;
    I probably should have just stuck with the latter for
    consistency.


    No problem - I just wanted to point it out in case my own posts looked
    wrong.

    <snip for brevity>


    This writeup of the original bug is pretty good: https://dwheeler.com/essays/apple-goto-fail.html


    I believe I read that at the time of the event. It's good to work
    through such critical errors to see how to avoid them in the future.

    He also recommends several of the techniques you do.

    In this particular case, the bug _should_ have been caught.
    Some combination of rigorous testing, static analysis, and
    manual review ought to have prevented it; that the bug made it
    into production software anyway is a software engineering
    failure.

    I think one can level some reasonable criticism at the language,
    however. The `goto error;` idiom is used in C because there are
    few alternatives for cleanup handling on failure. Modulo what
    we discussed before, that code can _often_ be restructured to
    avoid it, but sometimes it can't, and frequently it just isn't.


    I think "isn't" is more common than "can't". But I also don't think the
    "goto error" idiom is necessarily bad in itself - it's just that it is
    often used badly. A typical indication of poor usage is when a function
    is getting very long, and there are multiple "error" labels.

    However, the problem with this code was not the "goto error" idiom, or
    the "goto" itself - the problem was the mismatch between indentation and
    the statement under control of the "if". It would have been equally bad
    if it had been "return" rather than "goto", or if gcc cleanup attributes
    had been used to handle cleanup, or if some kind of "defer" mechanism
    had been used (as supported by some programming languages, and proposed
    for a future C version).

    These various cleanup mechanisms can definitely be better than "goto
    error", but they would not have prevented this error. (A programming
    language that requires braces for statements controlled by "if", on the
    other hand, /would/ have prevented the error.)

    In contrast, newer languages give you more expressive power in
    this regard: Go has the `defer` keyword to register a closure
    that runs when the enclosing function returns:

    ```go
    f, err := os.Open(...)
    if err != nil {
    return err
    }
    defer f.Close()
    ...
    ```

    The list of deferred closures will be run whenever the function
    returns, no matter what path it takes. You can't accidentally
    omit the close.

    Similarly, the RAII idiom prevalent in C++ and Rust uses object
    destructors that are automatically run when something goes out
    of scope to do the cleanup. C++ pairs that with
    exceptions (arguably worse than goto) while Rust represents
    errors with sum types and a little bit of syntactic sugar with
    the `?` operator. In either case, the descriptors run and do
    the cleanup, regardless of whether the return was an error path
    or a success path.

    These are all nice ways of handling cleanup.


    Other languages feature "linear types", instances of which must
    be used exactly once: failure to cleanup on an error path is a
    compile time error. This means that you can't forget to
    deallocate memory, close a file, or unlock a mutex, for example,
    but I don't know that it directly addresses the issue where you
    just skip over the actual thing the program is supposed to do.
    Still, with more expressive type systems, it is likely one can
    much more easily structure the program so that the type of logic
    that lead to this failure is unnecessary.


    There is definitely potential for a language's type system to make it
    harder to make some kinds of mistakes. But there is a risk in making
    the language too restrictive - people end up writing horrible code to
    work around restrictions, or use "unsafe" code too much.

    I did some work, eons ago, in a language called XC that was specifically
    for XMOS microcontrollers. The language and tools had a feature that
    made data races impossible by not allowing competing access to shared variables from different threads. Since threads were part of the
    hardware, and the tools analysed the code flow through threads, this
    could all be enforced at build time - data had to be passed in messages,
    not shared memory. But for some things that involved large buffers,
    that was hopelessly inefficient - and these devices were regularly used
    with USB, audio, and similar things that needed large and predictable
    buffers. So code - even library and example code from the manufacturer
    - was full of inline assembly to work around the "smart-arse" language
    and tools.

    I understand that someone has written a paper proposing adding
    `defer` to C; that would obviate many of these problems.

    - Dan C.


    Yes. Jens Gustedt - one of the few members of the C standards committee
    who is vocal and public about pushing new ideas into C. (Of course not everyone will agree with the ideas he comes with.)

    <https://gustedt.wordpress.com/2026/02/15/defer-available-in-gcc-and-clang/>



    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Thu May 21 14:31:19 2026
    From Newsgroup: comp.lang.c

    On 21/05/2026 14:08, Janis Papanagnou wrote:
    On 2026-05-21 13:56, Bart wrote:
    [...]

    Some of it is an actual nuisance, but quite a bit is also
    aesthetically displeasing.

    (I'm not interested in what you find a nuisance, pleasing, aesthetic;
    you have a very specific and "sharply limited" and strong opinion it
    seems, so no good base for discussions which ask for a somewhat more
    open mind.)

    [...]
    There is also not being able to write 'L:}'; this one bites.

    But, being curious, what is that supposed to mean? - A label at the
    end of a block? - Seems to work for me (with my GNU C-compiler)...

    It didn't use to be allowed (becase a label was defined as being a
    prefix to another statement, so you needed at least an empty statement
    like ";").

    It seems to be relaxed in C23. If you do:

    gcc -std=cxx -pedantic

    then it will give warnings for xx = 90, 99, 11 and 17. Arbitrary other C compilers may complain too.

    So this is a rare example of something I'd thought was an annoying
    quirk, being fixed.

    Of course, if I'd brought it up years ago, people would say the same
    things criticising me, my knowledge, my 'ignorance' and suggest it was a non-issue as you can work around it, exactly like you do below.

    (If generating code, then you don't always know in advance if a label
    will be followed by a } or not; the result is that all labels are
    written as "L:;".)

    int main (void)
    {
        {
            int a = 42;
            goto end;
            return a;
        end:
        }
        return 0;
    }

    (although I wouldn't have been astonished if there'd be a requirement
    to allow labels at statements only,

    So you didn't know this? How ignorant of you!

    However there is another aspect to this: in the original spec for
    labels, defining it as a prefix to a statemen meant that its definition
    was recursive. That means that a program like this:

    L1:
    L2:
    ....
    Ln:

    would risk overflowing the compiler stack, compared with a non-recursive version. A minor risk as you'd need tens of thousands of such labels. It
    would only show up in stress tests.

    thus at least requiring something
    like an empty statement as in

        ...
        end: ;
        }

    But all fine in my book. (Especially since I'm anyway rarely - actually
    not at all - using 'goto' labels.)

    No, 'end:;' is not ugly at all!


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Thu May 21 15:04:53 2026
    From Newsgroup: comp.lang.c

    cross@spitfire.i.gajendra.net (Dan Cross) writes:

    Similarly, the RAII idiom prevalent in C++ and Rust uses object
    destructors that are automatically run when something goes out
    of scope to do the cleanup. C++ pairs that with
    exceptions (arguably worse than goto) while Rust represents
    errors with sum types and a little bit of syntactic sugar with
    the `?` operator. In either case, the descriptors run and do
    the cleanup, regardless of whether the return was an error path
    or a success path.

    We used this in 1990 when implementing a fork() function in
    an unix-compatible distributed operating system (although it
    wasn't known formally as RAII in those days).

    int
    tProc::Fork(KnCap* PActUI, int Caller, tLwp *CrLwp, int IsVFork, boolean_t ForkAll)
    {
    int Error = 0;
    int nlwp; // total number of active lwps in the child
    int total_lwp; // total number of lwps in the child (active + zombies)

    ASSERT((Caller == FORK_SYSCALL) || (Caller == FORK_BOOTPROC));

    KnCap *ChildCap = Actor.GetCap();

    // Hand crafted processes do not have a ParentP.
    tProc *ParentP = CrLwp->MyProc();

    // Initialise the global Ops event that is used for global Ops
    // synchronisation

    EVENT_INIT(&GlobalEvt);

    //
    // Only Single threaded processes can vfork().
    // Currently, we don't support vfork.
    if (IsVFork && (!(ParentP->LwpList.SingleThreaded()))) {
    return EINVAL;
    }

    if (ForkAll) {
    nlwp = ParentP->LwpList.GetCountLwp();
    total_lwp = ParentP->LwpList.GetTotalLwp();

    } else {
    nlwp = 1;
    total_lwp = 1;
    }

    //
    // Check to see if we will be exceeding the quota.
    // XXXKYS: Check the interface for picking up quota tokens.
    // Would prefer an interface that accepted the number of
    // tokens needed - proc + lwps.
    // NOTE: The number of LWP tokens picked up will be equal to the
    // total number of LWPs in the child (including zombie LWPs).

    if (UidQuota.Fork(ProcCred->GetUid(), ProcCred->GetEuid(), total_lwp)) {
    return EAGAIN;
    }

    tForkFailure ff(this, CrLwp, ChildCap, nlwp, total_lwp);
    .
    .
    .
    }

    === The tForkfailure class encapsulated the resources allocated
    to the thread. The fork function could return at any time
    after this and the destructor for tForkFailure would clean up
    any allocated resources.

    //
    // Destructor: undo what fork has done upon failure.
    //
    tForkFailure::~tForkFailure()
    {
    if (ret == 0) {
    return;
    }

    if (PActUI != NULL) {
    procp->Mem.Exit(ChildCap);
    }

    //
    // XXXKYS: the DeleteAll() needs to take
    // into account that some of the LWPs
    // in the list may be zombies.
    //
    procp->LwpList.DeleteAll();

    if (rgn == B_TRUE) {
    procp->Rgn.ShmExit(CrLwp, procp);
    }

    if (semundo == B_TRUE) {
    procp->Undo.SemUnFork();
    }

    if (file == B_TRUE) {
    procp->File.ForkUndo();
    }

    procp->ProcRlim->rlFree(nlwp + 1);
    procp->ProcCred->CrFree(nlwp + 1);

    procp->UidQuota.FreeProc(B_TRUE, total_lwp, 1);
    }
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Thu May 21 15:12:16 2026
    From Newsgroup: comp.lang.c

    Bart <bc@freeuk.com> writes:
    On 21/05/2026 01:30, Janis Papanagnou wrote:
    On 2026-05-20 12:55, Bart wrote:
    On 20/05/2026 05:24, Janis Papanagnou wrote:
    [...]

    2. Are you sure that above code is valid (error-free) Algol 68 code?

    I'm not sure about that. - My old textbook says that you can use these >>>> identifiers *after* you've set them (not before). And skimming through >>>> the standard document, the Revised Report, I could not find anything.
    If there is; can you point me to the relevant chapter, please?

    It could also just not have been defined in the standard. (The above
    code or Genie's behavior would then not tell anything relevant about
    the language. - It would just expose yet another example of code that
    an experienced programmer would just not write that way.)

    You seem to have tested that with the Genie interpreter? - This would
    not say anything about what the Algol 68 standard says, mind.

    You seem determined to prove Bart wrong, aren't you?

    You made a claim that looked strange, and I had been asking you
    whether you can point to the standard

    To the Algol68 standard? I wouldn't have a clue; probably only a handful
    of people on the planet do.

    A google search for "algol 68 standard document" would give you
    a clue.

    https://algol68-lang.org/docs/algol68-draft-report.pdf


    For that you took an arbitrary Algol 68
    interpreter

    So give me links to some others, or to playgrounds.

    You are not capable of searching the internet yourself?

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Thu May 21 16:47:49 2026
    From Newsgroup: comp.lang.c

    On 21/05/2026 16:12, Scott Lurndal wrote:
    Bart <bc@freeuk.com> writes:
    On 21/05/2026 01:30, Janis Papanagnou wrote:
    On 2026-05-20 12:55, Bart wrote:
    On 20/05/2026 05:24, Janis Papanagnou wrote:
    [...]

    2. Are you sure that above code is valid (error-free) Algol 68 code? >>>>>
    I'm not sure about that. - My old textbook says that you can use these >>>>> identifiers *after* you've set them (not before). And skimming through >>>>> the standard document, the Revised Report, I could not find anything. >>>>> If there is; can you point me to the relevant chapter, please?

    It could also just not have been defined in the standard. (The above >>>>> code or Genie's behavior would then not tell anything relevant about >>>>> the language. - It would just expose yet another example of code that >>>>> an experienced programmer would just not write that way.)

    You seem to have tested that with the Genie interpreter? - This would >>>>> not say anything about what the Algol 68 standard says, mind.

    You seem determined to prove Bart wrong, aren't you?

    You made a claim that looked strange, and I had been asking you
    whether you can point to the standard

    To the Algol68 standard? I wouldn't have a clue; probably only a handful
    of people on the planet do.

    A google search for "algol 68 standard document" would give you
    a clue.

    https://algol68-lang.org/docs/algol68-draft-report.pdf

    That's not what I wouldn't have a clue about.

    BTW there are better, newer versions than that badly scanned and poorly typeset document:

    https://www.algol68-lang.org/docs/algol68-revised-report.pdf

    Have a glance through that then you will understand my remark.

    For that you took an arbitrary Algol 68
    interpreter

    So give me links to some others, or to playgrounds.

    You are not capable of searching the internet yourself?

    Yes of course. But there is nothing out there other than A68Genie, or
    some experimental version using gcc 16.

    This is why I asked: because I suspect there is little else.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Thu May 21 19:27:51 2026
    From Newsgroup: comp.lang.c

    On Thu, 21 May 2026 15:12:16 GMT
    scott@slp53.sl.home (Scott Lurndal) wrote:

    Bart <bc@freeuk.com> writes:
    On 21/05/2026 01:30, Janis Papanagnou wrote:
    On 2026-05-20 12:55, Bart wrote:
    On 20/05/2026 05:24, Janis Papanagnou wrote:
    [...]

    2. Are you sure that above code is valid (error-free) Algol 68
    code?

    I'm not sure about that. - My old textbook says that you can use
    these identifiers *after* you've set them (not before). And
    skimming through the standard document, the Revised Report, I
    could not find anything. If there is; can you point me to the
    relevant chapter, please?

    It could also just not have been defined in the standard. (The
    above code or Genie's behavior would then not tell anything
    relevant about the language. - It would just expose yet another
    example of code that an experienced programmer would just not
    write that way.)

    You seem to have tested that with the Genie interpreter? - This
    would not say anything about what the Algol 68 standard says,
    mind.

    You seem determined to prove Bart wrong, aren't you?

    You made a claim that looked strange, and I had been asking you
    whether you can point to the standard

    To the Algol68 standard? I wouldn't have a clue; probably only a
    handful of people on the planet do.

    A google search for "algol 68 standard document" would give you
    a clue.

    https://algol68-lang.org/docs/algol68-draft-report.pdf



    I did not open it, but my guess is that finding a clue from A68 Standard
    is a Very Hard Job.

    For that you took an arbitrary Algol 68
    interpreter

    So give me links to some others, or to playgrounds.

    You are not capable of searching the internet yourself?



    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Thu May 21 19:37:21 2026
    From Newsgroup: comp.lang.c

    On Thu, 21 May 2026 14:31:19 +0100
    Bart <bc@freeuk.com> wrote:
    On 21/05/2026 14:08, Janis Papanagnou wrote:
    On 2026-05-21 13:56, Bart wrote:
    [...]

    Some of it is an actual nuisance, but quite a bit is also
    aesthetically displeasing.

    (I'm not interested in what you find a nuisance, pleasing,
    aesthetic; you have a very specific and "sharply limited" and
    strong opinion it seems, so no good base for discussions which ask
    for a somewhat more open mind.)

    [...]
    There is also not being able to write 'L:}'; this one bites.

    But, being curious, what is that supposed to mean? - A label at the
    end of a block? - Seems to work for me (with my GNU C-compiler)...

    It didn't use to be allowed (becase a label was defined as being a
    prefix to another statement, so you needed at least an empty
    statement like ";").

    It seems to be relaxed in C23. If you do:

    gcc -std=cxx -pedantic

    then it will give warnings for xx = 90, 99, 11 and 17. Arbitrary
    other C compilers may complain too.

    So this is a rare example of something I'd thought was an annoying
    quirk, being fixed.

    Of course, if I'd brought it up years ago, people would say the same
    things criticising me, my knowledge, my 'ignorance' and suggest it
    was a non-issue as you can work around it, exactly like you do below.

    When I brought it not many years ago, but before C23 was finalized,
    most people that reacted agreed with me that being pedantic in this
    case is not a good idea on part of compiler unless it is asked
    specifically to be pedantic.
    (If generating code, then you don't always know in advance if a label
    will be followed by a } or not; the result is that all labels are
    written as "L:;".)

    int main (void)
    {
    {
    int a = 42;
    goto end;
    return a;
    end:
    }
    return 0;
    }

    (although I wouldn't have been astonished if there'd be a
    requirement to allow labels at statements only,

    So you didn't know this? How ignorant of you!

    However there is another aspect to this: in the original spec for
    labels, defining it as a prefix to a statemen meant that its
    definition was recursive. That means that a program like this:

    L1:
    L2:
    ....
    Ln:

    would risk overflowing the compiler stack, compared with a
    non-recursive version. A minor risk as you'd need tens of thousands
    of such labels. It would only show up in stress tests.

    thus at least requiring something
    like an empty statement as in

    ...
    end: ;
    }

    But all fine in my book. (Especially since I'm anyway rarely -
    actually not at all - using 'goto' labels.)

    No, 'end:;' is not ugly at all!


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Thu May 21 18:26:46 2026
    From Newsgroup: comp.lang.c

    On 21/05/2026 13:55, David Brown wrote:
    On 21/05/2026 13:56, Bart wrote:

    [Separate 'namespace' for label names]


    (The separation of the struct/union/enum tag name space from variable namespace /is/ a feature that people use


    Urgh, another discussion. Let's not go there. Suffice that it is unique
    to C.

    We have no idea of a user benefit from /not/ having separate name spaces here.

    So let's have separate namespaces for everything! We just have to keep
    it quite so that people can discover that amazing fact by accident.

    So are you justified in claiming it is pointless?  No, you are not - because we haven't ruled out any possibility of user benefits, there is
    a definite implementer benefit, and having the opposite choice would be
    less likely to be of any benefit to anyone.  But I think we can
    reasonably say it is a very minor matter that makes little practical difference to anyone.

    You keep saying this but then still disagree it is pointless.

    If that extra namespace disappeared overnight, would anyone notice?

    If making a new language, would you choose to have the same label name
    and non-label name co-exist in the same scope?

    If not familiar with C, the concept would be bizarre (it is bizarre anyway).

    But labels are inherently simpler - you can only "goto" directly to a defined label.  Parentheses could be of no benefit, so /allowing/ them would complicate the grammar.

    Having a label be just another term could be a benefit. For example:

    goto cond ? L1 : L2;

    That means that whatever follows 'goto' is just an expression and those
    may have parentheses.

    However, that example gives a better clue as to why label names are
    special in C: that "L1:" looks like a label definition. I doubt such definitions are allowed in the middle of an expression, but it looks problematical.


    It is not an anomaly when very different things have different rules.

    OK, call it a lack of orthogonality.

    There is also not being able to write 'L:}'; this one bites.

    I don't see why that "bites", unless it is some weird smiley.  I would expect most people to have a the close brace on a separate line from the label, but it is legitimate for people to want to jump to the end of a block.

    An intervening newline doesn't help. But this particular restriction has
    been eased in C23.

    No, most language extensions do not become standard.  This one has been
    in gcc for maybe three decades or more, and is not part of the standard.

    It (label pointers) has been used to help make CPython faster for a long
    time. But only on Linux. On Windows, CPython has to build with MSVC (as
    of a decade ago but maybe still the case), and that doesn't have the
    feature.

    So Python on Windows may be slower because that useful extension was not standardised.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Thu May 21 18:38:51 2026
    From Newsgroup: comp.lang.c

    On 21/05/2026 17:37, Michael S wrote:
    On Thu, 21 May 2026 14:31:19 +0100
    Bart <bc@freeuk.com> wrote:

    On 21/05/2026 14:08, Janis Papanagnou wrote:

    It didn't use to be allowed (becase a label was defined as being a
    prefix to another statement, so you needed at least an empty
    statement like ";").

    It seems to be relaxed in C23. If you do:

    gcc -std=cxx -pedantic

    then it will give warnings for xx = 90, 99, 11 and 17. Arbitrary
    other C compilers may complain too.

    So this is a rare example of something I'd thought was an annoying
    quirk, being fixed.

    Of course, if I'd brought it up years ago, people would say the same
    things criticising me, my knowledge, my 'ignorance' and suggest it
    was a non-issue as you can work around it, exactly like you do below.


    When I brought it not many years ago, but before C23 was finalized,
    most people that reacted agreed with me that being pedantic in this
    case is not a good idea on part of compiler unless it is asked
    specifically to be pedantic.

    This is what I get from various compilers and versions, using default
    options:

    gcc 10.x: error: label at end of compound statement

    gcc 14.x/16.x: nothing (-pedantic will give a warning)

    DMC: error

    lccwin32: error

    Tiny C: warning

    clang 18.x: nothing

    So it used to be taken seriously for decades, but why would it do that
    in the first place?

    And why would anyone want it reported? This is a clear misfeature that provides no benefit.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Thu May 21 19:46:17 2026
    From Newsgroup: comp.lang.c

    On 2026-05-21 17:12, Scott Lurndal wrote:
    [...]

    A google search for "algol 68 standard document" would give you
    a clue.

    https://algol68-lang.org/docs/algol68-draft-report.pdf

    The Genie system (that I strongly presume Bart is also using)
    comes with a 700-pages manual/tutorial/reference that contains
    the full Revised Report.

    https://jmvdveer.home.xs4all.nl/learning-algol-68-genie.pdf

    Yes, it would have been easy to *find* it, but I'd presume if
    one is using the Genie he would already *have* the document.

    But for some folks it's easier to write a dozen posts full of
    complaints instead of just taking the time to sit back and slow
    down, think, and inspect the documentation instead of emitting
    wild guesses and declare them as facts.

    Janis

    [...]

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Thu May 21 13:48:12 2026
    From Newsgroup: comp.lang.c

    On 21/05/2026 13:56, Bart wrote:
    On 21/05/2026 07:45, David Brown wrote:
    You can have an opinion from ignorance, as many people have told you.
    You can't expect that opinion to be respected.

    That's just rubbish, and an attempt to stifle criticism.
    No, only to stifle uninformed criticism.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Thu May 21 13:54:04 2026
    From Newsgroup: comp.lang.c

    On 2026-05-21 09:08, Janis Papanagnou wrote:
    On 2026-05-21 13:56, Bart wrote:
    ...
    There is also not being able to write 'L:}'; this one bites.

    But, being curious, what is that supposed to mean? - A label at the
    end of a block? - Seems to work for me (with my GNU C-compiler)...

    The only two places where labels are permitted are in labeled statements (6.8.2p1) and goto statements. Since ';' qualifies as a null statement,
    Bart is required to make the extremely painful modification of instead
    writing 'L:;}'.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Thu May 21 20:03:31 2026
    From Newsgroup: comp.lang.c

    On 2026-05-21 18:27, Michael S wrote:
    On Thu, 21 May 2026 15:12:16 GMT
    scott@slp53.sl.home (Scott Lurndal) wrote:

    https://algol68-lang.org/docs/algol68-draft-report.pdf

    I did not open it, but my guess is that finding a clue from A68 Standard
    is a Very Hard Job.

    Well, my experience with (most) standards is that they are typically
    not written for the casual user or ordinary programmer. The Algol 68
    standard, the Revised Report, specifically seems to be regularly
    considered as being "not the easiest" to read document. (Myself I've
    never read a standards document to learn a programming language, but
    I've studied other international standards, documents of hundreds and
    thousands of pages, so I'm at least not repelled by such documents
    beforehand, and if I'm interested and want clarity about something I
    look up these documents. - I did that also to look for Bart's claim,
    but I could not find anything that would support Bart's statement.)

    Though for the given sub-thread we have to state that to clarify the
    correct syntax and semantics of a language we will have to look into
    the respective defining reference documents; the standards. - A wild
    guess, speculation, wish, or opinion, cannot substitute a standard.

    Janis

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Thu May 21 20:09:41 2026
    From Newsgroup: comp.lang.c

    On 2026-05-21 19:54, James Kuyper wrote:
    On 2026-05-21 09:08, Janis Papanagnou wrote:
    On 2026-05-21 13:56, Bart wrote:
    ...
    There is also not being able to write 'L:}'; this one bites.

    But, being curious, what is that supposed to mean? - A label at the
    end of a block? - Seems to work for me (with my GNU C-compiler)...

    The only two places where labels are permitted are in labeled statements (6.8.2p1) and goto statements. Since ';' qualifies as a null statement,
    Bart is required to make the extremely painful modification of instead writing 'L:;}'.

    Aha, so the GNU C-compiler I'm using was just generous to not require
    a semicolon at the block-terminating brace.

    Thanks for the information (and the standard reference)!

    Janis

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Thu May 21 19:42:30 2026
    From Newsgroup: comp.lang.c

    On 21/05/2026 19:03, Janis Papanagnou wrote:
    On 2026-05-21 18:27, Michael S wrote:
    On Thu, 21 May 2026 15:12:16 GMT
    scott@slp53.sl.home (Scott Lurndal) wrote:

    https://algol68-lang.org/docs/algol68-draft-report.pdf

    I did not open it, but my guess is that finding a clue from A68 Standard
    is a Very Hard Job.

    Well, my experience with (most) standards is that they are typically
    not written for the casual user or ordinary programmer. The Algol 68 standard, the Revised Report, specifically seems to be regularly
    considered as being "not the easiest" to read document. (Myself I've
    never read a standards document to learn a programming language, but
    I've studied other international standards, documents of hundreds and thousands of pages, so I'm at least not repelled by such documents beforehand, and if I'm interested and want clarity about something I
    look up these documents. - I did that also to look for Bart's claim,
    but I could not find anything that would support Bart's statement.)

    Though for the given sub-thread we have to state that to clarify the
    correct syntax and semantics of a language we will have to look into
    the respective defining reference documents; the standards. - A wild
    guess, speculation, wish, or opinion, cannot substitute a standard.

    Have you yet found a working version that implements what it said?


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Thu May 21 20:08:19 2026
    From Newsgroup: comp.lang.c

    On 21/05/2026 18:46, Janis Papanagnou wrote:
    On 2026-05-21 17:12, Scott Lurndal wrote:
    [...]

    A google search for "algol 68 standard document" would give you
    a clue.

    https://algol68-lang.org/docs/algol68-draft-report.pdf

    The Genie system (that I strongly presume Bart is also using)
    comes with a 700-pages manual/tutorial/reference that contains
    the full Revised Report.

    https://jmvdveer.home.xs4all.nl/learning-algol-68-genie.pdf

    Yes, it would have been easy to *find* it, but I'd presume if
    one is using the Genie he would already *have* the document.

    But for some folks it's easier to write a dozen posts full of
    complaints

    I haven't made any complaint about Algol68 here.

    I merely wrote a program and observed its behaviour on what might be the
    only current implementation in the world.

    What reason would I have had to doubt that it was performing correctly?

    instead of just taking the time to sit back and slow
    down, think, and inspect the documentation instead of emitting
    wild guesses and declare them as facts.

    You mean that completely impenetrable gobbledygook? Perhaps you'd like
    to enlighten us to what exactly it says.

    That is, what exactly is in scope within the dotted lines here:

    INT a:=1; BEGIN ... INT a:=2; END

    In C it would be that first 'a'.

    In both my languages it would be that second 'a'.

    In Python, it would also be that second 'a' (which is another
    'real-world' language that does this; I didn't need Algol68 after all!)

    In Algol68, assuming a working reference implementation, it would be
    .... ? Simple question.

    in the same way I politely pointed out the issue with labels at the ends
    of compound statements, that you seemed to be unaware of, you could have politely corrected my assumption.

    (I guess now you're going to study the Python reference manual, and
    badger the CPython maintainers to admit they may have made a mistake,
    just to prove me wrong again.)

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Thu May 21 21:23:27 2026
    From Newsgroup: comp.lang.c

    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:
    On 21/05/2026 13:56, Bart wrote:

    [Separate 'namespace' for label names]


    (The separation of the struct/union/enum tag name space from variable
    namespace /is/ a feature that people use


    Urgh, another discussion. Let's not go there. Suffice that it is unique
    to C.

    Really? All other languages keep everything in the one name space, do
    they? Are you /sure/ about that? You have checked how Lisp, Scala,
    Ocaml, and other languages work?


    We have no idea of a user benefit from /not/ having separate name
    spaces here.

    So let's have separate namespaces for everything! We just have to keep
    it quite so that people can discover that amazing fact by accident.

    So are you justified in claiming it is pointless?  No, you are not -
    because we haven't ruled out any possibility of user benefits, there
    is a definite implementer benefit, and having the opposite choice
    would be less likely to be of any benefit to anyone.  But I think we
    can reasonably say it is a very minor matter that makes little
    practical difference to anyone.

    You keep saying this but then still disagree it is pointless.

    If that extra namespace disappeared overnight, would anyone notice?

    "Pointless" means there is /no/ point. That's different from saying it
    is a minor matter and alternative decisions would likely not make a big difference.


    If making a new language, would you choose to have the same label name
    and non-label name co-exist in the same scope?


    The question I'd be asking myself is why would I choose to put label
    names in the same scope as anything else? (And that's assuming a new
    language even has a concept of labels.) There's no reason to do so.
    Much more interesting are thoughts about whether types, functions and variables should share a name space or be separate.

    If not familiar with C, the concept would be bizarre (it is bizarre
    anyway).

    At what point in your C programming life have you felt "I wish labels
    were in the same name space as variables. It would make my programming
    /so/ much easier and clearer" ?


    But labels are inherently simpler - you can only "goto" directly to a
    defined label.  Parentheses could be of no benefit, so /allowing/ them
    would complicate the grammar.

    Having a label be just another term could be a benefit. For example:

      goto cond ? L1 : L2;

    C does not have any kind of goto statement with an expression. This is
    good and appropriate. "goto" is an unstructured concept, and should be
    used sparingly in well-written code.


    That means that whatever follows 'goto' is just an expression and those
    may have parentheses.

    However, that example gives a better clue as to why label names are
    special in C: that "L1:" looks like a label definition. I doubt such definitions are allowed in the middle of an expression, but it looks problematical.


    Yes, in the same way that "x * y" looks like pointer dereference. In
    other words, not at all.


    It is not an anomaly when very different things have different rules.

    OK, call it a lack of orthogonality.


    "A foolish consistency is the hobgoblin of little minds."

    There is also not being able to write 'L:}'; this one bites.

    I don't see why that "bites", unless it is some weird smiley.  I would
    expect most people to have a the close brace on a separate line from
    the label, but it is legitimate for people to want to jump to the end
    of a block.

    An intervening newline doesn't help. But this particular restriction has been eased in C23.

    Oh, I see what you mean - you are complaining that (prior to C23) you
    could /not/ have a label just before the closing brace. I still don't
    see how that "bites" - you had to write "L: ;". I'd be surprised if
    anyone found it a hardship, never mind thinking it "bites".


    No, most language extensions do not become standard.  This one has
    been in gcc for maybe three decades or more, and is not part of the
    standard.

    It (label pointers) has been used to help make CPython faster for a long time. But only on Linux. On Windows, CPython has to build with MSVC (as
    of a decade ago but maybe still the case), and that doesn't have the feature.

    So Python on Windows may be slower because that useful extension was not standardised.


    It is not a lack of standardisation - it is a lack of support for the
    feature in MSVC. There is nothing to stop MSVC having the same
    extensions, and MSVC is renowned for failing to support lots of C
    standard features (basically, anything in C99 onwards that is not also
    in C++ - though I believe they have got marginally better in recent years).

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Thu May 21 20:59:59 2026
    From Newsgroup: comp.lang.c

    On 21/05/2026 20:23, David Brown wrote:
    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:
    On 21/05/2026 13:56, Bart wrote:

    [Separate 'namespace' for label names]


    (The separation of the struct/union/enum tag name space from variable
    namespace /is/ a feature that people use


    Urgh, another discussion. Let's not go there. Suffice that it is
    unique to C.

    Really?  All other languages keep everything in the one name space, do they?  Are you /sure/ about that?  You have checked how Lisp, Scala, Ocaml, and other languages work?

    OK, it's something I haven't come across before in other languages.

    Some may have naming conventions, others may only have types appearing
    in certain contexts.

    But I don't recall seeing anything like C's scheme which has both struct
    tag names in their own namespace, AND user-defined type names in the
    general namespace:

    typedef struct Point {int x, y;} Point;
    struct Point a;
    Point b;

    or:

    struct Vector {Point p, q;};
    struct Vector Vector;

    It's messy; Point is both a struct tag and type name in the same scope.
    Vector is both a struct tag and variable name in the same scope.

    You can't have the hat-trick however because the same type name and
    variable name can't appear in the same scope; it needs an extra namespace!

    At best they can be in the same block:

    struct Vector Vector; {....; typedef struct Vector Vector; Vector:;}

    Within those {} exist Vector (variable); Vector (struct tag); and Vector
    (type name); plus Vector (label) for good measure.

    You're saying Lisp has all this too?

    Lisp does has a reputation for being everything (interpreted/compiled; static/dynamic; functional/imperative etc), but this seems a stretch.


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

    record Point = (int x, y)
    record Vector = (Point p, q)

    Point a, b
    Vector `Vector # best I could do
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Thu May 21 14:23:27 2026
    From Newsgroup: comp.lang.c

    Bart <bc@freeuk.com> writes:
    On 21/05/2026 07:45, David Brown wrote:
    [...]
    So do you have an example of where code has been written to take
    advantage of the separate name spaces?

    No. This is why I claimed it was pointless.

    This entire discussion is pointless. It is a fact that C labels
    are in their own name space. Nobody here has provided a rationale
    for that fact. Nobody here other than you particularly cares.

    If that fact had caused you any inconvenience, you might have a
    glimmering of point.

    If you really care about the reason, you could do your own research.
    You could *ask* rather than whining about the fact most of us aren't
    bothered by it. You could look at documents for earlier versions
    of C, and for C's predecessor languages.

    David knows that labels have their own name space. He apparently
    doesn't know why, and likely has decided it doesn't matter much.
    And yet you continue to argue.

    Here's an entirely speculative possible rationale. If I define
    a variable "foo" within a function, and then add a label "foo:"
    later in the same function, the label name does not interfere with
    the variable name. If labels were not in their own name space,
    adding the label later in the function would interfere with the
    declaration earlier in the function. Labels are the only named
    entities whose scope begins before their definition. That *might*
    have been in Ritchie's mind when he specified how labels work.

    [...]

    You didn't like my example of being able to write '(F)(x)' (or 'int
    (a);') but not 'goto (L)', but the anomaly is there.

    Different entities have different rules. You invent contrived
    examples meant to demonstrate that C Is Bad And Inconsistent rather
    than doing anything at all to help anyone understand what the rules
    actually are or how to work with them. You expend tremendous effort
    trying to prove that C has quirks, something that everyone here is
    perfectly well aware of.

    Putting parentheses around a label name wouldn't even make any sense. Disallowing `goto (L)` is not a quirk. Allowing it would be.

    There is also not being able to write 'L:}'; this one bites.

    It's always been trivially easy to write `L:;}` rather than `L:}`.
    And C23 allows a bare label just before a `}`, so the problem is
    now fixed.

    I predict that you will be outraged that this minor problem wasn't
    solved sooner rather than happy that it was actually solved.

    [...]
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Thu May 21 14:31:31 2026
    From Newsgroup: comp.lang.c

    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    On 2026-05-21 19:54, James Kuyper wrote:
    On 2026-05-21 09:08, Janis Papanagnou wrote:
    On 2026-05-21 13:56, Bart wrote:
    ...
    There is also not being able to write 'L:}'; this one bites.

    But, being curious, what is that supposed to mean? - A label at the
    end of a block? - Seems to work for me (with my GNU C-compiler)...
    The only two places where labels are permitted are in labeled
    statements
    (6.8.2p1) and goto statements. Since ';' qualifies as a null statement,
    Bart is required to make the extremely painful modification of instead
    writing 'L:;}'.

    Aha, so the GNU C-compiler I'm using was just generous to not require
    a semicolon at the block-terminating brace.

    Thanks for the information (and the standard reference)!

    gcc by default compiles GNU C, not ISO C. It also fails to emit many language-required diagnostics. GNU C is ISO C with gcc extensions.
    Prior to C23, allowing "L:}" is a documented gcc extension.
    It's rejected if you ask gcc to conform to ISO C17 or earlier.

    Observing what gcc allows by default tells you very litlte about
    the C language.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.lang.c on Thu May 21 21:35:10 2026
    From Newsgroup: comp.lang.c

    In article <10uno7u$10dvs$1@dont-email.me>, Bart <bc@freeuk.com> wrote: >[snip]

    You're saying Lisp has all this too?

    You're moving the goalposts, again. You posted some abstruse C
    code and are asking if that code, specifically, exists in all of
    its grotesque glory in Lisp. But the question was really
    whether C was unique in having different namespaces for
    different kinds of objects (types, functions, variables, and so
    on).

    Lisp does has a reputation for being everything (interpreted/compiled; >static/dynamic; functional/imperative etc), but this seems a stretch.

    It depends on the specific Lisp you are talking about (there are
    many). Note that it is typical to refer to the plural of Lisp
    as "Lisps" (as opposed to dialects or similar).

    In https://dreamsongs.com/Separation.html, Gabriel describes the
    dual notions of a "value namespace" and a "function namespace",
    and categories lists into two broad categories, based on whether
    a particular Lisp uses a single namespace value and function
    (Scheme, Clojure) or these are distinct (Common Lisp). He calls
    thse Lisp_1 and Lisp_2, respectively (note the numbers are meant
    to be subscripted).

    As a Lisp_2, in Common Lisp a symbol has an associated plist
    with distinct value and function slots; which is given when a
    symbol is evaluated depends on context: if the symbol is in
    the function position of a form, you get the entry from the
    function namespace, otherwise, from the value namespace. Some
    people don't like this. Some do.

    So, in Common Lisp, a symbol can refer to both a function and a
    value simultaneously; which you get depends on context. See
    also section 12 of Gabriel's paper, linked above.

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Thu May 21 23:47:02 2026
    From Newsgroup: comp.lang.c

    On 21/05/2026 22:23, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:
    On 21/05/2026 07:45, David Brown wrote:
    [...]
    So do you have an example of where code has been written to take
    advantage of the separate name spaces?

    No. This is why I claimed it was pointless.

    This entire discussion is pointless. It is a fact that C labels
    are in their own name space. Nobody here has provided a rationale
    for that fact. Nobody here other than you particularly cares.

    This is how labels entered the discussion:

    "The simplest scoping rule in C is for labels: they have function-wide
    scope, regardless of block nesting label.

    That would almost be as simple as how my own scopes work, except C just
    has to still be quirky:

    double c; c: goto c;

    (Labels of course have their own namespace, for some obscure reason. I'm
    sure 99% of C programmers don't know that.) "

    I introduced label names as an example of a much simpler scoping scheme
    than other kinds of names.

    Here's an entirely speculative possible rationale. If I define
    a variable "foo" within a function, and then add a label "foo:"
    later in the same function, the label name does not interfere with
    the variable name. If labels were not in their own name space,
    adding the label later in the function would interfere with the
    declaration earlier in the function. Labels are the only named
    entities whose scope begins before their definition. That *might*
    have been in Ritchie's mind when he specified how labels work.

    You can also define a new type, or macro, or enum, or declare a function called "foo" whose name could interfere with variable "foo" if within
    the same block.

    Did the earliest C have block scopes? Then scoping rules between labels
    and non-labels would have clashed. Perhaps that was a reason.


    Putting parentheses around a label name wouldn't even make any sense. Disallowing `goto (L)` is not a quirk. Allowing it would be.

    Allowing labels as expressions has advantages; I gave some examples.
    Being able to use parentheses is a consequence, but not useful in
    itself. But the special namespace puts paid to that.


    There is also not being able to write 'L:}'; this one bites.

    It's always been trivially easy to write `L:;}` rather than `L:}`.
    And C23 allows a bare label just before a `}`, so the problem is
    now fixed.

    I predict that you will be outraged that this minor problem wasn't
    solved sooner rather than happy that it was actually solved.

    Yes; why did it exist in the first place?

    (1) I complain about something

    (2) Everyone says it is a non-issue and I'm complaining about
    triviliaties

    (3) Despite that, it mysteriously gets fixed anyway

    Is it possible that I might have had a valid point in the first place?

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Thu May 21 19:41:52 2026
    From Newsgroup: comp.lang.c

    On 2026-05-21 17:31, Keith Thompson wrote:
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    On 2026-05-21 19:54, James Kuyper wrote:
    ...
    The only two places where labels are permitted are in labeled
    statements
    (6.8.2p1) and goto statements. Since ';' qualifies as a null statement,
    Bart is required to make the extremely painful modification of instead
    writing 'L:;}'.

    Aha, so the GNU C-compiler I'm using was just generous to not require
    a semicolon at the block-terminating brace.

    Thanks for the information (and the standard reference)!

    gcc by default compiles GNU C, not ISO C. It also fails to emit many language-required diagnostics. GNU C is ISO C with gcc extensions.
    Prior to C23, allowing "L:}" is a documented gcc extension.
    It's rejected if you ask gcc to conform to ISO C17 or earlier.

    Observing what gcc allows by default tells you very litlte about
    the C language.

    He's not relying on gcc, he's relying on my citation of a draft of the standard. I missed the fact that 6.8.3p1 has been changed to allow a
    label with no following statement as a block item in a compound statement.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Thu May 21 19:49:40 2026
    From Newsgroup: comp.lang.c

    On 2026-05-21 17:23, Keith Thompson wrote:
    ...
    This entire discussion is pointless. It is a fact that C labels
    are in their own name space. Nobody here has provided a rationale
    for that fact.

    I did. I pointed out that the standard sets aside a separate name space
    for each kind of identifier which is usable only in syntactic contexts
    where an ordinary identifier would not be allowed.

    The Rationale says "The position adopted in the Standard is to permit as
    many separate name spaces as can be distinguished by context, ...".

    Apparently the Committee likes separate name spaces; you'll have to ask
    them why.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Thu May 21 17:24:02 2026
    From Newsgroup: comp.lang.c

    Bart <bc@freeuk.com> writes:
    On 21/05/2026 22:23, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:
    On 21/05/2026 07:45, David Brown wrote:
    [...]
    So do you have an example of where code has been written to take
    advantage of the separate name spaces?

    No. This is why I claimed it was pointless.

    This entire discussion is pointless. It is a fact that C labels
    are in their own name space. Nobody here has provided a rationale
    for that fact. Nobody here other than you particularly cares.

    This is how labels entered the discussion:

    "The simplest scoping rule in C is for labels: they have function-wide
    scope, regardless of block nesting label.

    That would almost be as simple as how my own scopes work, except C
    just has to still be quirky:

    double c; c: goto c;

    (Labels of course have their own namespace, for some obscure
    reason. I'm sure 99% of C programmers don't know that.) "

    I suggest using the term "name space" rather than "namespace",
    which happens to be the name of a C++ feature.

    I introduced label names as an example of a much simpler scoping
    scheme than other kinds of names.

    And that point has been abundantly made.

    [...]

    Did the earliest C have block scopes? Then scoping rules between
    labels and non-labels would have clashed. Perhaps that was a reason.

    Maybe. If you care, do the research. If you don't, why are you still
    talking about it?

    Putting parentheses around a label name wouldn't even make any sense.
    Disallowing `goto (L)` is not a quirk. Allowing it would be.

    Allowing labels as expressions has advantages; I gave some
    examples. Being able to use parentheses is a consequence, but not
    useful in itself. But the special namespace puts paid to that.

    In C, label names are not expressions. It's that simple. C does
    not allow parentheses around a label name, and there is no reason
    at all why it should do so. (If it did, you'd probably complain
    that it's inconsistent.)

    There is also not being able to write 'L:}'; this one bites.

    It's always been trivially easy to write `L:;}` rather than `L:}`.
    And C23 allows a bare label just before a `}`, so the problem is
    now fixed.

    I predict that you will be outraged that this minor problem wasn't
    solved sooner rather than happy that it was actually solved.

    Yes; why did it exist in the first place?

    It wasn't seen as necessary. The workaround, adding a semicolon, is
    trivial, and has been mentioned at least as far back as K&R1, 1978.

    (1) I complain about something

    (2) Everyone says it is a non-issue and I'm complaining about
    triviliaties

    (3) Despite that, it mysteriously gets fixed anyway

    Is it possible that I might have had a valid point in the first place?

    Sure. FYI, here's the proposal that, as far as I can tell, led to C23
    allowing a label immediately before a "}". It was written by Martin
    Uecker in 2020.

    <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2496.pdf>

    The author described the existing definition as "an unnecessarily and
    annoying limitation" and proposed a fix, which was accepted.

    Two things can be true simultaneously:

    1. The existing limitation was no more than a minor annoyance with
    an easy workaround. Few C programmers thought that it "bites".

    2. The committee decided it was worth fixing. Somebody (notably not
    you) took the time to write a proposal and submit it to the
    commmittee, which accepted it. The proposal was designed not
    to break any existing code.

    Note that this change did not affect the scoping rules for labels.

    There is a proposal to change the scoping rules for labels in C2y.
    If you're curious, visit

    <https://www.open-std.org/jtc1/sc22/wg14/www/wg14_document_log>

    and search for recent papers with "labels" in their titles. This is
    related to named break and continue statements, a new feature in C2y.
    It does not affect scoping for labels that are used only as targets
    for goto statements.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Thu May 21 17:27:23 2026
    From Newsgroup: comp.lang.c

    James Kuyper <jameskuyper@alumni.caltech.edu> writes:
    On 2026-05-21 17:23, Keith Thompson wrote:
    ...
    This entire discussion is pointless. It is a fact that C labels
    are in their own name space. Nobody here has provided a rationale
    for that fact.

    I did. I pointed out that the standard sets aside a separate name space
    for each kind of identifier which is usable only in syntactic contexts
    where an ordinary identifier would not be allowed.

    The Rationale says "The position adopted in the Standard is to permit as
    many separate name spaces as can be distinguished by context, ...".

    I stand corrected. (This wording appears in both the C89 and C99
    Rationale documents.)

    Apparently the Committee likes separate name spaces; you'll have to ask
    them why.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Fri May 22 01:51:20 2026
    From Newsgroup: comp.lang.c

    On 22/05/2026 01:24, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:

    Allowing labels as expressions has advantages; I gave some
    examples. Being able to use parentheses is a consequence, but not
    useful in itself. But the special namespace puts paid to that.

    In C, label names are not expressions. It's that simple. C does
    not allow parentheses around a label name, and there is no reason
    at all why it should do so. (If it did, you'd probably complain
    that it's inconsistent.)

    You don't understand my point, so I will leave it.

    It wasn't seen as necessary. The workaround, adding a semicolon, is
    trivial, and has been mentioned at least as far back as K&R1, 1978.

    (1) I complain about something

    (2) Everyone says it is a non-issue and I'm complaining about
    triviliaties

    (3) Despite that, it mysteriously gets fixed anyway

    Is it possible that I might have had a valid point in the first place?

    Sure. FYI, here's the proposal that, as far as I can tell, led to C23 allowing a label immediately before a "}". It was written by Martin
    Uecker in 2020.

    <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2496.pdf>

    This is very interesting. So perhaps tens of millions of C programmers encountered the quirk, found it annoying, and moved on.

    Some may have complained (and possibly told to shut up and go away).
    It's only until 2020 that somebody does something about it.


    The author described the existing definition as "an unnecessarily and annoying limitation" and proposed a fix, which was accepted.

    Two things can be true simultaneously:

    1. The existing limitation was no more than a minor annoyance with
    an easy workaround. Few C programmers thought that it "bites".

    2. The committee decided it was worth fixing. Somebody (notably not
    you)

    Nor you or anyone else here either. Remember I'm a casual user of C.

    And notably:

    * The issue has NEVER existed in my own languages

    * The fix for C already exists in my C compiler (that is, allowing
    labels in front of } or declarations). A hangover from when I tried to
    fix a few other things.

    So I've already done more than most in dealing with it within language
    design, within an implementation, and talking about it here.



    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Fri May 22 04:48:43 2026
    From Newsgroup: comp.lang.c

    On 2026-05-21 10:16, David Brown wrote:
    On 21/05/2026 02:45, Janis Papanagnou wrote:
    [...]
    David, the answer of my emailed question arrived; the behavior of
    the Genie interpreter in case of identity relations is just a bug!
    [...]
    Algol 68 is a well designed language and the scope rules are as we
    expect sensibly chosen; entities are valid within the block scope
    and their "existence", when you can use them, starts with their
    declaration (not before).
    [...]

    OK.  I know very little about Algol 68, and don't have the interest to bother checking such details myself.  Your description makes sense to
    me, both of the way the language is intended to work, and that there is
    an oddity or bug in a particular implementation.  "It makes sense to me" is, of course, not a particularly strong argument.  But I don't think it
    is worth going into more detail here in c.l.c.

    Bart does have a tendency to mix up "this is how the language is
    designed" and "this is what a particular implementation does".  With his own languages, that's fine - there is only one implementation, and only
    a rough description of the language, so the two coincide.  It is also
    how pre-standardisation languages tend to work.  But it is not how C
    works, nor, as I understand it, how Algol68 works.

    Short update on that (for those interested); my question I recently
    sent to Marcel about Genie's behavior made him not only explain that
    it's a bug, but he also immediately fixed it. - The current Genie
    release 3.12.2 (published a few hours ago) now reports:

    a68g: runtime error: 1: attempt to use an uninitialised INT value,
    in [] "SIMPLOUT" collateral-clause starting at "(" in this line.

    Janis

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Fri May 22 05:02:34 2026
    From Newsgroup: comp.lang.c

    On 2026-05-22 04:48, Janis Papanagnou wrote:
    On 2026-05-21 10:16, David Brown wrote:
    [...]

    Short update on that (for those interested); my question I recently
    sent to Marcel about Genie's behavior made him not only explain that
    it's a bug, but he also immediately fixed it. - The current Genie
    release 3.12.2 (published a few hours ago) now reports:

    a68g: runtime error: 1: attempt to use an uninitialised INT value,
    in [] "SIMPLOUT" collateral-clause starting at "(" in this line.

    BTW, thanks to Bart for (inadvertently!) detecting a bug in Genie.

    While his statement had been only an uninformed attempt to support
    an inappropriate claim of him he contributed to a fix that way. :-)


    Janis


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Fri May 22 05:23:17 2026
    From Newsgroup: comp.lang.c

    On 2026-05-22 01:41, James Kuyper wrote:
    On 2026-05-21 17:31, Keith Thompson wrote:
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    On 2026-05-21 19:54, James Kuyper wrote:
    ...
    The only two places where labels are permitted are in labeled
    statements
    (6.8.2p1) and goto statements. Since ';' qualifies as a null statement, >>>> Bart is required to make the extremely painful modification of instead >>>> writing 'L:;}'.

    Aha, so the GNU C-compiler I'm using was just generous to not require
    a semicolon at the block-terminating brace.

    Thanks for the information (and the standard reference)!

    gcc by default compiles GNU C, not ISO C. It also fails to emit many
    language-required diagnostics. GNU C is ISO C with gcc extensions.
    Prior to C23, allowing "L:}" is a documented gcc extension.
    It's rejected if you ask gcc to conform to ISO C17 or earlier.

    Observing what gcc allows by default tells you very litlte about
    the C language.

    He's not relying on gcc, he's relying on my citation of a draft of the standard.

    Exactly. - Actually I didn't read Keith's comment as presuming I'd
    rely on gcc's behavior; but only because I factually don't rely on
    gcc. But his wording could indeed be misread and impute something
    like that. So thanks for pointing that out; myself I wouldn't have
    noticed that connotation.

    My intention should have been clear to Keith by my original saying
    "Seems to work for me (with my GNU C-compiler)" with an *explicit*
    hint on specific behavior of a specific tool mentioned. But he may
    have missed that. - And to his favor we can still also interpret
    his post's "tells you" having been meant just as a general hint to
    all readers, to be understood as "tells one".

    I missed the fact that 6.8.3p1 has been changed to allow a
    label with no following statement as a block item in a compound statement.

    Thanks.

    Janis

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Fri May 22 05:58:51 2026
    From Newsgroup: comp.lang.c

    On 2026-05-22 02:24, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:
    [...]

    (Labels of course have their own namespace, for some obscure
    reason. I'm sure 99% of C programmers don't know that.) "

    I suggest using the term "name space" rather than "namespace",
    which happens to be the name of a C++ feature.

    The term "namespace" is, as I observe, a regular English word
    (it's in my dictionary, at least).

    So if you want to talk about a namespace there's nothing wrong
    with using the term namespace (without any punctuation or any
    arbitrary spacing).

    I think (instead of a space) it's sensible to apply typographical
    conventions to signify "features". I've, for example, generally
    used single quotes to identify programming language entities or
    keywords, as in 'namespace' (a C++ feature or keyword).

    ("A namespace in C++ is denoted by the keyword 'namespace'.")

    Denoting such things with (for example) single quotes makes the
    whole text also better readable, because the typical programming
    languages' entities are (also) English words and interfere with
    the surrounding texts.

    Janis

    [...]

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Thu May 21 21:21:49 2026
    From Newsgroup: comp.lang.c

    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    On 2026-05-22 02:24, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:
    [...]
    (Labels of course have their own namespace, for some obscure
    reason. I'm sure 99% of C programmers don't know that.) "
    I suggest using the term "name space" rather than "namespace",
    which happens to be the name of a C++ feature.

    The term "namespace" is, as I observe, a regular English word
    (it's in my dictionary, at least).

    It's not in dictionary.com or the online Merriam Webster (not
    surprisingly, the OED has it), and I don't think it's a common term
    outside programming.

    So if you want to talk about a namespace there's nothing wrong
    with using the term namespace (without any punctuation or any
    arbitrary spacing).

    The problem is that "namespace" is a C++ keyword with a specific
    meaning that doesn't apply to C. And the C standard consistently
    uses the phrase "name space", and defines it as a technical term.

    Of course C and C++ are two different languages, but they share a lot
    in common. C++ has both C-style name spaces and its own namespaces.

    Referring to C's name spaces as "namespaces" might not cause much
    confusion, but it's likely to induce a reaction from the more
    pedantic among us.

    [snip]
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Fri May 22 06:52:28 2026
    From Newsgroup: comp.lang.c

    On 2026-05-21 21:23, David Brown wrote:
    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:
    [...]
    Having a label be just another term could be a benefit. For example:

       goto cond ? L1 : L2;

    What benefit is it to have an alternative construct for existing
    clearer 'if' and 'switch' features? - What's the concrete gain? -
    To create harder to follow spaghetti-code?

    Labels are no first-class object in "C", and there's no necessity
    to be (IMO).


    C does not have any kind of goto statement with an expression.  This is good and appropriate.  "goto" is an unstructured concept, and should be used sparingly in well-written code.

    Well, for someone used to 'goto' the existence of a calculated 'goto'
    might be considered worthwhile. (Many older languages do support
    such a feature. But I have no overview about its prevalence in newer
    languages, though. I'd suppose it's primarily a remnant of history.)

    [...]
    It is not an anomaly when very different things have different rules.

    OK, call it a lack of orthogonality.

    "A foolish consistency is the hobgoblin of little minds."

    Oh, orthogonality is a valuable property of a programming language!

    "C" is mostly lacking that property, but "C" is what it is. There's
    certainly no point in Bart's continuous complaints-tirades.

    Janis

    [...]

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Fri May 22 07:17:30 2026
    From Newsgroup: comp.lang.c

    On 2026-05-22 06:21, Keith Thompson wrote:
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    On 2026-05-22 02:24, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:
    [...]
    (Labels of course have their own namespace, for some obscure
    reason. I'm sure 99% of C programmers don't know that.) "
    I suggest using the term "name space" rather than "namespace",
    which happens to be the name of a C++ feature.

    The term "namespace" is, as I observe, a regular English word
    (it's in my dictionary, at least).

    It's not in dictionary.com or the online Merriam Webster (not
    surprisingly, the OED has it), and I don't think it's a common term
    outside programming.

    So if you want to talk about a namespace there's nothing wrong
    with using the term namespace (without any punctuation or any
    arbitrary spacing).

    The problem is that "namespace" is a C++ keyword with a specific
    meaning that doesn't apply to C. And the C standard consistently
    uses the phrase "name space", and defines it as a technical term.

    Of course C and C++ are two different languages, but they share a lot
    in common. C++ has both C-style name spaces and its own namespaces.

    Referring to C's name spaces as "namespaces" might not cause much
    confusion, but it's likely to induce a reaction from the more
    pedantic among us.

    I wouldn't import the conventions of a specific language's "bible"
    to colloquial communication, even if that communication is mostly
    about that language. If we're talking about concepts ("namespaces")
    it's fine and appropriate to simply name them as such. (YMMV.)

    A subtle space is just an IMO inappropriate means; if compared to
    the typographical suggestion I made. Specifically in the light that
    we often see word-compounds written in many alternative forms (say,
    "name space", "name-space", or "namespace").

    I don't much care about the pedants - I know them by name :-) - if
    they're missing the more important global picture; the clearness of formulations (as I previously expanded on [in the part you trimmed]).

    In the light of your suggestion (a space) I wanted to suggest a
    (IMO better) typographical alternative (quoted technical entities).

    Janis

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Fri May 22 09:31:36 2026
    From Newsgroup: comp.lang.c

    On 21/05/2026 21:59, Bart wrote:
    On 21/05/2026 20:23, David Brown wrote:
    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:
    On 21/05/2026 13:56, Bart wrote:

    [Separate 'namespace' for label names]


    (The separation of the struct/union/enum tag name space from
    variable namespace /is/ a feature that people use


    Urgh, another discussion. Let's not go there. Suffice that it is
    unique to C.

    Really?  All other languages keep everything in the one name space, do
    they?  Are you /sure/ about that?  You have checked how Lisp, Scala,
    Ocaml, and other languages work?

    OK, it's something I haven't come across before in other languages.

    Fair enough. I am happy to go along with that. We both know a bit
    about a number of languages, but it is a big stretch to make claims
    about /all/ languages (even all mainstream languages).

    Scala and ML languages, AFAIUI, have separate name spaces for types and variables, and it is not uncommon to have "int : int" style declarations
    to make a variable of type "int". Lisp, according to my brief googling
    (so take this with a cup-load of salt) has separate name spaces for
    functions and variables. Languages with sigils like Perl, PHP and BASIC
    have multiple name spaces for different kinds of variables, where the
    sigil makes the name space explicit.


    Some may have naming conventions, others may only have types appearing
    in certain contexts.

    But I don't recall seeing anything like C's scheme which has both struct
    tag names in their own namespace, AND user-defined type names in the
    general namespace:

       typedef struct Point {int x, y;} Point;
       struct Point a;
       Point b;

    or:

       struct Vector {Point p, q;};
       struct Vector Vector;

    It's messy; Point is both a struct tag and type name in the same scope. Vector is both a struct tag and variable name in the same scope.

    It's not "messy" - it is simple and clear. The rules are in no sense difficult.

    People have different preferences here - some people to use "struct
    Point" to emphasise that the type is a structure and not a scalar type,
    some people like to use "Point" and avoid the distinction (this is,
    naturally, common for C++ programmers). Some people like to skip the
    struct tag name entirely and write "typedef struct { int x; int y; }
    Point;". (You can't do that for recursive types.) And some people like
    to use different tag names and typedef names.

    It gives a flexibility and choice of style.

    Of course, if a programmer randomly mixes the different styles, it can
    be messy and confusing. But no matter what rules a language has, no
    language can eliminate programmers' ability to write messy and confusing
    code.

    You think of every feature in C as a way for people to write unclear
    code - in reality, almost everything you complain about is a way for
    people to write clear and readable code.

    I think perhaps your problem here is that for your own language, all the
    code samples you can find were written by /you/. Thus all code written
    in your language is clear and easily understood by everyone reading the language - because it is all the one person. With real-world languages,
    there is a huge variety of readers and writers, and it is normal that
    they have wide differences in what they consider messy or neat, readable
    or incomprehensible.


    You can't have the hat-trick however because the same type name and
    variable name can't appear in the same scope; it needs an extra namespace!

    At best they can be in the same block:

       struct Vector Vector; {....; typedef struct Vector Vector; Vector:;}

    Within those {} exist Vector (variable); Vector (struct tag); and Vector (type name); plus Vector (label) for good measure.

    You're saying Lisp has all this too?

    I didn't say any details about Lisp - merely that it does not have
    (AFAIUI) everything in one name space.


    Lisp does has a reputation for being everything (interpreted/compiled; static/dynamic; functional/imperative etc), but this seems a stretch.


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

    record Point  = (int x, y)
    record Vector = (Point p, q)

    Point a, b
    Vector `Vector           # best I could do

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Fri May 22 09:34:28 2026
    From Newsgroup: comp.lang.c

    On 22/05/2026 06:52, Janis Papanagnou wrote:
    On 2026-05-21 21:23, David Brown wrote:
    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:
    [...]
    Having a label be just another term could be a benefit. For example:

       goto cond ? L1 : L2;

    What benefit is it to have an alternative construct for existing
    clearer 'if' and 'switch' features? - What's the concrete gain? -
    To create harder to follow spaghetti-code?

    Labels are no first-class object in "C", and there's no necessity
    to be (IMO).


    C does not have any kind of goto statement with an expression.  This
    is good and appropriate.  "goto" is an unstructured concept, and
    should be used sparingly in well-written code.

    Well, for someone used to 'goto' the existence of a calculated 'goto'
    might be considered worthwhile. (Many older languages do support
    such a feature. But I have no overview about its prevalence in newer languages, though. I'd suppose it's primarily a remnant of history.)

    [...]
    It is not an anomaly when very different things have different rules.

    OK, call it a lack of orthogonality.

    "A foolish consistency is the hobgoblin of little minds."

    Oh, orthogonality is a valuable property of a programming language!

    "C" is mostly lacking that property, but "C" is what it is. There's
    certainly no point in Bart's continuous complaints-tirades.


    Emphasis is on the "foolish". Consistency and orthogonality are good,
    taking them too far is counter-productive.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Fri May 22 10:49:02 2026
    From Newsgroup: comp.lang.c

    On 22/05/2026 05:58, Janis Papanagnou wrote:
    On 2026-05-22 02:24, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:
    [...]

    (Labels of course have their own namespace, for some obscure
    reason. I'm sure 99% of C programmers don't know that.) "

    I suggest using the term "name space" rather than "namespace",
    which happens to be the name of a C++ feature.

    The term "namespace" is, as I observe, a regular English word
    (it's in my dictionary, at least).

    So if you want to talk about a namespace there's nothing wrong
    with using the term namespace (without any punctuation or any
    arbitrary spacing).

    I think (instead of a space) it's sensible to apply typographical
    conventions to signify "features". I've, for example, generally
    used single quotes to identify programming language entities or
    keywords, as in 'namespace' (a C++ feature or keyword).

    ("A namespace in C++ is denoted by the keyword 'namespace'.")

    Denoting such things with (for example) single quotes makes the
    whole text also better readable, because the typical programming
    languages' entities are (also) English words and interfere with
    the surrounding texts.

    Janis


    There are two reasons to use the term "name space" rather than
    "namespace". On is that C++, and many other languages, support
    "namespaces" as a type of structuring of identifier scope. This is a different concept from the "name spaces" we are discussing here.

    The other reason to include the space is that the C standards use the
    term "name space", so if anyone wants to look things up in the standards
    and search for mentions of the concept, they will need to include the space.


    It is unfortunate that there are no good, convenient ways to distinguish between "identifier name spaces" in C (and other languages) and "scope namespaces" in C++ (and other languages, but not C). Such collisions
    occur all the time. We might colloquially say that functions and
    variables are different types of objects - but "type" and "object" have specific meanings in C that do not match such loose usage. (And the
    meaning of "object" in C differs substantially from the same term in
    many other languages.) So we can try other words, like "kind" (but
    that's a keyword in Fortran), or "concept" (now a keyword in C++), and
    so on.

    The best we can do is try to be as accurate as we can about terms in the
    C standards, since we are in c.l.c., and try to make the context clear.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Fri May 22 11:18:24 2026
    From Newsgroup: comp.lang.c

    On 22/05/2026 03:48, Janis Papanagnou wrote:
    On 2026-05-21 10:16, David Brown wrote:
    On 21/05/2026 02:45, Janis Papanagnou wrote:
    [...]
    David, the answer of my emailed question arrived; the behavior of
    the Genie interpreter in case of identity relations is just a bug!
    [...]
    Algol 68 is a well designed language and the scope rules are as we
    expect sensibly chosen; entities are valid within the block scope
    and their "existence", when you can use them, starts with their
    declaration (not before).
    [...]

    OK.  I know very little about Algol 68, and don't have the interest to
    bother checking such details myself.  Your description makes sense to
    me, both of the way the language is intended to work, and that there
    is an oddity or bug in a particular implementation.  "It makes sense
    to me" is, of course, not a particularly strong argument.  But I don't
    think it is worth going into more detail here in c.l.c.

    Bart does have a tendency to mix up "this is how the language is
    designed" and "this is what a particular implementation does".  With
    his own languages, that's fine - there is only one implementation, and
    only a rough description of the language, so the two coincide.  It is
    also how pre-standardisation languages tend to work.  But it is not
    how C works, nor, as I understand it, how Algol68 works.

    Short update on that (for those interested); my question I recently
    sent to Marcel about Genie's behavior made him not only explain that
    it's a bug, but he also immediately fixed it. - The current Genie
    release 3.12.2 (published a few hours ago) now reports:

    a68g: runtime error: 1: attempt to use an uninitialised INT value,
    in [] "SIMPLOUT" collateral-clause starting at "(" in this line.

    Thanks, now we get can some definite answers. If I try this program:

    INT a=1234;

    PROC fred = (INT n)INT:
    BEGIN
    print((a, newline));
    INT a=777;
    print((a, newline));

    a
    END;

    INT x;
    x:=fred(23);

    print(x)

    Then the older A68G showed +0 +777 +777. The new one reports an error on
    that first print.

    If I change 'a=' to 'a:=', then both report an attempt to use an
    uninitialised REF INT value.

    If I remove that nested declaration of 'a', then both show +1234 +1234
    +1234.

    So what has been fixed is that an attempt to use an uninitialised INT
    value (when using '=' rather than ':=') is now reported.

    But unfortunately it does look in that case as though the scope of the
    second 'a' does run from the preceding BEGIN. It's just there's no way
    to practically make use of it there (I tried a few ways).

    A similar thing happens in Python, but there you can use the earlier 'a' legally if you void using the the uninitialised version:

    a=1
    def F():
    i=0

    for j in range(2):
    if i:
    print ("Ai",a)
    a=2
    print ("Aii",a)
    i=1

    F()
    print(a)

    (The absence of 'global a' inside the function, and the assignment to
    'a', I believe makes 'a' a local, whose scope is function-wide.)

    There was something else: you said this:

    entities are valid within the block scope
    and their "existence", when you can use them, starts with their
    declaration (not before).

    and I asked why this works:

    fred(9999);

    PROC fred = (INT n)VOID: print((n, newline));

    print("end")

    I speculated it was the same bug. But it still works with 3.12.2. So
    this case, it is possible to call 'fred' even though it has not yet been defined.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Fri May 22 11:43:06 2026
    From Newsgroup: comp.lang.c

    On 22/05/2026 05:52, Janis Papanagnou wrote:
    On 2026-05-21 21:23, David Brown wrote:
    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:
    [...]
    Having a label be just another term could be a benefit. For example:

       goto cond ? L1 : L2;

    What benefit is it to have an alternative construct for existing
    clearer 'if' and 'switch' features? - What's the concrete gain? -
    To create harder to follow spaghetti-code?

    You're asking why '?:' exists when you can use 'if-else'?

    Actually you can do pretty much this with gnu C:

    goto *(c ? &&L1 : &&L2);

    but it still doesn't allow a general expression. It seems that what
    comes after 'goto' must be either a regular label, or '*' to dereference
    a label pointer.

    In my syntax, it can do this:

    goto (c | L1, L2, L3 | Lx)

    It's sweeter /because/ label names live in the normal name space. I'm
    sure A68 can do this too, but I couldn't get it to work.

    The point is, other people do think is has a benefit! At least in gnu C
    and A68G even if you dismiss my own use of it.



    Labels are no first-class object in "C", and there's no necessity
    to be (IMO).


    C does not have any kind of goto statement with an expression.  This
    is good and appropriate.  "goto" is an unstructured concept, and
    should be used sparingly in well-written code.

    Well, for someone used to 'goto' the existence of a calculated 'goto'
    might be considered worthwhile. (Many older languages do support
    such a feature. But I have no overview about its prevalence in newer languages, though. I'd suppose it's primarily a remnant of history.)

    In gnu C computed goto tends to be used for faster dispatching (in
    bytecode interpreters, emulators etc) compared with regular 'switch',
    because it uses multiple dispatch points (which help CPU branch
    predication). 'switch' uses only one.

    (My language has a dedicated feature for this, so using explicit
    computed goto is not needed. That allows it to outperform optimised
    /standard/ C that uses plain switch.)

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Fri May 22 11:45:34 2026
    From Newsgroup: comp.lang.c

    [Possible duplicate post]

    On 22/05/2026 05:52, Janis Papanagnou wrote:
    On 2026-05-21 21:23, David Brown wrote:
    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:
    [...]
    Having a label be just another term could be a benefit. For example:

    goto cond ? L1 : L2;

    What benefit is it to have an alternative construct for existing
    clearer 'if' and 'switch' features? - What's the concrete gain? -
    To create harder to follow spaghetti-code?
    You're asking why '?:' exists when you can use 'if-else'?

    Actually you can do pretty much this with gnu C:

    goto *(c ? &&L1 : &&L2);

    but it still doesn't allow a general expression. It seems that what
    comes after 'goto' must be either a regular label, or '*' to dereference
    a label pointer.

    In my syntax, it can do this:

    goto (c | L1, L2, L3 | Lx)

    It's sweeter /because/ label names live in the normal name space. I'm
    sure A68 can do this too, but I couldn't get it to work.

    The point is, other people do think is has a benefit! At least in gnu C
    and A68G even if you dismiss my own use of it.



    Labels are no first-class object in "C", and there's no necessity
    to be (IMO).


    C does not have any kind of goto statement with an expression. This
    is good and appropriate. "goto" is an unstructured concept, and
    should be used sparingly in well-written code.

    Well, for someone used to 'goto' the existence of a calculated 'goto'
    might be considered worthwhile. (Many older languages do support
    such a feature. But I have no overview about its prevalence in newer languages, though. I'd suppose it's primarily a remnant of history.)
    In gnu C computed goto tends to be used for faster dispatching (in
    bytecode interpreters, emulators etc) compared with regular 'switch',
    because it uses multiple dispatch points (which help CPU branch
    predication). 'switch' uses only one.

    (My language has a dedicated feature for this, so using explicit
    computed goto is not needed. That allows it to outperform optimised
    /standard/ C that uses plain switch.)



    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Fri May 22 13:31:05 2026
    From Newsgroup: comp.lang.c

    On 22/05/2026 12:41, Bart wrote:
    On 22/05/2026 05:52, Janis Papanagnou wrote:
    On 2026-05-21 21:23, David Brown wrote:
    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:
    [...]
    Having a label be just another term could be a benefit. For example:

       goto cond ? L1 : L2;

    What benefit is it to have an alternative construct for existing
    clearer 'if' and 'switch' features? - What's the concrete gain? -
    To create harder to follow spaghetti-code?

    You're asking why '?:' exists when you can use 'if-else'?


    I don't believe that is at all what he was asking. As I see it, he was
    asking what benefit you have from writing :

    goto cond ? L1 : L2;

    compared to :

    if (cond) {
    goto L1;
    } else {
    goto L2;
    }

    Given that - except for niche applications - goto's are rarely used in C
    and then almost always with a single labele ("goto error", or escaping
    from nested loops), it seems absurd to me to complicate gotos and labels
    by supporting expressions. C is mostly a structured programming
    language - unstructured constructs like "goto" should be minimal, not encouraged.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.lang.c on Fri May 22 12:13:19 2026
    From Newsgroup: comp.lang.c

    In article <10upbvq$1dhq4$1@dont-email.me>, Bart <bc@freeuk.com> wrote:
    On 22/05/2026 05:52, Janis Papanagnou wrote:
    On 2026-05-21 21:23, David Brown wrote:
    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:
    [...]
    Having a label be just another term could be a benefit. For example:

       goto cond ? L1 : L2;

    What benefit is it to have an alternative construct for existing
    clearer 'if' and 'switch' features? - What's the concrete gain? -
    To create harder to follow spaghetti-code?

    You're asking why '?:' exists when you can use 'if-else'?

    Is that meant to be obtuse? No, the question is why anyone
    would want to write code that uses `goto` in that fashion.

    The ternary operator, `?:`, is expression-oriented. Label names
    are just identifiers; identifiers may appear in expressions
    (specifically, "primary expressions"), but only if they name an
    object, or a function.

    A label name can only appear as part of a labeled statement;
    thus, it names neither an object nor a function, so it cannot be
    used as part of a primary expression, and so therefore, one
    cannot use label names in the form mentioned above.

    You seem to be saying, "look, you can't do this!" But the
    question is, _why would you want to do that in the first place_?

    You further seem to be, once again, using this as some sort of
    subjective argument for the inferiority of C. Ok, we get it:
    you don't like C. But, yet again, for the $n$th time, not being
    able to write "weird" code like this is not a good argument in
    support of your opinion.

    Actually you can do pretty much this with gnu C:

    goto *(c ? &&L1 : &&L2);

    but it still doesn't allow a general expression. It seems that what
    comes after 'goto' must be either a regular label, or '*' to dereference
    a label pointer.

    In my syntax, it can do this:

    goto (c | L1, L2, L3 | Lx)

    It's sweeter /because/ label names live in the normal name space. I'm
    sure A68 can do this too, but I couldn't get it to work.

    The point is, other people do think is has a benefit! At least in gnu C
    and A68G even if you dismiss my own use of it.

    My subjective opinion is that that is not sweet: it's hideous.

    Obviously someone thinks, or thought, that this had some kind of
    benefit, otherwise they wouldn't have implemented it. It seems
    extraordinarily rarely used.

    Sometimes people have an idea of what they think is going to be
    a neat feature, only to find out with some experience that it
    didn't pan out the way they thought it might. This happens all
    the time; language designers are not perfect. I strongly
    suspect that this feature falls into that category.

    C does not have any kind of goto statement with an expression.  This
    is good and appropriate.  "goto" is an unstructured concept, and
    should be used sparingly in well-written code.

    Well, for someone used to 'goto' the existence of a calculated 'goto'
    might be considered worthwhile. (Many older languages do support
    such a feature. But I have no overview about its prevalence in newer
    languages, though. I'd suppose it's primarily a remnant of history.)

    In gnu C computed goto tends to be used for faster dispatching (in
    bytecode interpreters, emulators etc) compared with regular 'switch', >because it uses multiple dispatch points (which help CPU branch >predication). 'switch' uses only one.

    (My language has a dedicated feature for this, so using explicit
    computed goto is not needed. That allows it to outperform optimised >/standard/ C that uses plain switch.)

    Absent a benchmark, I find that assertion specious. Speculation
    about performance on modern machines is just that; speculation.
    But machines these days are so incredibly complicated, and so
    dynamic during execution, that we simply cannot reason about
    them from first principles anymore: you either produce data for
    a particular target machine, or you're just guessing.

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Fri May 22 13:42:34 2026
    From Newsgroup: comp.lang.c

    On 22/05/2026 12:31, David Brown wrote:
    On 22/05/2026 12:41, Bart wrote:
    On 22/05/2026 05:52, Janis Papanagnou wrote:
    On 2026-05-21 21:23, David Brown wrote:
    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:
    [...]
    Having a label be just another term could be a benefit. For example: >>>>>
       goto cond ? L1 : L2;

    What benefit is it to have an alternative construct for existing
    clearer 'if' and 'switch' features? - What's the concrete gain? -
    To create harder to follow spaghetti-code?

    You're asking why '?:' exists when you can use 'if-else'?


    I don't believe that is at all what he was asking.  As I see it, he was asking what benefit you have from writing :

        goto cond ? L1 : L2;

    compared to :

        if (cond) {
            goto L1;
        } else {
            goto L2;
        }

    This is the same difference between:

    a = cond ? x : y;

    and:

    if (cond) {
    a = x;
    } else {
    a = y;
    }

    If ?: is considered a convenient, clearer short-cut for the latter, then
    the same applies to the goto.


    Given that - except for niche applications - goto's are rarely used in C

    You seem to be supporting some kinds of /very/ small niches like those
    enabled by label name spaces.

    But I bet 'goto' in C is a lot more common!


    and then almost always with a single labele ("goto error", or escaping
    from nested loops), it seems absurd to me to complicate gotos and labels
    by supporting expressions.

    And yet, in the gnu extension, exactly this is enabled, by allowing first-class labels (sort of) and allowing them within expressions.


      C is mostly a structured programming
    language - unstructured constructs like "goto" should be minimal, not encouraged.

    But when you do need to conditionally jump to L1 or L2, then ?: lets you
    do that with only one 'goto', not two. (I can do it with zero: (cond |
    L1 | L2); the 'goto' can be implied.)

    We all know you don't use it yourself, but I can list half a dozen
    use-cases for 'goto'. Here are two:

    (1) For generated C code when transpiling from a higher level language. Without 'goto' this would be near-impossible. (Not impossible, but hard
    enough that it is easier to use another language.)

    (2) For porting code to C that already uses 'goto'.




    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Fri May 22 15:11:42 2026
    From Newsgroup: comp.lang.c

    On 22/05/2026 14:42, Bart wrote:
    On 22/05/2026 12:31, David Brown wrote:
    On 22/05/2026 12:41, Bart wrote:
    On 22/05/2026 05:52, Janis Papanagnou wrote:
    On 2026-05-21 21:23, David Brown wrote:
    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:
    [...]
    Having a label be just another term could be a benefit. For example: >>>>>>
       goto cond ? L1 : L2;

    What benefit is it to have an alternative construct for existing
    clearer 'if' and 'switch' features? - What's the concrete gain? -
    To create harder to follow spaghetti-code?

    You're asking why '?:' exists when you can use 'if-else'?


    I don't believe that is at all what he was asking.  As I see it, he
    was asking what benefit you have from writing :

         goto cond ? L1 : L2;

    compared to :

         if (cond) {
             goto L1;
         } else {
             goto L2;
         }

    This is the same difference between:

        a = cond ? x : y;

    and:

        if (cond) {
            a = x;
        } else {
            a = y;
        }


    No, it is not.

    In expressions, the tertiary operator can be useful for giving neater
    and more compact code. It is also important in situations where you
    want to be working with a single expression - such as for variable initialisation. The second form can be clearer precisely because it is
    more verbose - it gives space to put comments, or perhaps breakpoints
    when debugging, or when you want to get more accurate indications of
    changes when doing line-by-line comparisons of code versions. The
    second form is also better if the sub-expressions "x" and "y" are more complex, or it is important to be visually and immediately clear which expressions are being evaluated, or if you have more than two branches
    or the possibility of never assigning to "a".

    So while you can sometimes use either form, often there are very
    distinct advantages in picking a particular form for such conditional assignments.

    With goto, there is never a particular benefit. Who cares if you save
    three lines of source code on a construct that occurs once in a million
    lines? I'd rather have it stand out, not be compact and terse.

    If ?: is considered a convenient, clearer short-cut for the latter, then
    the same applies to the goto.


    Given that - except for niche applications - goto's are rarely used in C

    You seem to be supporting some kinds of /very/ small niches like those enabled by label name spaces.

    I do not "seem" to be supporting anything of the kind. As usual, you
    are completely wrong about what I think.


    But I bet 'goto' in C is a lot more common!

    I bet it is. But I bet the desire to write "goto cond ? L1 : L2;" is
    very, very small.



    and then almost always with a single labele ("goto error", or escaping
    from nested loops), it seems absurd to me to complicate gotos and
    labels by supporting expressions.

    And yet, in the gnu extension, exactly this is enabled, by allowing first-class labels (sort of) and allowing them within expressions.


    All that demonstrates is that someone working on gcc development, long,
    long ago (in a galaxy far, far away) thought it might be useful. The
    early days of gcc development were full of wild ideas about extensions,
    often added with little thought or planning, and it is generally very difficult to remove them later (though it has been done in some cases).
    The fact that gcc has a particular extension does not imply that it is
    widely used (though I know this one /is/ used in a few programs), nor
    that it is popular or well liked (even amongst those that use it), or
    that it is suitable for adding to the C language.


      C is mostly a structured programming language - unstructured
    constructs like "goto" should be minimal, not encouraged.

    But when you do need to conditionally jump to L1 or L2, then ?: lets you
    do that with only one 'goto', not two. (I can do it with zero: (cond |
    L1 | L2); the 'goto' can be implied.)

    Let me try again, slowly.

    Almost no C code has any use of "goto" or labels. I'd guess that
    rounded up to the nearest 0.1%, 0.1% of functions in C use "goto" or labels.

    Of the C code that uses "goto", almost none of it needs anything more
    complex than "goto error" or "goto break_from_all_loops". When (if) the upcoming "defer" and "break label" features are added to the language,
    those use-cases will disappear. (Of course old code will not change,
    and it will take time for people to use new C2y features once they are supported.)

    So we are at perhaps 0.1% of 0.1% of functions that have benefits of
    anything more complex than a simple "goto label".

    And when "goto label" is not enough, it is common to want significantly
    more complicated setups - such as using the gcc extensions - for things
    like virtual machine byte-code interpreters. There, something akin to
    "goto cond ? L1 : L2;" would be useless.

    Let the three people in the world who want to write "goto cond ? L1 :
    L2;" use the "if" construction - it is not worth changing the language
    and tools to save them a few centimetres of screen space.



    We all know you don't use it yourself, but I can list half a dozen use- cases for 'goto'. Here are two:

    (1) For generated C code when transpiling from a higher level language. Without 'goto' this would be near-impossible. (Not impossible, but hard enough that it is easier to use another language.)

    (2) For porting code to C that already uses 'goto'.





    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Fri May 22 14:16:11 2026
    From Newsgroup: comp.lang.c

    On 22/05/2026 13:13, Dan Cross wrote:
    In article <10upbvq$1dhq4$1@dont-email.me>, Bart <bc@freeuk.com> wrote:
    On 22/05/2026 05:52, Janis Papanagnou wrote:
    On 2026-05-21 21:23, David Brown wrote:
    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:
    [...]
    Having a label be just another term could be a benefit. For example: >>>>>
       goto cond ? L1 : L2;

    What benefit is it to have an alternative construct for existing
    clearer 'if' and 'switch' features? - What's the concrete gain? -
    To create harder to follow spaghetti-code?

    You're asking why '?:' exists when you can use 'if-else'?

    Is that meant to be obtuse? No, the question is why anyone
    would want to write code that uses `goto` in that fashion.

    The ternary operator, `?:`, is expression-oriented. Label names
    are just identifiers; identifiers may appear in expressions
    (specifically, "primary expressions"), but only if they name an
    object, or a function.

    And as I said, gnu C allows just that.

    You're anyone asking the wrong question. It's usually a mistake to try
    and double-guess the needs of a programmer, since it's possible they may
    have a genuine use-case you haven't thought of. Or they have a
    particular coding style they want to use.


    A label name can only appear as part of a labeled statement;
    thus, it names neither an object nor a function, so it cannot be
    used as part of a primary expression, and so therefore, one
    cannot use label names in the form mentioned above.

    You seem to be saying, "look, you can't do this!" But the
    question is, _why would you want to do that in the first place_?

    So what on earth was the point of gnu C's &&L label pointers?

    Other than enabling faster interpreters such as with CPython (which
    needs all the help it can get). Writing FSMs and so on...

    You seem to downplaying the benefits only because standard C doesn't
    allow it.

    My subjective opinion is that that is not sweet: it's hideous.

    OK. Of course we'd have to see the alternative.


    Obviously someone thinks, or thought, that this had some kind of
    benefit, otherwise they wouldn't have implemented it. It seems extraordinarily rarely used.

    Sometimes people have an idea of what they think is going to be
    a neat feature, only to find out with some experience that it
    didn't pan out the way they thought it might. This happens all
    the time;

    It happens with me too! You should see some of my ideas that were too
    naff. There were also better ones but that just didn't get enough usage
    to warrant the support needed.

    But I believe 'goto' is fundamental at this level of language, and it is
    handy to allow first-class handling of labels even if little used: it's something that if it isn't there when needed, it is harder to get around.

    In gnu C computed goto tends to be used for faster dispatching (in
    bytecode interpreters, emulators etc) compared with regular 'switch',
    because it uses multiple dispatch points (which help CPU branch
    predication). 'switch' uses only one.

    (My language has a dedicated feature for this, so using explicit
    computed goto is not needed. That allows it to outperform optimised
    /standard/ C that uses plain switch.)

    Absent a benchmark, I find that assertion specious. Speculation
    about performance on modern machines is just that; speculation.
    But machines these days are so incredibly complicated, and so
    dynamic during execution, that we simply cannot reason about
    them from first principles anymore: you either produce data for
    a particular target machine, or you're just guessing.

    OK, here is a test program which is a toy Pascal interpreter, running recursive Fibonacci to calculate fib(36).

    It was in C, then ported to my language. Someone also upgraded the C
    version to use computed goto when available:

    gcc -O2 using switch: 1.33 seconds (C version)
    gcc -O2 using computed goto: 0.78 seconds

    mm using 'doswitch': 1.03 seconds (my languages)
    mm using 'doswitchu': 0.74 seconds (with special feature)

    Timings are on Windows running on x64.

    In my language, 'doswitch' is just a looping version of switch.
    'doswitchu' is a version where the compiler uses multiple dispatch
    points, one per branch, rather than looping back to the start.

    Switching between the two means changing 'doswitch' to 'doswitchu'.

    In C switching would require a rewrite, unless you write it in a
    contrived manner. But then it still needs that bunch of macros shown
    below, and it needs that table of label pointers to be manually maintained.


    ----------------------------------------------------
    #if !defined(__GNUC__) || defined(FORCE_SWITCH)
    #define START start: switch (*pc++)
    #define CASE case
    #define NEXT goto start
    #define DEFAULT default
    puts("switch");
    #else

    /*************************************************************************
    Dependency: the ording of this table depends on the Code_t enum.

    *************************************************************************/
    static void *label[] = {
    &&add, &&sub, &&neg, &&mul, &&divd, &&remd, &&div2, &&rem2,
    &&eqli, &&neqi,
    &&lssi, &&leqi, &&gtri, &&geqi, &&dupl, &&swap, &&andb, &&orb, &&load, &&stor,
    &&halt, &&wri, &&wrc, &&wrl, &&rdi, &&rdc, &&rdl, &&eofi,
    &&eol, &&ldc, &&ldla,
    &&ldl, &&ldg, &&stl, &&stg, &&move, &&copy, &&addc, &&subc,
    &&mulc, &&jump,
    &&jumpz, &&call, &&adjs, &&sets, &&ret, &&DEFAULT
    };

    assert(label[ret] == &&ret);
    assert(label[ret+1] == &&DEFAULT);

    #define START goto *label[*pc++];
    #define CASE
    #define NEXT goto *label[*pc++]
    #define DEFAULT DEFAULT
    puts("goto");
    #endif

    START {
    CASE add : s[sp+1] += s[sp]; sp++; pc++; NEXT;
    CASE sub : s[sp+1] -= s[sp]; sp++; pc++; NEXT;
    CASE neg : s[sp]= -s[sp]; pc++; NEXT;
    CASE mul : s[sp+1] *= s[sp]; sp++; pc++; NEXT;
    ....







    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Fri May 22 14:04:07 2026
    From Newsgroup: comp.lang.c

    Bart <bc@freeuk.com> writes:
    On 22/05/2026 01:24, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:

    Allowing labels as expressions has advantages; I gave some
    examples. Being able to use parentheses is a consequence, but not
    useful in itself. But the special namespace puts paid to that.

    In C, label names are not expressions. It's that simple. C does
    not allow parentheses around a label name, and there is no reason
    at all why it should do so. (If it did, you'd probably complain
    that it's inconsistent.)

    You don't understand my point, so I will leave it.

    You don't seem to have ever made a "point", you just complain
    about things you either don't like or don't understand.


    Sure. FYI, here's the proposal that, as far as I can tell, led to C23
    allowing a label immediately before a "}". It was written by Martin
    Uecker in 2020.

    <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2496.pdf>

    This is very interesting. So perhaps tens of millions of C programmers >encountered the quirk, found it annoying, and moved on.

    The vast majority of C programmers have likely never used goto.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.lang.c on Fri May 22 14:44:55 2026
    From Newsgroup: comp.lang.c

    In article <10un0j7$obiv$2@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    On 21/05/2026 14:18, Dan Cross wrote:
    In article <10umdsa$hnml$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    [snip]
    I think one can level some reasonable criticism at the language,
    however. The `goto error;` idiom is used in C because there are
    few alternatives for cleanup handling on failure. Modulo what
    we discussed before, that code can _often_ be restructured to
    avoid it, but sometimes it can't, and frequently it just isn't.

    I think "isn't" is more common than "can't".

    Yes, that's the distinction I tried to draw.

    But I also don't think the
    "goto error" idiom is necessarily bad in itself - it's just that it is
    often used badly. A typical indication of poor usage is when a function
    is getting very long, and there are multiple "error" labels.

    However, the problem with this code was not the "goto error" idiom, or
    the "goto" itself - the problem was the mismatch between indentation and
    the statement under control of the "if". It would have been equally bad
    if it had been "return" rather than "goto", or if gcc cleanup attributes
    had been used to handle cleanup, or if some kind of "defer" mechanism
    had been used (as supported by some programming languages, and proposed
    for a future C version).

    These various cleanup mechanisms can definitely be better than "goto
    error", but they would not have prevented this error. (A programming >language that requires braces for statements controlled by "if", on the >other hand, /would/ have prevented the error.)

    Yes, the immediate cause of the error was the repeated `goto`;
    whether the misleading indentation was observed by the
    programmer or not I couldn't say, however. I have no way of
    knowing, but my sense at the time was that this was an example
    of copy-paste gone wrong. Wheeler suggested it might be due to
    removal as well; that's plausable.

    I do agree that a language where the grammar forced braces (or
    the equivalent) around conditionals would have prevented this,
    or at least made it much more obvious. Notably, both Rust and
    Go do that. Both also have automated formatters that are used
    as a matter of course, which would have made the error easier to
    spot; compare to the myriad different individual styles of C and
    C++, and paucity of effective formatters.

    Another method, one that I don't particularly care for, would
    have been to nest the conditionals on success; this gets ugly
    because code tends to move towards the right, and deeply nested
    control structures can be terribly confusing.

    However, Ken Thompson used to commit this grave sin, which sort
    of ameliorates the problem:

    if ((err = thing()) == SUCCESS)
    if ((err = other()) == SUCCESS)
    if ((err = third()) == SUCCESS)
    dosomething();
    return err;

    One could, of course, string those conditional together into a
    single `if` using `&&`, but Ken thought this was cleaner.

    [snip]
    Other languages feature "linear types", instances of which must
    be used exactly once: failure to cleanup on an error path is a
    compile time error. This means that you can't forget to
    deallocate memory, close a file, or unlock a mutex, for example,
    but I don't know that it directly addresses the issue where you
    just skip over the actual thing the program is supposed to do.
    Still, with more expressive type systems, it is likely one can
    much more easily structure the program so that the type of logic
    that lead to this failure is unnecessary.

    There is definitely potential for a language's type system to make it
    harder to make some kinds of mistakes. But there is a risk in making
    the language too restrictive - people end up writing horrible code to
    work around restrictions, or use "unsafe" code too much.

    Interesting. I've found it to be somewhat the opposite; using a
    richer type system has lead to code that is easier to understand
    and reason about, and less buggy: type-oriented programming can
    make entire categories of errors *unrepresentable*, so it's not
    just _harder_ to make certain kinds of mistakes, but
    _impossible_. The existance of an object of some type can be
    thought of as an existence proof that the invariants the type
    represents hold.

    And Alexis King wrote the great, "Parse, Don't Validate" essay
    some years ago where she talks about "Type-Driven Development": https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/

    More recently, Yaron Minsky gave a talk and discussed this in an
    OCaml context (https://www.youtube.com/watch?v=rUYP4C29yCw; the
    most relevant bits start at about 6:35. The talk is about AI
    and constraining agents, but the discussion around types is more
    general).

    I wrote the production OS loader for Oxide compute sleds using
    this technique in the virtual memory system (and other places):
    the loader uses multiple page sizes, and the rule is that, when
    mapping a region of memory, it uses the largest page size that
    it can, given size and alignment constraints. But I use the
    type system to make it impossible to, say, map a 2MiB "large"
    physical page frame to a non-2MiB aligned virtual boundary.

    I did some work, eons ago, in a language called XC that was specifically
    for XMOS microcontrollers. The language and tools had a feature that
    made data races impossible by not allowing competing access to shared >variables from different threads. Since threads were part of the
    hardware, and the tools analysed the code flow through threads, this
    could all be enforced at build time - data had to be passed in messages,
    not shared memory. But for some things that involved large buffers,
    that was hopelessly inefficient - and these devices were regularly used
    with USB, audio, and similar things that needed large and predictable >buffers. So code - even library and example code from the manufacturer
    - was full of inline assembly to work around the "smart-arse" language
    and tools.

    Hmm. This reads less like an indictment of the idea of stronger
    typing, but rather a failure to provide adequate abstractions in
    the type system.

    I'm going to mention Rust again; apologies. When confined to
    the safe subset, it _also_ has data race freedom. The rules
    that give this property are:

    1. Every object has exactly one owner, and assignments of
    non-trivial types change ownership (they are logically a
    "move");
    2. References to an object may be "borrowed" from the owner,
    and mutable references (that is, references that may be used
    to write to the object) are distinct from immutable
    references (that is, references that may only be used to read
    from an object);
    3. Mutable and immutable references are temporally mutually
    exclusive: a mutable reference may be borrowed from an object
    iff that is the only live reference to that object at the
    time: that is, it is not permitted to borrow a mut ref to an
    object if either another mut ref or any immutable refs to it
    are live; any number of immutable references may be taken to
    an object concurrently.

    If all of these rules are obeyed (and in safe rust, they're
    verified at compile time by the borrow checker) then you cannot
    have data races.

    At first glance it appears that it must suffer from the same
    drawbacks as `XC`, which you mentioned above. Except that the
    language does provide controlled ways to share data
    concurrently.

    Using the _unsafe_ subset, there is one place where it is
    permitted to have multiple, mutable references to an object: the
    `UnsafeCell`. This gives Rust interior mutability, which means
    that you can build safe abstractions for data sharing, like
    `Mutex` types that own the data they protect.

    That last bit is important: since the `Mutex` owns whatever it
    protects, it controls access to that thing: there's no going
    behind the `Mutex`'s back and accessing something outside of
    the lock. Instead, the `lock` method returns a `MutexGuard`
    object, which one might think of as a handle to the data,
    allowing the user to manipulate it, but also having a drop
    method (in Rust, that's basically a destructor) that
    automatically unlocks the mutex run the guard is destroyed.

    Anyway, the point is, both the rigor _and_ the expressiveness of
    the type system let you do things like this, whereas you just
    cannot in C.

    I understand that someone has written a paper proposing adding
    `defer` to C; that would obviate many of these problems.

    Yes. Jens Gustedt - one of the few members of the C standards committee
    who is vocal and public about pushing new ideas into C. (Of course not >everyone will agree with the ideas he comes with.)

    <https://gustedt.wordpress.com/2026/02/15/defer-available-in-gcc-and-clang/>

    Ah, I didn't realize it was Jens. Very cool.

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.lang.c on Fri May 22 14:57:09 2026
    From Newsgroup: comp.lang.c

    In article <10upivp$1fkbh$1@dont-email.me>, Bart <bc@freeuk.com> wrote:
    This is the same difference between:

    a = cond ? x : y;

    and:

    if (cond) {
    a = x;
    } else {
    a = y;
    }

    If ?: is considered a convenient, clearer short-cut for the latter,

    First, it's not, for reasons that have been explained to you but
    that you are ignoring because you don't like them.

    But beyond that, I don't think that's what it is "considered" to
    be, at all. `?:` can be used in an expression; `if` cannot.

    In fact, I find the ternary operator to be used rather
    sparingly, even in the kind of code you posted above.

    then the same applies to the goto.

    You have made it clear that you think that _should_ be true.
    However, it is _not_ true. A brief perusal of the standard
    would show _how_ it is not true. You may ask _why_ it is not
    true, but I don't know that anyone here can answer that
    definitively. Your argument that C is somehow bad because of
    _this_ does not follow.

    [snip]
      C is mostly a structured programming
    language - unstructured constructs like "goto" should be minimal, not
    encouraged.

    But when you do need to conditionally jump to L1 or L2, then ?: lets you
    do that with only one 'goto', not two.

    Actually it doesn't let you do that, because it's not allowed by
    the language. ;-}

    (I can do it with zero: (cond | L1 | L2); the 'goto' can be implied.)

    Yikes.

    We all know you don't use it yourself, but I can list half a dozen
    use-cases for 'goto'. Here are two:

    (1) For generated C code when transpiling from a higher level language. >Without 'goto' this would be near-impossible. (Not impossible, but hard >enough that it is easier to use another language.)

    (2) For porting code to C that already uses 'goto'.

    Strawman. No one said `goto` in C should be disallowed.

    The above quote said it should be _discouraged_ and its use
    _minimal_. That doesn't mean it's not useful in some cases.

    However, that's not support for standardizing the use of labels
    you are advocating for so passionately.

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Fri May 22 17:12:55 2026
    From Newsgroup: comp.lang.c

    On 22/05/2026 16:44, Dan Cross wrote:
    In article <10un0j7$obiv$2@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    On 21/05/2026 14:18, Dan Cross wrote:
    In article <10umdsa$hnml$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:

    [snip]
    Other languages feature "linear types", instances of which must
    be used exactly once: failure to cleanup on an error path is a
    compile time error. This means that you can't forget to
    deallocate memory, close a file, or unlock a mutex, for example,
    but I don't know that it directly addresses the issue where you
    just skip over the actual thing the program is supposed to do.
    Still, with more expressive type systems, it is likely one can
    much more easily structure the program so that the type of logic
    that lead to this failure is unnecessary.

    There is definitely potential for a language's type system to make it
    harder to make some kinds of mistakes. But there is a risk in making
    the language too restrictive - people end up writing horrible code to
    work around restrictions, or use "unsafe" code too much.

    Interesting. I've found it to be somewhat the opposite; using a
    richer type system has lead to code that is easier to understand
    and reason about, and less buggy: type-oriented programming can
    make entire categories of errors *unrepresentable*, so it's not
    just _harder_ to make certain kinds of mistakes, but
    _impossible_. The existance of an object of some type can be
    thought of as an existence proof that the invariants the type
    represents hold.

    In general, I agree - I prefer strongly typed languages, and it's always
    best if an error is identified by the code being uncompilable rather
    than waiting for run-time testing (or testing by evil hackers). But if
    people want to do something with the language that is hard to achieve efficiently within the norms of the language, and there are escape
    hatches ("unsafe" code, inline assembly, calling external C functions,
    etc.) then people will use them. People do sometimes write C code that knowingly depends on undefined behaviour, because it makes their results
    more efficient and "it worked when I tested it".

    Type safety - like any kind of safety - can sometimes get in the way of "getting things done". A balance always needs to be found to ensure
    that people can do what they need to do without breaking rules or using "escapes" more than absolutely necessary. If people find that much of
    their Rust code is "unsafe", then most of the point of using Rust is
    lost. (Of course this also means that different languages are suited to different tasks.)


    And Alexis King wrote the great, "Parse, Don't Validate" essay
    some years ago where she talks about "Type-Driven Development": https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/

    More recently, Yaron Minsky gave a talk and discussed this in an
    OCaml context (https://www.youtube.com/watch?v=rUYP4C29yCw; the
    most relevant bits start at about 6:35. The talk is about AI
    and constraining agents, but the discussion around types is more
    general).

    I wrote the production OS loader for Oxide compute sleds using
    this technique in the virtual memory system (and other places):
    the loader uses multiple page sizes, and the rule is that, when
    mapping a region of memory, it uses the largest page size that
    it can, given size and alignment constraints. But I use the
    type system to make it impossible to, say, map a 2MiB "large"
    physical page frame to a non-2MiB aligned virtual boundary.

    I did some work, eons ago, in a language called XC that was specifically
    for XMOS microcontrollers. The language and tools had a feature that
    made data races impossible by not allowing competing access to shared
    variables from different threads. Since threads were part of the
    hardware, and the tools analysed the code flow through threads, this
    could all be enforced at build time - data had to be passed in messages,
    not shared memory. But for some things that involved large buffers,
    that was hopelessly inefficient - and these devices were regularly used
    with USB, audio, and similar things that needed large and predictable
    buffers. So code - even library and example code from the manufacturer
    - was full of inline assembly to work around the "smart-arse" language
    and tools.

    Hmm. This reads less like an indictment of the idea of stronger
    typing, but rather a failure to provide adequate abstractions in
    the type system.

    It is not an indication of problems with stronger typing, but it is an indication that the restrictions inherent in the language and tools made
    them somewhat unsuitable for a lot of use-cases that fitted the hardware
    well. (Modern XMOS tools have changed significantly since those days,
    perhaps partly because of such issues.)


    I'm going to mention Rust again; apologies. When confined to
    the safe subset, it _also_ has data race freedom. The rules
    that give this property are:

    1. Every object has exactly one owner, and assignments of
    non-trivial types change ownership (they are logically a
    "move");
    2. References to an object may be "borrowed" from the owner,
    and mutable references (that is, references that may be used
    to write to the object) are distinct from immutable
    references (that is, references that may only be used to read
    from an object);
    3. Mutable and immutable references are temporally mutually
    exclusive: a mutable reference may be borrowed from an object
    iff that is the only live reference to that object at the
    time: that is, it is not permitted to borrow a mut ref to an
    object if either another mut ref or any immutable refs to it
    are live; any number of immutable references may be taken to
    an object concurrently.

    If all of these rules are obeyed (and in safe rust, they're
    verified at compile time by the borrow checker) then you cannot
    have data races.

    That's all good, by the sound of it. But if people find this gets in
    the way of efficiency - perhaps because they know that it is safe to
    hold two separate mutable references to an object for reasons the borrow checker can't see - the temptation to use "unsafe" or other workarounds
    comes quickly.

    That does not mean that a language should not try to have such rules,
    and enforce them at compile time - far from it. The aim should be that
    as much code as possible is correct, or at least immune to particular
    classes of bugs, checked by the compiler and tools.


    At first glance it appears that it must suffer from the same
    drawbacks as `XC`, which you mentioned above. Except that the
    language does provide controlled ways to share data
    concurrently.

    Using the _unsafe_ subset, there is one place where it is
    permitted to have multiple, mutable references to an object: the `UnsafeCell`. This gives Rust interior mutability, which means
    that you can build safe abstractions for data sharing, like
    `Mutex` types that own the data they protect.


    If "unsafe" gives enough, but is rarely needed in most code, then it
    sounds like a good balance.

    That last bit is important: since the `Mutex` owns whatever it
    protects, it controls access to that thing: there's no going
    behind the `Mutex`'s back and accessing something outside of
    the lock. Instead, the `lock` method returns a `MutexGuard`
    object, which one might think of as a handle to the data,
    allowing the user to manipulate it, but also having a drop
    method (in Rust, that's basically a destructor) that
    automatically unlocks the mutex run the guard is destroyed.

    Anyway, the point is, both the rigor _and_ the expressiveness of
    the type system let you do things like this, whereas you just
    cannot in C.


    Sure.

    I understand that someone has written a paper proposing adding
    `defer` to C; that would obviate many of these problems.

    Yes. Jens Gustedt - one of the few members of the C standards committee
    who is vocal and public about pushing new ideas into C. (Of course not
    everyone will agree with the ideas he comes with.)

    <https://gustedt.wordpress.com/2026/02/15/defer-available-in-gcc-and-clang/>

    Ah, I didn't realize it was Jens. Very cool.

    - Dan C.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Fri May 22 16:16:18 2026
    From Newsgroup: comp.lang.c

    On 22/05/2026 15:04, Scott Lurndal wrote:
    Bart <bc@freeuk.com> writes:
    On 22/05/2026 01:24, Keith Thompson wrote:

    <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2496.pdf>

    This is very interesting. So perhaps tens of millions of C programmers
    encountered the quirk, found it annoying, and moved on.

    The vast majority of C programmers have likely never used goto.

    Source? Oh, 'likely', so it is just a random guess.

    In any case the issue (labels at the end of a compound statement)
    applied also to case labels and to 'default:'.

    Now you're going to say the vast majority of C programmers have never
    used 'switch' either! 'Probably...'



    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Fri May 22 17:47:39 2026
    From Newsgroup: comp.lang.c

    On 22/05/2026 17:16, Bart wrote:
    On 22/05/2026 15:04, Scott Lurndal wrote:
    Bart <bc@freeuk.com> writes:
    On 22/05/2026 01:24, Keith Thompson wrote:

    <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2496.pdf>

    This is very interesting. So perhaps tens of millions of C programmers
    encountered the quirk, found it annoying, and moved on.

    The vast majority of C programmers have likely never used goto.

    Source? Oh, 'likely', so it is just a random guess.

    In any case the issue (labels at the end of a compound statement)
    applied also to case labels and to 'default:'.

    If a programmer wants a case label or default label at the end of a
    switch, doing nothing, then (prior to C23, and excluding compiler
    extensions) they have to put in an empty statement - "default : ;". (Or
    they could use "break;" to make code feel more symmetrical.) I can
    imagine that happening, and I can imagine that is the most likely
    situation where you would want a label right before an end brace. This
    seems to be the main motivation for the change in C23.

    It is much harder to imagine that this "bites" anyone, or even annoys
    people in any significant way.


    Now you're going to say the vast majority of C programmers have never
    used 'switch' either! 'Probably...'

    I think you might need to switch out your crystal ball - your
    predictions about what people will say or do have rarely come close to reality. Or, perhaps, you should stop saying such stupid things.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Fri May 22 16:16:55 2026
    From Newsgroup: comp.lang.c

    cross@spitfire.i.gajendra.net (Dan Cross) writes:
    In article <10upivp$1fkbh$1@dont-email.me>, Bart <bc@freeuk.com> wrote: >>This is the same difference between:

    a = cond ? x : y;

    and:

    if (cond) {
    a = x;
    } else {
    a = y;
    }

    If ?: is considered a convenient, clearer short-cut for the latter,

    First, it's not, for reasons that have been explained to you but
    that you are ignoring because you don't like them.

    But beyond that, I don't think that's what it is "considered" to
    be, at all. `?:` can be used in an expression; `if` cannot.

    In fact, I find the ternary operator to be used rather
    sparingly, even in the kind of code you posted above.

    I wouldn't necessarily categorize it as "sparingly"; Searching
    through the simulator source base, I find it used rather
    frequently in certain scenarios:

    diag = snprintf(cp, remaining, "Feature1:%c Feature2:%c", is_feature1()?'+':'-',
    is_feature2()?'+':'-');

    deliver_interrupt(IntVec_TIMER, is_secure()?FLAGS_SECURE:0u);
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Fri May 22 17:35:06 2026
    From Newsgroup: comp.lang.c

    On 22/05/2026 16:47, David Brown wrote:
    On 22/05/2026 17:16, Bart wrote:
    On 22/05/2026 15:04, Scott Lurndal wrote:
    Bart <bc@freeuk.com> writes:
    On 22/05/2026 01:24, Keith Thompson wrote:

    <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2496.pdf>

    This is very interesting. So perhaps tens of millions of C programmers >>>> encountered the quirk, found it annoying, and moved on.

    The vast majority of C programmers have likely never used goto.

    Source? Oh, 'likely', so it is just a random guess.

    In any case the issue (labels at the end of a compound statement)
    applied also to case labels and to 'default:'.

    If a programmer wants a case label or default label at the end of a
    switch, doing nothing, then (prior to C23, and excluding compiler extensions) they have to put in an empty statement - "default : ;".  (Or they could use "break;" to make code feel more symmetrical.)  I can
    imagine that happening, and I can imagine that is the most likely
    situation where you would want a label right before an end brace.  This seems to be the main motivation for the change in C23.

    It is much harder to imagine that this "bites" anyone, or even annoys
    people in any significant way.

    And yet, the link at the top was about somebody proposing to fix this
    very thing. And it was accepted.

    There was no reason whatsoever to ban:

    L:}
    L:int x;

    other than the grammar happening to define labels in a certain way. That
    there are workarounds is not the point.


    Now you're going to say the vast majority of C programmers have never
    used 'switch' either! 'Probably...'

    I think you might need to switch out your crystal ball - your
    predictions about what people will say or do have rarely come close to reality.  Or, perhaps, you should stop saying such stupid things.

    Scott Lurndal said something stupid and I responded with sarcasm. Yet
    you attack me and not him. Biased at all?

    This is what I responded to in his post:

    * An assertion that the 'vast majority' of C programmers have never used 'goto'.

    * The assumption that the labels issue was only about goto labels

    Is that assertion true? I don't know, but it sounds unlikely more than
    likely. If I look at some codebases:

    gotos used?

    Lua Yes (300 in 30Kloc)
    SQLite Yes (Over 600 in 220Kloc of comment-heavy code)
    Tiny C Yes (40 in 30Kloc
    LibJPEG Yes (About 80 in 60Kloc)
    CPython Yes (over 1000 just in Objects folder, about 100Kloc)
    A68G Yes (a handful, but they exist, in 70Kloc)
    GMP Yes (50 in mpn/generic, 36Kloc)

    I struggled to find any sizeable project without it.

    So if you had to put money on it would you say Scott was right? What
    does 'vast majority' mean anyway?

    (Of course, Scott said programmers rather than applications. Maybe 1000
    people work on CPython, but there's only one who writes all the gotos!)
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Fri May 22 17:53:06 2026
    From Newsgroup: comp.lang.c

    On 22/05/2026 15:57, Dan Cross wrote:
    In article <10upivp$1fkbh$1@dont-email.me>, Bart <bc@freeuk.com> wrote:
    This is the same difference between:

    a = cond ? x : y;

    and:

    if (cond) {
    a = x;
    } else {
    a = y;
    }

    If ?: is considered a convenient, clearer short-cut for the latter,

    First, it's not, for reasons that have been explained to you but
    that you are ignoring because you don't like them.

    But beyond that, I don't think that's what it is "considered" to
    be, at all. `?:` can be used in an expression; `if` cannot.

    In fact, I find the ternary operator to be used rather
    sparingly, even in the kind of code you posted above.

    I wonder if there is any feature of C that is not used sparingly by the regulars here? So far:

    * Hardly anyone uses 'goto' or labels

    * Few use ?:

    * Nobody takes advantage of labels' private namespace (but at the same
    time, it is definitely not pointless!)

    * Very few ever refactor source files such as by splitting or combining

    * Nobody here has ever used label pointers. (An extension, yes, but then
    I guess nobody has ever used an extension either?)

    * David Brown at least rarely uses forward function declarations

    I have a sneaking suspicion that people are deliberately playing such
    things down (or up, for label namespaces); I wonder why?


    then the same applies to the goto.

    You have made it clear that you think that _should_ be true.
    However, it is _not_ true. A brief perusal of the standard
    would show _how_ it is not true.

    You mean whether C allows 'goto expr'? Yes I know it is not allowed. I responded to someone saying it wouldn't be useful if it was.

    (Part of a broader point about what is missed with labels having a
    private namespace and only appearing in certain contexts.)


    (I can do it with zero: (cond | L1 | L2); the 'goto' can be implied.)

    Yikes.

    Blame Algol68, that's where implied goto comes from.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.lang.c on Fri May 22 17:32:08 2026
    From Newsgroup: comp.lang.c

    In article <10upkur$1fkbh$2@dont-email.me>, Bart <bc@freeuk.com> wrote:
    On 22/05/2026 13:13, Dan Cross wrote:
    In article <10upbvq$1dhq4$1@dont-email.me>, Bart <bc@freeuk.com> wrote:
    On 22/05/2026 05:52, Janis Papanagnou wrote:
    On 2026-05-21 21:23, David Brown wrote:
    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:
    [...]
    Having a label be just another term could be a benefit. For example: >>>>>>
       goto cond ? L1 : L2;

    What benefit is it to have an alternative construct for existing
    clearer 'if' and 'switch' features? - What's the concrete gain? -
    To create harder to follow spaghetti-code?

    You're asking why '?:' exists when you can use 'if-else'?

    Is that meant to be obtuse? No, the question is why anyone
    would want to write code that uses `goto` in that fashion.

    The ternary operator, `?:`, is expression-oriented. Label names
    are just identifiers; identifiers may appear in expressions
    (specifically, "primary expressions"), but only if they name an
    object, or a function.

    And as I said, gnu C allows just that.

    Actually no, it doesn't; syntactically it uses a very different
    form (one that appears to conflict with rvalue references in
    C++, FWIW).
    https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html

    But by itself, "GNU C does it!" is meaningless. Ok, GNU C
    allows one to write expression treat labels as values. So what?
    Is it widely used? It doesn't appear to be.

    You're anyone asking the wrong question. It's usually a mistake to try
    and double-guess the needs of a programmer, since it's possible they may >have a genuine use-case you haven't thought of. Or they have a
    particular coding style they want to use.

    Congratulations: that's the central tension faced by language
    designers. Let me give a counter example: in the Plan 9 C
    dialect, there's an extension where, if a type referring to a
    struct is named in an enclosing struct, then the elements of
    that struct are available in the enclosing struct.

    For example:

    typedef struct Lock Lock;
    struct Lock {
    int lock;
    Thread *owner;
    // Whatever else goes into a lock...
    };

    int lock(Lock *lock);
    void unlock(Lock *lock);

    typedef struct Ref Ref;
    struct Ref {
    Lock;
    int nref;
    };

    void incref(Ref *ref);
    int decref(Ref *ref);

    Using this, one can pass a pointer to a `Ref` to a function that
    takes a lock. For example, image if `decref` were defined to
    return the previous value of the ref count, it might be written
    as such:

    int
    decref(Ref *ref)
    {
    int nref;

    if (ref == nil)
    panic("decref got nil ref");

    lock(ref);
    nref = ref->nref;
    --ref->nref;
    unlock(ref);

    if (nref <= 0)
    panic("decref got corrupt ref);

    return nref;
    }

    Note that Plan 9 C spells `NULL` as `nil`, and note how we just
    passed the `Ref*` to `lock()` and `unlock()`. Kinda cool, huh?

    Well no. It actually hides a really subtle problem. Consider
    this struct:

    // A Chan (short for "channel") is sort of like a queue;
    // lots of Plan 9's internals were inspired for Hoare's
    // CSP.
    typedef struct Chan Chan;
    struct Chan {
    Ref;
    Lock;

    // ...The things that go into a channel.
    };

    Here, there's a problem; which "Lock" do we get? Actually, we
    don't get to choose. The compiler combines them for us.

    Why is this a problem? Because the two locks are doing
    semantically different things: the `Lock` in the `Ref` is
    protecting updates to the reference counter, which in turn
    concerns the lifetime of the `Chan`. The `Lock` embedded
    directly into the `Chan`, on the other hand, is meant to
    serialize access to the `Chan`'s non-`Ref` data members.

    By combining them, we are now conflating the the two, but
    moreover creating contention where it was very likely not meant
    to be.

    And in fact, it was found that this _was_ a problem. Consider
    a program that is forking repeatedly, but also using a channel;
    the `Ref` on the channel is incremented every time the process
    `rfork`'s (Plan 9 uses `rfork`; `fork` is just a library
    function that calls `rfork` with some default flags), but that
    de facto locks the `Chan`, which contends with a proess that's
    actually trying to _use_ the `Chan`.

    In retrospect, it was determined that that language feature was
    a mistake.

    A label name can only appear as part of a labeled statement;
    thus, it names neither an object nor a function, so it cannot be
    used as part of a primary expression, and so therefore, one
    cannot use label names in the form mentioned above.

    You seem to be saying, "look, you can't do this!" But the
    question is, _why would you want to do that in the first place_?

    So what on earth was the point of gnu C's &&L label pointers?

    Who knows? Go find when it was added and ask the person who
    wrote it? Probably Stallman. Good luck getting an answer.

    Other than enabling faster interpreters such as with CPython (which
    needs all the help it can get). Writing FSMs and so on...

    You seem to downplaying the benefits only because standard C doesn't
    allow it.

    Not at all. It may well be useful for some applications. Does
    that justify adding it to the language, though?

    My subjective opinion is that that is not sweet: it's hideous.

    OK. Of course we'd have to see the alternative.

    The alternative is an `if` statement with a `goto`.

    Obviously someone thinks, or thought, that this had some kind of
    benefit, otherwise they wouldn't have implemented it. It seems
    extraordinarily rarely used.

    Sometimes people have an idea of what they think is going to be
    a neat feature, only to find out with some experience that it
    didn't pan out the way they thought it might. This happens all
    the time;

    It happens with me too! You should see some of my ideas that were too
    naff. There were also better ones but that just didn't get enough usage
    to warrant the support needed.

    But I believe 'goto' is fundamental at this level of language, and it is >handy to allow first-class handling of labels even if little used: it's >something that if it isn't there when needed, it is harder to get around.

    I don't know. Rust doesn't have `goto`, and it does ok at the
    same level as C: I've used it in Unix-class, multi-processor,
    multi-tasking, internally-threaded kernels; my colleagues use it
    on embedded microcontrollers; people I know use it to write
    jitted bytecode interpreters; all that kind of thing that people
    often incorrectly assume cannot be written in any language other
    than C.

    You don't have to like Rust, of course, but other languages
    exist that have similar capabilities and don't have `goto`.

    In gnu C computed goto tends to be used for faster dispatching (in
    bytecode interpreters, emulators etc) compared with regular 'switch',
    because it uses multiple dispatch points (which help CPU branch
    predication). 'switch' uses only one.

    (My language has a dedicated feature for this, so using explicit
    computed goto is not needed. That allows it to outperform optimised
    /standard/ C that uses plain switch.)

    Absent a benchmark, I find that assertion specious. Speculation
    about performance on modern machines is just that; speculation.
    But machines these days are so incredibly complicated, and so
    dynamic during execution, that we simply cannot reason about
    them from first principles anymore: you either produce data for
    a particular target machine, or you're just guessing.

    OK, here is a test program which is a toy Pascal interpreter, running >recursive Fibonacci to calculate fib(36).

    It was in C, then ported to my language. Someone also upgraded the C
    version to use computed goto when available:

    gcc -O2 using switch: 1.33 seconds (C version)
    gcc -O2 using computed goto: 0.78 seconds

    mm using 'doswitch': 1.03 seconds (my languages)
    mm using 'doswitchu': 0.74 seconds (with special feature)

    Timings are on Windows running on x64.

    In my language, 'doswitch' is just a looping version of switch.
    'doswitchu' is a version where the compiler uses multiple dispatch
    points, one per branch, rather than looping back to the start.

    Where to start.

    First, why not post the code? It's hard know what is _actually_
    going on here because one cannot see how the implementations
    might differ otherwise, aside from the tiny snippet you
    included.

    Second, how was it compiled? What optimization settings? What
    compiler? Did you test different compilers to see if they did
    things differently? You've made a broad general assertion about
    the code generated in response to a `switch` statement; my own
    experience is that that varies widely based on compiler, target
    architecture, and optimization settings.

    Finally, your assertion was that the version using your multiple
    dispatch is faster due to branch predicition: you've shown that
    this is faster, but you haven't shown _why_, let alone that it
    is due to branch prediction. Since you're running this on
    x86_64, did you try to look at the machine's perf counters to
    see what's actually happening?

    Switching between the two means changing 'doswitch' to 'doswitchu'.

    In C switching would require a rewrite, unless you write it in a
    contrived manner. But then it still needs that bunch of macros shown
    below, and it needs that table of label pointers to be manually maintained.

    [snip]

    I'm not sure what you mean here. You've given examples of
    timings using computed gotos and `switch` in C; it's unclear
    what would "require a rewrite" since you appear to already have
    both versions.

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.lang.c on Fri May 22 17:43:34 2026
    From Newsgroup: comp.lang.c

    In article <Xj%PR.313884$yHZ7.10738@fx10.iad>,
    Scott Lurndal <slp53@pacbell.net> wrote:
    cross@spitfire.i.gajendra.net (Dan Cross) writes:
    In article <10upivp$1fkbh$1@dont-email.me>, Bart <bc@freeuk.com> wrote: >>>This is the same difference between:

    a = cond ? x : y;

    and:

    if (cond) {
    a = x;
    } else {
    a = y;
    }

    If ?: is considered a convenient, clearer short-cut for the latter,

    First, it's not, for reasons that have been explained to you but
    that you are ignoring because you don't like them.

    But beyond that, I don't think that's what it is "considered" to
    be, at all. `?:` can be used in an expression; `if` cannot.

    In fact, I find the ternary operator to be used rather
    sparingly, even in the kind of code you posted above.

    I wouldn't necessarily categorize it as "sparingly"; Searching
    through the simulator source base, I find it used rather
    frequently in certain scenarios:

    diag = snprintf(cp, remaining, "Feature1:%c Feature2:%c", is_feature1()?'+':'-',
    is_feature2()?'+':'-');

    deliver_interrupt(IntVec_TIMER, is_secure()?FLAGS_SECURE:0u);

    Those are the kinds of scenarios where it can be useful; what
    percentage of code overall uses that versus `if`, though?

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Fri May 22 20:27:12 2026
    From Newsgroup: comp.lang.c

    On 22/05/2026 18:32, Dan Cross wrote:
    In article <10upkur$1fkbh$2@dont-email.me>, Bart <bc@freeuk.com> wrote:

    [computed goto]

    (My language has a dedicated feature for this, so using explicit
    computed goto is not needed. That allows it to outperform optimised
    /standard/ C that uses plain switch.)

    Absent a benchmark, I find that assertion specious. Speculation
    about performance on modern machines is just that; speculation.
    But machines these days are so incredibly complicated, and so
    dynamic during execution, that we simply cannot reason about
    them from first principles anymore: you either produce data for
    a particular target machine, or you're just guessing.

    OK, here is a test program which is a toy Pascal interpreter, running
    recursive Fibonacci to calculate fib(36).

    It was in C, then ported to my language. Someone also upgraded the C
    version to use computed goto when available:

    gcc -O2 using switch: 1.33 seconds (C version)
    gcc -O2 using computed goto: 0.78 seconds

    mm using 'doswitch': 1.03 seconds (my languages)
    mm using 'doswitchu': 0.74 seconds (with special feature)

    Timings are on Windows running on x64.

    In my language, 'doswitch' is just a looping version of switch.
    'doswitchu' is a version where the compiler uses multiple dispatch
    points, one per branch, rather than looping back to the start.

    Where to start.

    First, why not post the code? It's hard know what is _actually_
    going on here because one cannot see how the implementations
    might differ otherwise, aside from the tiny snippet you
    included.


    See: https://github.com/sal55/langs/blob/master/tinypas.c

    About 1.5Kloc. This will use computed goto with gnu C (ie __GNUC__ is defined). Uncomment 'FORCE_SWITCH' macro definition to always use 'switch'.

    When it runs it will anyway say 'goto' or 'switch'.

    For test input, use the program below, for example when it is called 'fib.pas':

    > time ./tinypas fib.pas

    Use any optimisation setting you like; I used -O2 and -O3 since we're
    trying to get the best performance.

    On Windows I got timings of 0.78/1.33 seconds goto/switch for x64.

    I also just tried RPi4 with ARM64, and it was 2.6/5.6 seconds
    goto/switch. So it made it twice as fast.

    ------------------------------------------
    program fibonacci(output);

    var i:integer;

    function fib(n:integer): integer;
    begin
    if (n < 3) then
    fib := 1
    else
    fib := fib(n-1) + fib(n-2);
    end;


    begin
    for i := 36 to 36 do
    writeln(i, ' ', fib(i));
    writeln('...');
    end.
    ------------------------------------------


    I use the same methods for own scripting language interpreter, a project
    of 40Kloc in my language. The same task as above takes 0.5s on Windows
    (recall that gcc took 0.78 seconds), even though this language uses
    dynamic typing not static like the Pascal.

    I can port that project to Linux and/or ARM64 via C, and maintain the
    same speeds, because I can translate my language's fast dispatch methods
    into gnu C label pointers.

    Second, how was it compiled? What optimization settings? What
    compiler? Did you test different compilers to see if they did
    things differently? You've made a broad general assertion about
    the code generated in response to a `switch` statement; my own
    experience is that that varies widely based on compiler, target
    architecture, and optimization settings.

    It will depend on the task and spread of workload. If the program being
    run is spending a lot of time in libraries, then bytecode dispatch is
    less of the overall overhead.

    Finally, your assertion was that the version using your multiple
    dispatch is faster due to branch predicition: you've shown that
    this is faster, but you haven't shown _why_, let alone that it
    is due to branch prediction. Since you're running this on
    x86_64, did you try to look at the machine's perf counters to
    see what's actually happening?

    It's a well-known technique. It was used on CPython (as compiled for
    Linux, since on Windows it doesn't use gcc), and may still be. Athough
    there are some experients to move towards complex threaded code via TCO instead.

    In any case, it seems to work, so who cares why?

    Switching between the two means changing 'doswitch' to 'doswitchu'.

    In C switching would require a rewrite, unless you write it in a
    contrived manner. But then it still needs that bunch of macros shown
    below, and it needs that table of label pointers to be manually maintained. >>
    [snip]

    I'm not sure what you mean here. You've given examples of
    timings using computed gotos and `switch` in C; it's unclear
    what would "require a rewrite" since you appear to already have
    both versions.

    You know what normal switch looks like:

    while (!stopped) {
    switch (opcode) {
    case ADD:
    ...

    Computed goto would first need a jumptable:

    void* jumptable[] = {&&ADDLAB, ....};

    Then dispatch at every point:

    goto *jumptable[opcode];
    ADDLAB:
    ....
    goto *jumptable[opcode]

    It is quite different. In Tinypas, it wraps it up in macros so that the
    same body of the 'switch' can be used for both choices.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Fri May 22 20:57:26 2026
    From Newsgroup: comp.lang.c

    Bart <bc@freeuk.com> writes:
    On 22/05/2026 16:47, David Brown wrote:
    On 22/05/2026 17:16, Bart wrote:
    On 22/05/2026 15:04, Scott Lurndal wrote:
    Bart <bc@freeuk.com> writes:
    On 22/05/2026 01:24, Keith Thompson wrote:

    <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2496.pdf>

    This is very interesting. So perhaps tens of millions of C programmers >>>>> encountered the quirk, found it annoying, and moved on.

    The vast majority of C programmers have likely never used goto.

    Source? Oh, 'likely', so it is just a random guess.

    In any case the issue (labels at the end of a compound statement)
    applied also to case labels and to 'default:'.

    If a programmer wants a case label or default label at the end of a
    switch, doing nothing, then (prior to C23, and excluding compiler
    extensions) they have to put in an empty statement - "default : ;".  (Or >> they could use "break;" to make code feel more symmetrical.)  I can
    imagine that happening, and I can imagine that is the most likely
    situation where you would want a label right before an end brace.  This
    seems to be the main motivation for the change in C23.

    It is much harder to imagine that this "bites" anyone, or even annoys
    people in any significant way.

    And yet, the link at the top was about somebody proposing to fix this
    very thing. And it was accepted.

    There was no reason whatsoever to ban:

    L:}
    L:int x;

    other than the grammar happening to define labels in a certain way. That >there are workarounds is not the point.


    Now you're going to say the vast majority of C programmers have never
    used 'switch' either! 'Probably...'

    I think you might need to switch out your crystal ball - your
    predictions about what people will say or do have rarely come close to
    reality.  Or, perhaps, you should stop saying such stupid things.

    Scott Lurndal said something stupid and I responded with sarcasm. Yet
    you attack me and not him. Biased at all?

    This is what I responded to in his post:

    * An assertion that the 'vast majority' of C programmers have never used >'goto'.

    Think about it. You cite 7 projects that have goto's in them.

    Google estimates there are 11 to 13 million C programmers.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Fri May 22 21:03:06 2026
    From Newsgroup: comp.lang.c

    cross@spitfire.i.gajendra.net (Dan Cross) writes:
    In article <Xj%PR.313884$yHZ7.10738@fx10.iad>,
    Scott Lurndal <slp53@pacbell.net> wrote:
    cross@spitfire.i.gajendra.net (Dan Cross) writes:
    In article <10upivp$1fkbh$1@dont-email.me>, Bart <bc@freeuk.com> wrote: >>>>This is the same difference between:

    a = cond ? x : y;

    and:

    if (cond) {
    a = x;
    } else {
    a = y;
    }

    If ?: is considered a convenient, clearer short-cut for the latter,

    First, it's not, for reasons that have been explained to you but
    that you are ignoring because you don't like them.

    But beyond that, I don't think that's what it is "considered" to
    be, at all. `?:` can be used in an expression; `if` cannot.

    In fact, I find the ternary operator to be used rather
    sparingly, even in the kind of code you posted above.

    I wouldn't necessarily categorize it as "sparingly"; Searching
    through the simulator source base, I find it used rather
    frequently in certain scenarios:

    diag = snprintf(cp, remaining, "Feature1:%c Feature2:%c", is_feature1()?'+':'-',
    is_feature2()?'+':'-');

    deliver_interrupt(IntVec_TIMER, is_secure()?FLAGS_SECURE:0u);

    Those are the kinds of scenarios where it can be useful; what
    percentage of code overall uses that versus `if`, though?

    - Dan C.


    Other than the cases above, 'if' dominates by orders of magnitude.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.lang.c on Fri May 22 22:02:59 2026
    From Newsgroup: comp.lang.c

    In article <ew3QR.651258$ZRIe.237915@fx22.iad>,
    Scott Lurndal <slp53@pacbell.net> wrote:
    cross@spitfire.i.gajendra.net (Dan Cross) writes:
    In article <Xj%PR.313884$yHZ7.10738@fx10.iad>,
    Scott Lurndal <slp53@pacbell.net> wrote:
    cross@spitfire.i.gajendra.net (Dan Cross) writes:
    In article <10upivp$1fkbh$1@dont-email.me>, Bart <bc@freeuk.com> wrote: >>>>>This is the same difference between:

    a = cond ? x : y;

    and:

    if (cond) {
    a = x;
    } else {
    a = y;
    }

    If ?: is considered a convenient, clearer short-cut for the latter,

    First, it's not, for reasons that have been explained to you but
    that you are ignoring because you don't like them.

    But beyond that, I don't think that's what it is "considered" to
    be, at all. `?:` can be used in an expression; `if` cannot.

    In fact, I find the ternary operator to be used rather
    sparingly, even in the kind of code you posted above.

    I wouldn't necessarily categorize it as "sparingly"; Searching
    through the simulator source base, I find it used rather
    frequently in certain scenarios:

    diag = snprintf(cp, remaining, "Feature1:%c Feature2:%c", is_feature1()?'+':'-',
    is_feature2()?'+':'-');

    deliver_interrupt(IntVec_TIMER, is_secure()?FLAGS_SECURE:0u);

    Those are the kinds of scenarios where it can be useful; what
    percentage of code overall uses that versus `if`, though?

    Other than the cases above, 'if' dominates by orders of magnitude.

    That was all I meant by "sparingly"; sorry if that wasn't clear.

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Fri May 22 18:37:49 2026
    From Newsgroup: comp.lang.c

    On 2026-05-21 23:58, Janis Papanagnou wrote:
    On 2026-05-22 02:24, Keith Thompson wrote:
    ...
    I suggest using the term "name space" rather than "namespace",
    which happens to be the name of a C++ feature.

    The term "namespace" is, as I observe, a regular English word
    (it's in my dictionary, at least).
    ...
    ("A namespace in C++ is denoted by the keyword 'namespace'.")

    The term "name spaces" is officially defined by the C standard (6.2.3p1).

    "Thus, there are separate _name spaces_ for various categories of
    identifiers, as follows:"

    Note that "name spaces" is in italics, an ISO convention indicating that
    the sentence containing the term is the official definition of that
    term, with a meaning more specific than that given in your dictionary.
    Every use of "name space" in the C standard is consistent with it being
    the singular term corresponding to the plural term "name spaces".

    The C++ standard also uses the term "name space", but does not define it
    - but seems to use it in a fashion consistent with C's definition,
    except that C++ has a smaller variety of categories. It also uses the
    term namespace without definition. It does formally define the terms namespace-definition and namespace-body, and the keyword namespace.
    Every appearance of "namespace" in the C++ standard has a meaning
    inconsistent with the one defined by the C standard and used by the C++ standard for the term "name spaces".

    Therefore, I think it's crucial not to confuse the two meanings, which
    requires being careful about the difference between "name space" and "namespace".
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Sat May 23 00:24:45 2026
    From Newsgroup: comp.lang.c

    On 22/05/2026 21:57, Scott Lurndal wrote:
    Bart <bc@freeuk.com> writes:

    This is what I responded to in his post:

    * An assertion that the 'vast majority' of C programmers have never used
    'goto'.

    Think about it. You cite 7 projects that have goto's in them.

    How many millions do you want me to look at?

    Google estimates there are 11 to 13 million C programmers.

    I can't isolate the work of individual programmers. I can only look at C codebases.

    So let's say that a 'vast majority' of programmers equates to 90% of
    codebases being free of 'goto'.

    That is, only 1 in 10 projects have one or more 'goto'.

    Then the probably of the first 7 projects I look at all having gotos is near-zero (I worked it out as 1 in 10 million).

    If I look at more projects, the results are more varied, but it looks
    like more than half of them use goto. Certainly not as few as 1 in 10.
    So I don't think it's even a majority that avoid 'goto', and it's
    unlikely to be a 'vast' majority.

    There's more to it than that: an individual programmer will have worked
    on multiple projects. Some may have used goto and some not, if examined.
    But you claimed that nearly all have /never/ used it. That is not
    practical to test for.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Sat May 23 05:49:18 2026
    From Newsgroup: comp.lang.c

    On 2026-05-22 10:49, David Brown wrote:
    [snip for brevity]

    All that you (and James downthread) wrote about namespaces I
    already knew (and acknowledge) from your earlier posts.

    My conclusions and suggestion are, for the already explained
    reasons, still the same. - So our opinions on that obviously
    differ. Never mind.

    Janis

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Fri May 22 21:38:16 2026
    From Newsgroup: comp.lang.c

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

    gcc by default compiles GNU C, not ISO C. It also fails to emit
    many language-required diagnostics. GNU C is ISO C with gcc
    extensions. Prior to C23, allowing "L:}" is a documented gcc
    extension.

    I seem to remember that sometime in the past gcc behaved in this
    way. And I see that current documentation on the gcc website says
    something to that effect. Trying it just now, however, I couldn't
    get gcc to accept "L:}" under any circumstances, no matter which
    combination of options was used. The unavailability doesn't bother
    me; I just thought the observation was interesting and worth
    reporting.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Sat May 23 07:33:53 2026
    From Newsgroup: comp.lang.c

    On 2026-05-22 16:57, Dan Cross wrote:
    In article <10upivp$1fkbh$1@dont-email.me>, Bart <bc@freeuk.com> wrote: [...]

    In fact, I find the ternary operator to be used rather
    sparingly, even in the kind of code you posted above.

    I haven't got that impression.

    Some piece of software (it's just a random example, to be clear)
    that has
    130 C-files
    250249 total LOC
    shows
    3864 lines with ?: appearing
    I think this is a lot!

    (I haven't inspected their application-types or done any ad hoc
    classification; these are just [huge] raw numbers. If interested
    in details the sample grep-output is currently available through http://volatile.gridbug.de/ternaries.txt .)

    [...]

    (I can do it with zero: (cond | L1 | L2); the 'goto' can be implied.)

    Yikes.

    I don't know what Bart refers to, but the sample is obviously a
    valid construct in Algol 68 - I think Bart has borrowed quite
    some Algol 68 concepts - where you have conditionals like either

    IF cond THEN val_1 ELSE val_2 FI

    or its terse alternative form

    ( cond | val_1 | val_2 )

    where, due to its orthogonality, it's obviously possible to allow
    all sorts of first class objects to be provided as values.

    There's also a (non-boolean) numeric case variant that may be used
    also with labels, as in its abbreviated form of

    ( value | val_1 , val_2 , val_3 , val_4 | val_else );

    I have to admit that I haven't checked the standard whether labels
    are actually first class objects in Algol 68 - I rarely use jumps -
    but typically you can put all types of first class items' values
    (including procedures and labels) as "values", where ever "values"
    are expected.

    The orthogonal concept I consider to be worthwhile, its long and
    short variants may satisfy personal preferences and various code
    contexts, and you can have the same conditionals also as "lvalues"
    not only as ("rvalue") expressions.

    I don't understand what makes you "Yikes." when hearing about such
    language options. - I mean beyond 'goto' being generally considered
    an inferior concept.

    If it's the "'goto' can be implied" you should know that in Algol 68
    it's equivalent to say either of
    GOTO stop
    GO TO stop
    stop
    These are semantically equivalent forms; you don't need that indecent
    'goto' keyword. IF n = 0 THEN stop FI or ( n = 0 | stop ) are
    both fairly clear forms (without an explicit GOTO).

    Janis

    [...]

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Sat May 23 07:51:49 2026
    From Newsgroup: comp.lang.c

    On 2026-05-22 18:53, Bart wrote:
    On 22/05/2026 15:57, Dan Cross wrote:
    In article <10upivp$1fkbh$1@dont-email.me>, Bart  <bc@freeuk.com> wrote:
    [...]

    (I can do it with zero: (cond | L1 | L2); the 'goto' can be implied.)

    Yikes.

    Blame Algol68, that's where implied goto comes from.

    No; since you said "I can do it ..." it was obviously _your decision_
    to borrow it for "your language(s)". So only *you* are to be accounted
    for *your* language implementation. - Stand by it, coward!

    You seem to like proudly pointing out how your languages work, and if
    there's criticism you blame the original source where the ideas stem
    from. - Disgusting moves; yikes!

    I think Algol 68's redundant/optional 'goto' keyword is good. (I made
    a statement and gave examples in a recent post.)

    Janis

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Sat May 23 07:56:37 2026
    From Newsgroup: comp.lang.c

    On 2026-05-22 14:13, Dan Cross wrote:
    In article <10upbvq$1dhq4$1@dont-email.me>, Bart <bc@freeuk.com> wrote:
    [...]

    In my syntax, it can do this:

    goto (c | L1, L2, L3 | Lx)

    It's sweeter /because/ label names live in the normal name space. I'm
    sure A68 can do this too, but I couldn't get it to work.

    You are right that you can do that in Algol 68. - In Algol 68 it's just

    (c | L1, L2, L3 | Lx)


    Janis

    [...]

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Fri May 22 23:17:06 2026
    From Newsgroup: comp.lang.c

    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    gcc by default compiles GNU C, not ISO C. It also fails to emit
    many language-required diagnostics. GNU C is ISO C with gcc
    extensions. Prior to C23, allowing "L:}" is a documented gcc
    extension.

    I seem to remember that sometime in the past gcc behaved in this
    way. And I see that current documentation on the gcc website says
    something to that effect. Trying it just now, however, I couldn't
    get gcc to accept "L:}" under any circumstances, no matter which
    combination of options was used. The unavailability doesn't bother
    me; I just thought the observation was interesting and worth
    reporting.

    Apparently this extension was introduced in gcc 11, released in 2021.
    It also allows labels on declarations.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Fri May 22 23:44:02 2026
    From Newsgroup: comp.lang.c

    scott@slp53.sl.home (Scott Lurndal) writes:

    I often declare local variables within a compound statement
    introduced by a do/while/for/case construct.

    I do this too, although I don't know if I would say often.
    Probably closer to 50/50. Also the same applies to if().

    I commonly use the declaration syntax in for() statements to have
    an iteration variable (or variables) local to the loop.

    In pre or post C99 code, I do not create compound statements
    willy-nilly just to hide a prior defined local variable with the
    same name (a maintenance nightmare, to be sure) or to locate the
    declaration closer to the use in a function.

    I agree with both of these. Very rarely I might introduce a
    compound statement "out of the blue" not for either those reasons
    but expressly to limit the scope of a variable in the midst of a
    long and complicated function. Especially because such cases are
    rare, I like to flag them with double braces, something like

    {{ ULL carry = 0;
    for(...){
    ... uses of carry
    ... uses of carry
    }
    if(...) uses of carry
    if(...)
    }}

    (and yes, in case anyone is wondering, there are reasons why the
    code in this case couldn't be abstracted into a sub-function).
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Sat May 23 00:04:51 2026
    From Newsgroup: comp.lang.c

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

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

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

    gcc by default compiles GNU C, not ISO C. It also fails to emit
    many language-required diagnostics. GNU C is ISO C with gcc
    extensions. Prior to C23, allowing "L:}" is a documented gcc
    extension.

    I seem to remember that sometime in the past gcc behaved in this
    way. And I see that current documentation on the gcc website says
    something to that effect. Trying it just now, however, I couldn't
    get gcc to accept "L:}" under any circumstances, no matter which
    combination of options was used. The unavailability doesn't bother
    me; I just thought the observation was interesting and worth
    reporting.

    Apparently this extension was introduced in gcc 11, released in 2021.
    It also allows labels on declarations.

    That's good to know, but it doesn't help my (probably incorrect)
    memory telling me I observed it working sometime in the distant
    past. In any case thank you for the reportage.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Sat May 23 09:21:22 2026
    From Newsgroup: comp.lang.c

    On 2026-05-22 12:18, Bart wrote:
    [...]
    and I asked why this works:

       fred(9999);

       PROC fred = (INT n)VOID: print((n, newline));

       print("end")

    I speculated it was the same bug. But it still works with 3.12.2. So
    this case, it is possible to call 'fred' even though it has not yet been defined.

    Concerning procedures I just want to give you this example:

    PROC fred = (INT n) VOID: ( magic | barney (1) );
    PROC barney = (INT n) VOID: ( magic | fred (2) );

    How do you think mutual reference can work if the declarations of
    procedures (or their names) are not visible in the whole context?

    Chapter 5.4. of the Genie documentation might answer your question.

    Otherwise I'd suggest to send an inquiry (or bug report) to Marcel.

    Generally I suggest to read some tutorial or textbook thoroughly
    and from start instead of randomly doing behavioral analysis of
    pieces of Algol 68 code (or what you think is Algol 68). - This
    place is not for discussing subtle details of Algol 68 or Genie.

    Janis

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Sat May 23 09:35:58 2026
    From Newsgroup: comp.lang.c

    On 2026-05-23 08:17, Keith Thompson wrote:
    [...]
    Apparently this extension was introduced in gcc 11, released in 2021.

    It also allows labels on declarations.

    Also in plain (i.e. uninitialized) declarations?

    Is there some application case where that's useful?
    (Or just making the formulation of a rule simpler?)

    Janis

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Sat May 23 11:46:13 2026
    From Newsgroup: comp.lang.c

    On 22/05/2026 18:35, Bart wrote:
    On 22/05/2026 16:47, David Brown wrote:
    On 22/05/2026 17:16, Bart wrote:
    On 22/05/2026 15:04, Scott Lurndal wrote:
    Bart <bc@freeuk.com> writes:
    On 22/05/2026 01:24, Keith Thompson wrote:

    <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2496.pdf>

    This is very interesting. So perhaps tens of millions of C programmers >>>>> encountered the quirk, found it annoying, and moved on.

    The vast majority of C programmers have likely never used goto.

    Source? Oh, 'likely', so it is just a random guess.

    In any case the issue (labels at the end of a compound statement)
    applied also to case labels and to 'default:'.

    If a programmer wants a case label or default label at the end of a
    switch, doing nothing, then (prior to C23, and excluding compiler
    extensions) they have to put in an empty statement - "default : ;".
    (Or they could use "break;" to make code feel more symmetrical.)  I
    can imagine that happening, and I can imagine that is the most likely
    situation where you would want a label right before an end brace.
    This seems to be the main motivation for the change in C23.

    It is much harder to imagine that this "bites" anyone, or even annoys
    people in any significant way.

    And yet, the link at the top was about somebody proposing to fix this
    very thing. And it was accepted.


    We all know that "L:;}" is used sometimes in code (typically with case
    or default labels). One day, someone who happened to use that
    construction regularly, and who was already a highly respected C
    developer and already worked with the C standards committee, took the
    time to figure out what wording would need to be changed in the standard
    to allow "L:}". This was not a change worth putting much effort into,
    but no one had to do much work for it.

    There was no reason whatsoever to ban:

       L:}
       L:int x;

    other than the grammar happening to define labels in a certain way. That there are workarounds is not the point.

    For someone who is so proud of having "designed" several languages, you
    are remarkably ignorant about how languages are designed. Most of the
    time, people decide what they want to /allow/, not what they want to
    ban. And most of the time, the aim is to keep things as simple as
    possible (but no simpler) to be able to do what you want to do. For C,
    up until C23, one form of "statement" is "labelled statement" that looks
    like "label : statement". That's simple and easy to write in the
    standards, simple and easy to use in practice, and simple and easy to
    support in implementations. While I am not privy to the thoughts of
    either Ritchie or early C implementers and influencers, I cannot imagine
    "L:}" was /banned/ - it was simply not supported in the grammar of the language, probably because that would have been an unnecessary
    complication in the descriptions.




    Now you're going to say the vast majority of C programmers have never
    used 'switch' either! 'Probably...'

    I think you might need to switch out your crystal ball - your
    predictions about what people will say or do have rarely come close to
    reality.  Or, perhaps, you should stop saying such stupid things.

    Scott Lurndal said something stupid and I responded with sarcasm. Yet
    you attack me and not him. Biased at all?

    Scott can answer that if he wants to.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Sat May 23 10:58:34 2026
    From Newsgroup: comp.lang.c

    On 23/05/2026 06:51, Janis Papanagnou wrote:
    On 2026-05-22 18:53, Bart wrote:
    On 22/05/2026 15:57, Dan Cross wrote:
    In article <10upivp$1fkbh$1@dont-email.me>, Bart  <bc@freeuk.com> wrote: >> [...]

    (I can do it with zero: (cond | L1 | L2); the 'goto' can be implied.)

    Yikes.

    Blame Algol68, that's where implied goto comes from.

    No; since you said "I can do it ..." it was obviously _your decision_
    to borrow it for "your language(s)". So only *you* are to be accounted
    for *your* language implementation. - Stand by it, coward!

    You seem to like proudly pointing out how your languages work, and if
    there's criticism you blame the original source where the ideas stem
    from. - Disgusting moves; yikes!

    Yes of course, take the credit when there is any (hardly ever) and pass
    the blame otherwise.

    But here I was trying to highlight bias: some here react strongly to a
    feature if they think I've had anything to do with it; there would be a
    more muted response if it's from some other more highly regarded or more established language.

    But, people have also been scathing of features in C, and even long-established C extensions. Maybe it is just the case that if I think
    well of them, there is an automatic reaction to take the opposite attitude.



    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Sat May 23 11:31:18 2026
    From Newsgroup: comp.lang.c

    On 23/05/2026 10:46, David Brown wrote:
    On 22/05/2026 18:35, Bart wrote:

    And yet, the link at the top was about somebody proposing to fix this
    very thing. And it was accepted.


    We all know that "L:;}" is used sometimes in code (typically with case
    or default labels).  One day, someone who happened to use that
    construction regularly, and who was already a highly respected C
    developer and already worked with the C standards committee,

    Oh, right. So there would have been no point in my doing it. But Keith Thompson had a go a me for not doing that anyway:

    "Somebody (notably not you) took the time to write a proposal and submit
    it to the commmittee, which accepted it."


    There was no reason whatsoever to ban:

        L:}
        L:int x;

    other than the grammar happening to define labels in a certain way.
    That there are workarounds is not the point.

    For someone who is so proud of having "designed" several languages, you
    are remarkably ignorant about how languages are designed.  Most of the time, people decide what they want to /allow/, not what they want to
    ban.  And most of the time, the aim is to keep things as simple as
    possible (but no simpler) to be able to do what you want to do.  For C,
    up until C23, one form of "statement" is "labelled statement" that looks like "label : statement".  That's simple and easy to write in the standards, simple and easy to use in practice, and simple and easy to support in implementations.  While I am not privy to the thoughts of
    either Ritchie or early C implementers and influencers, I cannot imagine "L:}" was /banned/ - it was simply not supported in the grammar of the language, probably because that would have been an unnecessary
    complication in the descriptions.

    So, you're picking up on the word "ban". How would you have worded it?

    For someone who is so proud of having "designed" several languages,

    All of which allow labels at the end of a block or before declarations,
    when the latter could be mixed within executable code.

    Actually, I probably allow labels in too many places, including in the
    middle of expressions, like here (expressed in A68G syntax):

    INT a, b:=2, c:=3, d:=4;

    a := IF b=c THEN fred: c ELSE d FI;
    GOTO fred;

    Declaring the label is not an error with A68G, but trying to jump into
    the expression doesn't work; presumably the scope of 'fred' is limited
    to the block so it just can't see it.

    Jumping out of the expression via GOTO is allowed however.

    My languages allow both. Jumping into the middle of a block is sometimes
    done and can be safe. Into the middle of an expression is less so, so
    should ideally be detected and blocked, probably in a later compiler pass.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Sat May 23 03:59:52 2026
    From Newsgroup: comp.lang.c

    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    On 2026-05-23 08:17, Keith Thompson wrote:
    [...]
    Apparently this extension was introduced in gcc 11, released in 2021.

    It also allows labels on declarations.

    Also in plain (i.e. uninitialized) declarations?

    A declaration without an initializer is a declaration, so yes.
    The thing after a label doesn't have to denote executable code.

    Is there some application case where that's useful?
    (Or just making the formulation of a rule simpler?)

    Both C99 and C23 arguably simplified the syntax of a
    compound-statement and made it more flexible.

    In C90, a compound-statement is a "{" followed by zero or more
    declarations, followed by zero or more statements, followed by a "}".

    In C99 through C23, a compound-statement is a "{", zero or more
    block-items, and a "}".

    In C99 through C17, a block-item is a declaration or a
    statement, allowing declarations and statements to be mixed.
    (A labeled-statement is a kind of statement.)

    In C23, a block-item is a declaration, an unlabeled-statement, or a
    label, so now you have three kinds of items that can be freely mixed.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Sat May 23 13:51:04 2026
    From Newsgroup: comp.lang.c

    On 23/05/2026 12:31, Bart wrote:
    On 23/05/2026 10:46, David Brown wrote:
    On 22/05/2026 18:35, Bart wrote:

    And yet, the link at the top was about somebody proposing to fix this
    very thing. And it was accepted.


    We all know that "L:;}" is used sometimes in code (typically with case
    or default labels).  One day, someone who happened to use that
    construction regularly, and who was already a highly respected C
    developer and already worked with the C standards committee,

    Oh, right. So there would have been no point in my doing it. But Keith Thompson had a go a me for not doing that anyway:

    Do you think Keith, me, and perhaps the rest of the c.l.c. regulars all
    get together in secret meetings to discuss how we are going to attack
    you? Or do you think we are all the same person, with different
    pseudonyms? If not, why do you keep asking people to justify the
    opinions or statements made by others? Or, more often, why do you ask
    people to justify words and sentiments that you put in other peoples'
    mouths? Don't you think it would be more productive to read what people write, think about what they wrote, try to understand it, and perhaps
    ask them if you can't figure out what they are saying?


    "Somebody (notably not you) took the time to write a proposal and submit
    it to the commmittee, which accepted it."


    There was no reason whatsoever to ban:

        L:}
        L:int x;

    other than the grammar happening to define labels in a certain way.
    That there are workarounds is not the point.

    For someone who is so proud of having "designed" several languages,
    you are remarkably ignorant about how languages are designed.  Most of
    the time, people decide what they want to /allow/, not what they want
    to ban.  And most of the time, the aim is to keep things as simple as
    possible (but no simpler) to be able to do what you want to do.  For
    C, up until C23, one form of "statement" is "labelled statement" that
    looks like "label : statement".  That's simple and easy to write in
    the standards, simple and easy to use in practice, and simple and easy
    to support in implementations.  While I am not privy to the thoughts
    of either Ritchie or early C implementers and influencers, I cannot
    imagine "L:}" was /banned/ - it was simply not supported in the
    grammar of the language, probably because that would have been an
    unnecessary complication in the descriptions.

    So, you're picking up on the word "ban". How would you have worded it?

    If you "ban" something, you actively and explicitly choose not to allow it.


    For someone who is so proud of having "designed" several languages,

    All of which ...
    ... are irrelevant.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Sat May 23 13:07:59 2026
    From Newsgroup: comp.lang.c

    On 23/05/2026 12:51, David Brown wrote:
    On 23/05/2026 12:31, Bart wrote:
    On 23/05/2026 10:46, David Brown wrote:
    On 22/05/2026 18:35, Bart wrote:

    And yet, the link at the top was about somebody proposing to fix
    this very thing. And it was accepted.


    We all know that "L:;}" is used sometimes in code (typically with
    case or default labels).  One day, someone who happened to use that
    construction regularly, and who was already a highly respected C
    developer and already worked with the C standards committee,

    Oh, right. So there would have been no point in my doing it. But Keith
    Thompson had a go a me for not doing that anyway:


    Do you think Keith, me, ....

    Irrelevant. That was clearly a snide comment aimed at me.
    KT:
    "Somebody (notably not you) took the time to write a proposal and
    submit it to the commmittee, which accepted it."


    So, you're picking up on the word "ban". How would you have worded it?

    If you "ban" something, you actively and explicitly choose not to allow it.

    So you don't like the word. Maybe it wasn't intentional at the start,
    and maybe neither was doing nothing about it for decades
    (unintentionally of course), but the end result was the same.



    For someone who is so proud of having "designed" several languages,

    All of which ...
    ... are irrelevant.

    My choices of label placement are irrelevant to the choices made in C in
    a paragraph about language design? OK.

    But you managed to squeeze in another snarky comment anyway.

    ALWAYS with the personal attacks in this group.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From fir@profesor.fir@gmail.com to comp.lang.c on Sat May 23 14:35:07 2026
    From Newsgroup: comp.lang.c

    Bart pisze:
    On 23/05/2026 12:51, David Brown wrote:
    On 23/05/2026 12:31, Bart wrote:
    On 23/05/2026 10:46, David Brown wrote:
    On 22/05/2026 18:35, Bart wrote:

    And yet, the link at the top was about somebody proposing to fix
    this very thing. And it was accepted.


    We all know that "L:;}" is used sometimes in code (typically with
    case or default labels).  One day, someone who happened to use that
    construction regularly, and who was already a highly respected C
    developer and already worked with the C standards committee,

    Oh, right. So there would have been no point in my doing it. But
    Keith Thompson had a go a me for not doing that anyway:


    Do you think Keith, me, ....

    Irrelevant. That was clearly a snide comment aimed at me.
    KT:
    "Somebody (notably not you) took the time to write a proposal and
    submit it to the commmittee, which accepted it."


    So, you're picking up on the word "ban". How would you have worded it?

    If you "ban" something, you actively and explicitly choose not to
    allow it.

    So you don't like the word. Maybe it wasn't intentional at the start,
    and maybe neither was doing nothing about it for decades
    (unintentionally of course), but the end result was the same.



    For someone who is so proud of having "designed" several languages,

    All of which ...
    ... are irrelevant.

    My choices of label placement are irrelevant to the choices made in C in
    a paragraph about language design? OK.

    But you managed to squeeze in another snarky comment anyway.

    ALWAYS with the personal attacks in this group.

    im not reading into that threads (it would need a very big focus not to
    get lost in that large branches of that trees) but i may say i find you
    and mclean as the most sense users of comp.lang.c - not that i hate this
    so called 'regulars' (im not - if the regulars would be not present the
    group would be empty and it would bo no place to post at all ;c
    esides thay talke on topic (thoug i would say only on part of it as they
    say most interesting part of topics too)
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From fir@profesor.fir@gmail.com to comp.lang.c on Sat May 23 14:38:22 2026
    From Newsgroup: comp.lang.c

    fir pisze:
    esides thay talke on topic (thoug i would say only on part of it as they
    say most interesting part of topics too)

    errata:
    "besides they talk on topic (though, i would say, only on part of it, as
    they "skip" (left not risen) most interesting part of topics, too)
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Sat May 23 13:55:13 2026
    From Newsgroup: comp.lang.c

    On 23/05/2026 11:31, Bart wrote:
    On 23/05/2026 10:46, David Brown wrote:

    For someone who is so proud of having "designed" several languages,

    All of which allow labels at the end of a block or before declarations,
    when the latter could be mixed within executable code.

    Actually, I probably allow labels in too many places, including in the middle of expressions, like here (expressed in A68G syntax):

      INT a, b:=2, c:=3, d:=4;

      a := IF b=c THEN fred: c ELSE d FI;
      GOTO fred;

    Declaring the label is not an error with A68G, but trying to jump into
    the expression doesn't work; presumably the scope of 'fred' is limited
    to the block so it just can't see it.

    Jumping out of the expression via GOTO is allowed however.

    My languages allow both. Jumping into the middle of a block is sometimes done and can be safe. Into the middle of an expression is less so, so
    should ideally be detected and blocked, probably in a later compiler pass.

    I tried gnu C, which has statement expressions. That also lets you have
    labels inside expressions.

    Jumping into an expression from outside is not allowed (for a different
    reason than Algol68), but jumping out of it is.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.lang.c on Sat May 23 15:30:51 2026
    From Newsgroup: comp.lang.c

    In article <10uqamg$1nrsr$1@dont-email.me>, Bart <bc@freeuk.com> wrote:
    On 22/05/2026 18:32, Dan Cross wrote:
    In article <10upkur$1fkbh$2@dont-email.me>, Bart <bc@freeuk.com> wrote:

    [snip]

    Well, Bart, you'll be pleased to know that I was able to
    replicate your results.

    I compiled your `tinypas.c` on a slightly older machine (16 core
    AMD Ryzen 9 5950X; 64 GiB of RAM; otherwise unloaded) running
    Arch Linux. I built with `clang -Os` and ran your recursive
    fibonacci program. The version using computed gotos was
    consistently about 60% faster. Absolute runtimes were about .3
    seconds of user time for the computed goto version, and about
    .47 to .5 seconds for the switch-based version.

    Furthermore, you appear to be correct about branch overhead, as
    well. Using the `perf stat` tool, from the `computed goto`
    version (slightly reformatted for posting):

    343.56 msec task-clock:u
    1,175,236 branch-misses:u # 0.3% branch_miss_rate
    372,329,571 branches:u # 1083.7 M/sec branch_frequency
    1,458,602,759 cpu-cycles:u
    3,562,568,240 instructions:u # 2.4 instructions insn_per_cycle
    190,845,228 stalled-cycles-frontend:u

    0.344303755 seconds time elapsed
    0.340037000 seconds user
    0.001000000 seconds sys

    Whereas for the `switch`-based version:

    486.68 msec task-clock:u
    6,387,425 branch-misses:u # 0.6 % branch_miss_rate
    1,149,861,784 branches:u # 2362.7 M/sec branch_frequency
    2,071,031,603 cpu-cycles:u
    5,271,732,241 instructions:u # 2.5 instructions insn_per_cycle
    152,508,399 stalled-cycles-frontend:u

    0.487171235 seconds time elapsed
    0.481526000 seconds user
    0.001000000 seconds sys

    Second, how was it compiled? What optimization settings? What
    compiler? Did you test different compilers to see if they did
    things differently? You've made a broad general assertion about
    the code generated in response to a `switch` statement; my own
    experience is that that varies widely based on compiler, target
    architecture, and optimization settings.

    It will depend on the task and spread of workload. If the program being
    run is spending a lot of time in libraries, then bytecode dispatch is
    less of the overall overhead.

    That wasn't what I meant. The point was that the generated code
    from the compiler will vary widely.

    Here is a post from a person who noted a much _smaller_
    performance difference between a switch-based loop and computed
    gotos in the cpython interpreter, after seeing a regression due
    to clang-19: https://blog.nelhage.com/post/cpython-tail-call/

    This asserts that modern compilers are capable of generating
    code for a `switch` that is essentially the same as that
    manually generated with computed gotos. I don't know why this
    doesn't seem to be the case with your code.

    Finally, your assertion was that the version using your multiple
    dispatch is faster due to branch predicition: you've shown that
    this is faster, but you haven't shown _why_, let alone that it
    is due to branch prediction. Since you're running this on
    x86_64, did you try to look at the machine's perf counters to
    see what's actually happening?

    It's a well-known technique. It was used on CPython (as compiled for
    Linux, since on Windows it doesn't use gcc), and may still be. Athough
    there are some experients to move towards complex threaded code via TCO >instead.

    In any case, it seems to work, so who cares why?

    This is actually the main point I was trying to make. You
    asserted a specific behavior, but without data to back it up. I
    found the claim dubious, in part because of the compiler
    optimizations mentioned above. In this case, you were correct;
    but understanding why is important.

    Switching between the two means changing 'doswitch' to 'doswitchu'.

    In C switching would require a rewrite, unless you write it in a
    contrived manner. But then it still needs that bunch of macros shown
    below, and it needs that table of label pointers to be manually maintained. >>>
    [snip]

    I'm not sure what you mean here. You've given examples of
    timings using computed gotos and `switch` in C; it's unclear
    what would "require a rewrite" since you appear to already have
    both versions.

    You know what normal switch looks like:

    while (!stopped) {
    switch (opcode) {
    case ADD:
    ...

    Computed goto would first need a jumptable:

    void* jumptable[] = {&&ADDLAB, ....};

    Then dispatch at every point:

    goto *jumptable[opcode];
    ADDLAB:
    ....
    goto *jumptable[opcode]

    It is quite different. In Tinypas, it wraps it up in macros so that the
    same body of the 'switch' can be used for both choices.

    I still don't understand what you mean by "rewrite": it appeared
    you were referring to work that had yet to be done, but the work
    was done.

    Anyway, it doesn't much matter. Your analysis was correct here.

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Sat May 23 17:59:57 2026
    From Newsgroup: comp.lang.c

    On 23/05/2026 16:30, Dan Cross wrote:
    In article <10uqamg$1nrsr$1@dont-email.me>, Bart <bc@freeuk.com> wrote:
    On 22/05/2026 18:32, Dan Cross wrote:
    In article <10upkur$1fkbh$2@dont-email.me>, Bart <bc@freeuk.com> wrote:

    [snip]

    Well, Bart, you'll be pleased to know that I was able to
    replicate your results.

    OK, that's good. (Secretly thinking, phew!)

    Here is a post from a person who noted a much _smaller_
    performance difference between a switch-based loop and computed
    gotos in the cpython interpreter, after seeing a regression due
    to clang-19: https://blog.nelhage.com/post/cpython-tail-call/

    I think that was explained due to LLVM combining the multiple dispatch
    points into one, defeating the purpose of the technique.


    This asserts that modern compilers are capable of generating
    code for a `switch` that is essentially the same as that
    manually generated with computed gotos. I don't know why this
    doesn't seem to be the case with your code.

    It says that it needs Clang 18, or 19 with appropriate flags. So will
    Clang 18 just do it anyway? That sounds undesirable; individual dispatch points generates more code. (You may want to look at your -Os option).

    I wasn't aware C compilers could do this (of course it would have to be
    a switch within loop). And for a long time they didn't, and some still
    don't, that's the reason for the computed goto method.

    (The actual change in the compiler is simple, and that is what I do in
    my language. But I control it per-switch rather than across the program
    for the reasons above. I start with a 'doswitch' keyword so it already
    knows it loops. The keywords used to control it are:

    doswitch # normal looping switch with single dispatch
    doswitchu # multiple dispatch
    doswitchx # multiple dispatch without indexing

    The 'u' in 'doswitchu' was originally because only range-checking was disabled, but that made little difference. I may change the name.

    The 'doswitchx' goes a little further than gnu C's computed gotos. There
    the dispatch code looks like this:

    goto *jumptable[pc.opcode]

    There's a table lookup. By doing a fixup pass, the labels can be
    directly written into the bytecode program. Then the dispatch code can
    look like this:

    goto *pc.labaddr

    It's a few % faster.)

    Regarding computed goto (either explict, or via compiler option) and TCO methods, I wouldn't expect the latter to be by itself much faster.

    AIUI, the benefits are that with TCO each bytecode handler has its own function, which is easier to optimise than one giant function with a big switch statement or computed goto labels.


    I still don't understand what you mean by "rewrite": it appeared
    you were referring to work that had yet to be done, but the work
    was done.

    Yes, somebody did that.

    In general, you start by writing a switch statement. If later you decide
    to use computed goto, it is massively different. Or you can make it
    worth with both via macro tricks as used by tinypas.c.

    Now apparently there's a compiler option, if using Clang, to enable
    multiple dispatch points.

    Note that with tinypas.c in 'switch' mode, it uses:

    start:
    switch (...)
    CASE ADD: ... NEXT

    'NEXT' is 'goto start', so it is not obvious that this is a looping
    switch. (You might want to use 'break' and wrap 'while(1){} around the
    switch in case this is the problem. I don't have a fully working Clang.)


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From makendo@makendo@makendo.invalid to comp.lang.c on Sun May 24 01:14:35 2026
    From Newsgroup: comp.lang.c

    Back in those days, languages that needed multipass compilers (e.g.
    Algol 68) were considered complicated and expensive to implement.
    That’s why C went for a single-pass language design, like Pascal. And
    like Pascal, it has forward declarations to mitigate this somewhat.

    You need some kind of use-before-define facility in any realistic
    language, if you want to allow recursion, and in particular mutual
    recursion.

    It’s amusing to think that C++, that behemoth that, in terms of sheer complexity, leaves old-style monsters like Algol 68 or PL/I in the
    dust, is still essentially a single-pass language design.

    Nobody really cares about whatever shit multipass is now -- you've got gigabytes of main memory to work with, and you can always store a
    yet-to-be defined label in a hash table if you are reinventing your
    own assembler, as a simple example. Multipass is for when you have
    only kilobits of core memory, where the only way to realistically
    compile your code is to dump intermediate results (in the assembler
    case, memory address of labels) on a magnetic tape.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.lang.c on Sun May 24 02:48:25 2026
    From Newsgroup: comp.lang.c

    In article <10ure81$1s756$1@dont-email.me>,
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 2026-05-22 16:57, Dan Cross wrote:
    In article <10upivp$1fkbh$1@dont-email.me>, Bart <bc@freeuk.com> wrote:
    [...]

    In fact, I find the ternary operator to be used rather
    sparingly, even in the kind of code you posted above.

    I haven't got that impression.

    Some piece of software (it's just a random example, to be clear)
    that has
    130 C-files
    250249 total LOC
    shows
    3864 lines with ?: appearing
    I think this is a lot!

    That's about 1.5% of source lines. I'd say that's pretty
    sparingly used.

    (I haven't inspected their application-types or done any ad hoc >classification; these are just [huge] raw numbers. If interested
    in details the sample grep-output is currently available through >http://volatile.gridbug.de/ternaries.txt .)

    I'm not terribly. I never said it wasn't used; just used
    sparingly. That's not a precise unit of measure, unfortunately;
    perhaps a better way to put it would have been that, compared to
    `if` or even `if (foo) bar = 1; else bar = 2;` the ternary
    operator is used much less frequently.

    [snip]
    If it's the "'goto' can be implied" you should know that in Algol 68
    it's equivalent to say either of
    GOTO stop
    GO TO stop
    stop
    These are semantically equivalent forms; you don't need that indecent
    'goto' keyword. IF n = 0 THEN stop FI or ( n = 0 | stop ) are
    both fairly clear forms (without an explicit GOTO).

    Careful or you're going to make me say "yikes" again. I think
    these are awful design decisions. Wirth had strong opinions
    about Algol 68; I can see why.

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Sat May 23 14:21:14 2026
    From Newsgroup: comp.lang.c

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

    Bart <bc@freeuk.com> writes:

    On 21/05/2026 07:45, David Brown wrote:

    [...]

    So do you have an example of where code has been written to take
    advantage of the separate name spaces?

    No. This is why I claimed it was pointless.

    This entire discussion is pointless. [...]

    I was hoping you would stop there. Your point would have
    been made much more effectively.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Sat May 23 20:32:39 2026
    From Newsgroup: comp.lang.c

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

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

    Bart <bc@freeuk.com> writes:

    On 21/05/2026 07:45, David Brown wrote:

    [...]

    So do you have an example of where code has been written to take
    advantage of the separate name spaces?

    No. This is why I claimed it was pointless.

    This entire discussion is pointless. [...]

    I was hoping you would stop there. Your point would have
    been made much more effectively.

    Sorry for the duplicates. News server hiccups.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Sat May 23 20:46:31 2026
    From Newsgroup: comp.lang.c

    Bart <bc@freeuk.com> writes:
    On 23/05/2026 12:51, David Brown wrote:
    [...]
    Do you think Keith, me, ....

    Irrelevant. That was clearly a snide comment aimed at me.
    KT:
    "Somebody (notably not you) took the time to write a proposal and
    submit it to the commmittee, which accepted it."

    Yes, it was a snide comment aimed at you. Not part of a conspiracy
    or any biased attempt to reject ideas just because they're yours,
    just a single snide comment.

    [...]
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Sat May 23 21:12:35 2026
    From Newsgroup: comp.lang.c

    cross@spitfire.i.gajendra.net (Dan Cross) writes:

    In article <10ujm3r$3pnbb$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:

    [snip] I have almost never had need of "goto" or labels (excluding
    switch case labels, of course), and don't expect ever to do so in the
    future.

    While I generally try to avoid it, there are times (in C in
    particular) when it really is the right tool; [...]

    Yes.

    Breaking out of nested loops without a lot of unnecessary
    ceremony is sort of an obvious example; [...]

    Another scenario where using goto can be attractive is when
    so-called "loop and a half" processing is needed. Something
    like this

    goto ENTRY;
    do {
    ....
    ENTRY:
    ....
    } while( ..condition.. );

    is in many cases more attractive than goto-less alternatives.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Sat May 23 21:31:22 2026
    From Newsgroup: comp.lang.c

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

    cross@spitfire.i.gajendra.net (Dan Cross) writes:

    In article <10ujm3r$3pnbb$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:

    [snip] I have almost never had need of "goto" or labels (excluding
    switch case labels, of course), and don't expect ever to do so in the
    future.

    While I generally try to avoid it, there are times (in C in
    particular) when it really is the right tool; [...]

    Yes.

    Breaking out of nested loops without a lot of unnecessary
    ceremony is sort of an obvious example; [...]

    Another scenario where using goto can be attractive is when
    so-called "loop and a half" processing is needed. Something
    like this

    goto ENTRY;
    do {
    ....
    ENTRY:
    ....
    } while( ..condition.. );

    is in many cases more attractive than goto-less alternatives.

    I should add that, even though there are these and some other
    well-known counterexamples, IME occasions where goto is needed
    are exceedinly rare; under 0.02%, say. My heuristic is, when
    considering possibly using a goto, write two or three versions
    without using goto; then, only if a goto version is better than
    all the others should goto be used.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Sun May 24 10:13:39 2026
    From Newsgroup: comp.lang.c

    On 2026-05-24 04:48, Dan Cross wrote:
    In article <10ure81$1s756$1@dont-email.me>,
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 2026-05-22 16:57, Dan Cross wrote:
    In article <10upivp$1fkbh$1@dont-email.me>, Bart <bc@freeuk.com> wrote: >>> [...]

    In fact, I find the ternary operator to be used rather
    sparingly, even in the kind of code you posted above.

    I haven't got that impression.

    Some piece of software (it's just a random example, to be clear)
    that has
    130 C-files
    250249 total LOC
    shows
    3864 lines with ?: appearing
    I think this is a lot!

    That's about 1.5% of source lines. I'd say that's pretty
    sparingly used.

    (I haven't inspected their application-types or done any ad hoc
    classification; these are just [huge] raw numbers. If interested
    in details the sample grep-output is currently available through
    http://volatile.gridbug.de/ternaries.txt .)

    I'm not terribly. I never said it wasn't used; just used
    sparingly. That's not a precise unit of measure, unfortunately;

    (As you I also wasn't "precise" with my wording, deliberately;
    it's hard to be precise with such things without doing thorough
    statistics on huge code bases and analysis of application cases,
    common code patterns, and socialized personal preferences.)

    But for that size of code I think it's extremely commonly used
    for these sorts of _expressions_. (Mind that you are not allowed
    to use 'if' in expressions.) It's not that you have to seek for
    these with a magnifying glass; you open a file and immediately
    see such code patterns (in the appropriate places, of course).

    perhaps a better way to put it would have been that, compared to
    `if` or even `if (foo) bar = 1; else bar = 2;` the ternary
    operator is used much less frequently.

    The "C" language is, as other languages, not only about 'if' and
    ternary conditionals; given that your "1.5%" number is comparably
    huge.

    That sample source had ~7 times as many 'if' as those ternaries.
    But that also isn't surprising, as ternaries, in "C", can only be
    used in expressions (as opposed to, say, conditionals in Algol 68
    for example).

    The point is, if you're replacing all those ternaries by explicit
    if-constructs you'd blow up the source code, and often make it,
    because of the syntactic bloat - especially in "C" syntax - less
    readable by that.[*] For many/most of those cases in the sample
    code the corresponding 'if' would make the code much worse, IMO.


    [snip]
    If it's the "'goto' can be implied" you should know that in Algol 68
    it's equivalent to say either of
    GOTO stop
    GO TO stop
    stop
    These are semantically equivalent forms; you don't need that indecent
    'goto' keyword. IF n = 0 THEN stop FI or ( n = 0 | stop ) are
    both fairly clear forms (without an explicit GOTO).

    Careful or you're going to make me say "yikes" again.

    (You may say what you like. - But I wonder why, given that an
    explicit 'goto' keyword is just redundant and doesn't serve any
    purpose; unless one is thinking only in pre-1968 mindsets...

    I think
    these are awful design decisions. Wirth had strong opinions
    about Algol 68; I can see why.

    ...and an unspecific reference is also not helpful in any way.)

    You are unspecifically generalizing/extrapolation/inferring here.
    (Not sure about the most appropriate word - most likely all are
    fitting concerning your previous statement; hope you understand
    what I'm meaning here and also by what I'm expanding on below.)

    Back these days there were quite many, and well known CS experts
    (besides Wirth also Hoare and Knuth), who had other ideas about
    how a new Algol language should look like.[**] But their reasoning
    where differing;[***] Wirth didn't want a large powerful language,
    but a simpler small one, usable for education/teaching.[****] Also
    the problem of implementing a powerful large (in some aspects even
    complex) language was an issue, considering the difficulty that
    implementers might have. As far as I recall, mainly Algol's size
    (and perceived complexity, specifically the 2-level W-grammar) was
    primary source of disagreement.

    It's hard to believe that profane details like the optionality of
    a 'goto' statement would have been a crucial reason of dissent.

    The existence of the 'goto' in Algol 68 to me looks just like a
    concession to all those that were used to that keyword from prior
    existing low-level languages, for programmers who want imperative
    sounding code, and who maybe need, when programming, a feeling of
    wading through the mud of a BASIC-like type of code organization.
    I can explain the liking of an explicit 'goto' (or disliking its
    optionality despite its redundancy) only by specific socialization.

    But if you like a mandatory 'goto' I'm fine with that. No argument
    on opinions and personal preferences.

    Janis


    - Dan C.


    [*] Some time ago I gave examples where it makes, beyond preferences,
    sense to even mix (in Algol 68) its if-conditionals and the shortcut
    'if' variant, ( | | ), (that resembles C's ternary) in expressions.

    [**] Dissent is an effect of strong characters with strong opinions.

    [***] "But their objections were diverse." [Lindsay]

    [****] But he obviously completely misjudged Algol 68's design fitting
    for educational purposes; universities did use Algol 68 for teaching
    purpose. Algol's coherent design was actually a strong argument to use
    it for educational purposes.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Sun May 24 11:00:13 2026
    From Newsgroup: comp.lang.c

    On 2026-05-24 06:12, Tim Rentsch wrote:
    [...]

    Another scenario where using goto can be attractive is when
    so-called "loop and a half" processing is needed. Something
    like this

    goto ENTRY;
    do {
    ....
    ENTRY:
    ....
    } while( ..condition.. );

    is in many cases more attractive than goto-less alternatives.

    Yes, a (low-level) code-pattern that works well in "C".

    May there be issues if you have declarations in the loop?

    Other languages forbid that to not encounter inherent
    problems when trying to enter inner scopes. - Just noting.


    I think in "C" I'd prefer instead of

    goto entry; do { A; entry: B; } while (condition);

    for several reasons rather

    while (1) { B; if (!condition) break; A; }

    despite of the additional outer true-condition.

    Janis

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Sun May 24 04:15:00 2026
    From Newsgroup: comp.lang.c

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

    cross@spitfire.i.gajendra.net (Dan Cross) writes:

    In article <10ujm3r$3pnbb$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:

    [snip] I have almost never had need of "goto" or labels (excluding
    switch case labels, of course), and don't expect ever to do so in the
    future.

    While I generally try to avoid it, there are times (in C in
    particular) when it really is the right tool; [...]

    Yes.

    Breaking out of nested loops without a lot of unnecessary
    ceremony is sort of an obvious example; [...]

    Another scenario where using goto can be attractive is when
    so-called "loop and a half" processing is needed. Something
    like this

    goto ENTRY;
    do {
    ....
    ENTRY:
    ....
    } while( ..condition.. );

    is in many cases more attractive than goto-less alternatives.

    Another log on the goto fire...

    The code below is a transcription of Algorithm A in section 6.2.3
    of Knuth TAOCP, for insertion into an AVL tree. It uses the same
    variable names as in the original, except not capitalized.

    I think it's fair to call this algorithm a candidate poster child
    for showing abuse of goto. Ick.

    It makes a good challenge problem: provide a more comprehensible
    re-write, especially with respect to reducing use of goto.


    #include <assert.h>
    #include <stdlib.h>

    typedef struct tree_node_s *Tree;

    typedef signed int Key;
    typedef signed int Balance;

    struct tree_node_s {
    Tree left, right;
    Key key;
    Balance b;
    };


    void
    avl_insert( Tree head, Key k ){
    Tree t, s, p, q, r;
    Balance a;

    A1:
    t = head, s = p = head->right;
    /* fall through */

    A2:
    if( k < p->key ) goto A3;
    if( k > p->key ) goto A4;
    return;

    A3:
    q = p->left;
    if( !q ){
    left = q = malloc( sizeof *q );
    goto A5;
    }
    if( q->b != 0 ) t = p, s = q;
    p = q;
    goto A2;

    A4:
    q = p->right;
    if( !q ){
    right = q = malloc( sizeof *q );
    goto A5;
    }
    if( q->b != 0 ) t = p, s = q;
    p = q;
    goto A2;

    A5:
    q->left = q->right = 0, q->key = k, q->b = 0;
    /* fall through */

    A6:
    if( k < s->key ) r = p = s->left;
    else r = p = s->right;
    while( p != q ){
    if( k < p->key ) p->b = -1, p = p->left;
    else p->b = +1, p = p->right;
    }
    /* fall through */

    A7:
    if( k < s->key ) a = -1;
    else a = +1;
    if( s->b == 0 ){
    b = a;
    return;
    }
    if( s->b == -a ){
    b = 0;
    return;
    }
    if( s->b == a ){
    if( r->b == a ) goto A8;
    if( r->b == -a ) goto A9;
    assert( 0 );
    }
    assert( 0 );

    A8:
    p = r;
    if( a == 1 ) s->right = r->left, r->left = s;
    else s->left = r->right, r->right = s;
    s->b = r->b = 0;
    goto A10;

    A9:
    if( a == 1 ){
    p = r->right;
    right = p->left, p->left = r;
    left = p->right, p->right = s;
    } else {
    p = r->left;
    left = p->right, p->right = r;
    right = p->left, p->left = s;
    }
    if( p->b == a ) s->b = -a, r->b = 0;
    if( p->b == 0 ) s->b = 0, r->b = 0;
    if( p->b == -a ) s->b = 0, r->b = -a;
    p->b = 0;
    /* fall through */

    A10:
    if( s == t->right ) t->right = p;
    else t->left = p;

    }
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Sun May 24 12:30:19 2026
    From Newsgroup: comp.lang.c

    On 24/05/2026 03:48, Dan Cross wrote:
    In article <10ure81$1s756$1@dont-email.me>,
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 2026-05-22 16:57, Dan Cross wrote:
    In article <10upivp$1fkbh$1@dont-email.me>, Bart <bc@freeuk.com> wrote: >>> [...]

    In fact, I find the ternary operator to be used rather
    sparingly, even in the kind of code you posted above.

    I haven't got that impression.

    Some piece of software (it's just a random example, to be clear)
    that has
    130 C-files
    250249 total LOC
    shows
    3864 lines with ?: appearing
    I think this is a lot!

    That's about 1.5% of source lines. I'd say that's pretty
    sparingly used.

    I think if you take just about any feature, you will find it is only
    used in a small percentage of lines.

    But don't just take my opinion, I put it to to the test.

    I took sqlite3.c sources from 2018 (sqlite3.c + shell.c + header, which
    are combined into a 235Kloc source file).

    This was preprocessed down to a 84Kloc file, which would increase the percentages and work againt my argument. Even so, these were the figures
    I got:

    goto 0.75 % (no. of instances as percentage of line count)
    for 1.00
    while 0.36
    funcs 2.31 (no. of function definitions)
    + 2.38
    - minus 1.66
    if 8.73
    do 0.07
    ?: 0.56
    switch 0.79
    case 1.32
    break/sw 0.95
    break 0.35
    const 2.52
    union 0.06
    += 0.38
    ~ 0.32
    != 1.63
    [] 6.03 (includes declarations)
    ; 62.88

    Since that one has an unusual composition, here is another, sources for
    the Tiny C compiler, preprocessed to 28Kloc:

    goto 1.08 %
    for 0.82
    while 0.46
    funcs 1.88
    + 2.27
    - minus 1.00
    if 8.98
    do 0.05
    ?: 2.80
    switch 0.17
    case 2.38
    break/sw 1.18
    break 0.35
    const 2.30
    union 0.19
    += 0.41
    ~ 1.67
    != 1.97
    [] 3.25
    ; 64.55

    There is a reasonable correlation.

    Anyway. it seems half these common C features fall below the threshold
    of what you'd call sparingly used.

    (For me the most interesting one is ";". In my languages, the figure is
    around 0.1%. There must be a reason so many languages get rid of it or
    make it optional.)
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Sun May 24 13:39:52 2026
    From Newsgroup: comp.lang.c

    On 24/05/2026 12:30, Bart wrote:
    On 24/05/2026 03:48, Dan Cross wrote:
    In article <10ure81$1s756$1@dont-email.me>,
    Janis Papanagnou  <janis_papanagnou+ng@hotmail.com> wrote:
    On 2026-05-22 16:57, Dan Cross wrote:
    In article <10upivp$1fkbh$1@dont-email.me>, Bart  <bc@freeuk.com>
    wrote:
    [...]

    In fact, I find the ternary operator to be used rather
    sparingly, even in the kind of code you posted above.

    I haven't got that impression.

    Some piece of software (it's just a random example, to be clear)
    that has
          130  C-files
       250249  total LOC
    shows
         3864  lines with ?: appearing
    I think this is a lot!

    That's about 1.5% of source lines.  I'd say that's pretty
    sparingly used.

    I think if you take just about any feature, you will find it is only
    used in a small percentage of lines.

    But don't just take my opinion, I put it to to the test.

    I took sqlite3.c sources from 2018 (sqlite3.c + shell.c + header, which
    are combined into a 235Kloc source file).

    This was preprocessed down to a 84Kloc file, which would increase the percentages and work againt my argument. Even so, these were the figures
    I got:

     goto     0.75 % (no. of instances as percentage of line count)
     for      1.00
     while    0.36
     funcs    2.31   (no. of function definitions)
     +        2.38
     - minus  1.66

    I meant negation as in '-a'. I forgot 'minus' can mean both.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.lang.c on Sun May 24 19:08:55 2026
    From Newsgroup: comp.lang.c

    In article <10uprpn$1hfpm$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    On 22/05/2026 16:44, Dan Cross wrote:
    In article <10un0j7$obiv$2@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    [snip]
    There is definitely potential for a language's type system to make it
    harder to make some kinds of mistakes. But there is a risk in making
    the language too restrictive - people end up writing horrible code to
    work around restrictions, or use "unsafe" code too much.

    Interesting. I've found it to be somewhat the opposite; using a
    richer type system has lead to code that is easier to understand
    and reason about, and less buggy: type-oriented programming can
    make entire categories of errors *unrepresentable*, so it's not
    just _harder_ to make certain kinds of mistakes, but
    _impossible_. The existance of an object of some type can be
    thought of as an existence proof that the invariants the type
    represents hold.

    In general, I agree - I prefer strongly typed languages, and it's always >best if an error is identified by the code being uncompilable rather
    than waiting for run-time testing (or testing by evil hackers). But if >people want to do something with the language that is hard to achieve >efficiently within the norms of the language, and there are escape
    hatches ("unsafe" code, inline assembly, calling external C functions,
    etc.) then people will use them.

    Sure; that seems pretty clear, given extensive available
    evidence.

    People do sometimes write C code that
    knowingly depends on undefined behaviour, because it makes their results >more efficient and "it worked when I tested it".

    Yes, or they force some knob on their compiler into the required
    position to give them guaranteed results. Linux does this, for
    example; last time I worked in it, Google's massive (2BLOC) code
    base similarly.

    Type safety - like any kind of safety - can sometimes get in the way of >"getting things done". A balance always needs to be found to ensure
    that people can do what they need to do without breaking rules or using >"escapes" more than absolutely necessary. If people find that much of
    their Rust code is "unsafe", then most of the point of using Rust is
    lost. (Of course this also means that different languages are suited to >different tasks.)

    It _can_, but I wouldn't take it as a given that it _does_, and
    what I'm trying to say is that contrary to often getting in the
    way, it can be used effectively to make programs better and
    safer, with no runtime downside. Indeed, a well-typed program
    can be faster than the alternative, since a) the compiler can
    prove that some properties hold, and b) it can be more
    aggressively optimized at a higher level. An example here are
    non-nullable references; there's no need to check whether they
    are NULL or not before indirecting through them, since by
    definition they cannot be NULL.

    And Alexis King wrote the great, "Parse, Don't Validate" essay
    some years ago where she talks about "Type-Driven Development":
    https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/

    More recently, Yaron Minsky gave a talk and discussed this in an
    OCaml context (https://www.youtube.com/watch?v=rUYP4C29yCw; the
    most relevant bits start at about 6:35. The talk is about AI
    and constraining agents, but the discussion around types is more
    general).

    I wrote the production OS loader for Oxide compute sleds using
    this technique in the virtual memory system (and other places):
    the loader uses multiple page sizes, and the rule is that, when
    mapping a region of memory, it uses the largest page size that
    it can, given size and alignment constraints. But I use the
    type system to make it impossible to, say, map a 2MiB "large"
    physical page frame to a non-2MiB aligned virtual boundary.

    I did some work, eons ago, in a language called XC that was specifically >>> for XMOS microcontrollers. The language and tools had a feature that
    made data races impossible by not allowing competing access to shared
    variables from different threads. Since threads were part of the
    hardware, and the tools analysed the code flow through threads, this
    could all be enforced at build time - data had to be passed in messages, >>> not shared memory. But for some things that involved large buffers,
    that was hopelessly inefficient - and these devices were regularly used
    with USB, audio, and similar things that needed large and predictable
    buffers. So code - even library and example code from the manufacturer
    - was full of inline assembly to work around the "smart-arse" language
    and tools.

    Hmm. This reads less like an indictment of the idea of stronger
    typing, but rather a failure to provide adequate abstractions in
    the type system.

    It is not an indication of problems with stronger typing, but it is an >indication that the restrictions inherent in the language and tools made >them somewhat unsuitable for a lot of use-cases that fitted the hardware >well. (Modern XMOS tools have changed significantly since those days, >perhaps partly because of such issues.)

    Sure, but that's a failure of that system to provide good
    abstractions: the system was not rich enough to provide the
    functionality you needed or wanted.

    I think the central tension here is that the idea of a strong
    type system that is also semantically rich is being conflated
    with one that's highly restrictive. What I'm suggesting is that
    the opposite is true.

    I'm going to mention Rust again; apologies. When confined to
    the safe subset, it _also_ has data race freedom. The rules
    that give this property are:

    1. Every object has exactly one owner, and assignments of
    non-trivial types change ownership (they are logically a
    "move");
    2. References to an object may be "borrowed" from the owner,
    and mutable references (that is, references that may be used
    to write to the object) are distinct from immutable
    references (that is, references that may only be used to read
    from an object);
    3. Mutable and immutable references are temporally mutually
    exclusive: a mutable reference may be borrowed from an object
    iff that is the only live reference to that object at the
    time: that is, it is not permitted to borrow a mut ref to an
    object if either another mut ref or any immutable refs to it
    are live; any number of immutable references may be taken to
    an object concurrently.

    If all of these rules are obeyed (and in safe rust, they're
    verified at compile time by the borrow checker) then you cannot
    have data races.

    That's all good, by the sound of it. But if people find this gets in
    the way of efficiency - perhaps because they know that it is safe to
    hold two separate mutable references to an object for reasons the borrow >checker can't see - the temptation to use "unsafe" or other workarounds >comes quickly.

    Not really. The rule is that borrowing a mutable reference to
    some object is mutually exclusive with borrowing any other kind
    of reference to that same object at the same time. `unsafe`
    doesn't change that rules, or let the programmer off the hook
    for violating it: `unsafe` code isn't allowed to mix mut ref
    with other references any more than _safe_ code. All `unsafe`
    means is that the burden of upholding the rules of the language
    falls to the programmer, without compiler assistence, because
    the programmer knows something that the compiler does not (and
    probably cannot) for some reason.

    However, if it really _is_ the case that multiple simultaneous
    writes are ok, the ability to create new types and assign them
    semantics and behavior menas that a programmer can build a _new_
    abstraction that lets them do something similar. Atomics are
    an interesting case in point: one can store to them using a
    non-mutable reference: while this seems counter-intuitive at
    first, it sort of makes sense for the same reason that a `Mutex`
    is accessed via an immutable reference: one cannot observe an
    atomic in an intermediate state due to an update, and `load` and
    `store` are explicit operations (in practice, the compiler
    lowers those to single load/store operations and whatever
    barriers are appropriate for the target architecture).

    A hardware device modeling a framebuffer might be another case;
    those are output-only devices, and there's no reason that two
    threads cannot write to different parts of the buffer at the
    same time, but given a suitable interface, the interface doesn't
    need to expose unsafety. And if you've got a rich type system,
    you have the rules to build that interface.

    That does not mean that a language should not try to have such rules,
    and enforce them at compile time - far from it. The aim should be that
    as much code as possible is correct, or at least immune to particular >classes of bugs, checked by the compiler and tools.

    I think you're saying something different than what I'm saying;
    my point is that the safe interface shouldn't be seen as a
    burden and, if well-executed, it shouldn't add a runtime tax in
    terms of reduced performance. You seem to be taking it as a
    given that the rules of the language will make both of those
    things true, however.

    At first glance it appears that it must suffer from the same
    drawbacks as `XC`, which you mentioned above. Except that the
    language does provide controlled ways to share data
    concurrently.

    Using the _unsafe_ subset, there is one place where it is
    permitted to have multiple, mutable references to an object: the
    `UnsafeCell`. This gives Rust interior mutability, which means
    that you can build safe abstractions for data sharing, like
    `Mutex` types that own the data they protect.

    If "unsafe" gives enough, but is rarely needed in most code, then it
    sounds like a good balance.

    Yes. And moreover, one can hide that unsafety behind a safe
    interface.

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From tTh@tth@none.invalid to comp.lang.c on Sun May 24 22:09:21 2026
    From Newsgroup: comp.lang.c

    On 5/24/26 13:30, Bart wrote:

    I took sqlite3.c sources from 2018 (sqlite3.c + shell.c + header, which
    are combined into a 235Kloc source file).

    I don't understand your obsession with “combining” different
    sources into a single file. I'm no C expert, but I've been
    coding for 40 years, so let's take an example (simplified here)
    from my real-life experience as a programmer:

    /* here the various classic includes */
    static unsigned count;
    void count_increment()
    {
    count++;
    }
    void count_display()
    {
    printf("count is %ld\n", count);
    }
    /* just a little compil unit :) */

    If you "combine" that _independent_ code in an huge
    single file, and there was more than one compilation
    unit who use a static variable named count, two things
    can happen :
    a) your friend the compiler is barfing.
    b) nasal daemons are not accurate.

    choise you toxicity. I'm very curious...
    --
    ** **
    * tTh des Bourtoulots *
    * http://maison.tth.netlib.re/ *
    ** **
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Bart@bc@freeuk.com to comp.lang.c on Sun May 24 21:48:33 2026
    From Newsgroup: comp.lang.c

    On 24/05/2026 21:09, tTh wrote:
    On 5/24/26 13:30, Bart wrote:

    I took sqlite3.c sources from 2018 (sqlite3.c + shell.c + header,
    which are combined into a 235Kloc source file).

       I don't understand your obsession with “combining” different
       sources into a single file.

    It's not me doing it. sqlite3.c is an amalgamation of over 100 C files
    created by the SQLite developers. It's done to simplify embedding within
    your own applications. That file itself was already 220Kloc.

    'shell.c' is a program which can be used to create a standalone SQLite
    app, and it was convenient to combine it with sqlite2.c for test purposes.

    Other amalgamations I use include Lua 5.4 as one source file, again to
    simply embedding; somebody else did that too, so complain to them: https://github.com/edubart/minilua

    There are actually lots of one-file C and C++ libraries about; some
    might only be one file anyway, some might involve combining. For example stb_image.h.


    I'm no C expert, but I've been
       coding for 40 years, so let's take an example (simplified here)
       from my real-life experience as a programmer:

    /* here the various classic includes */
    static unsigned count;
    void count_increment()
    {
    count++;
    }
    void count_display()
    {
    printf("count is %ld\n", count);
    }
    /* just a little compil unit :) */

       If you "combine" that _independent_ code in an huge
       single file, and there was more than one compilation
       unit who use a static variable named count, two things
       can happen :
          a) your friend the compiler is barfing.
          b) nasal daemons are not accurate.

    Yeah, 'combining' doesn't mean just blindly concatenating a bunch of C
    source files. Various approaches are used; I don't know because I've
    never done it.

    My own single-file C programs are machine-generated by a transpiler,
    from non-C sources in another language, and it will take care of all
    those details.

    So if modules A and B both have a local called 'count', that might be
    renamed to A_count and B_count.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Sun May 24 22:04:52 2026
    From Newsgroup: comp.lang.c

    Bart <bc@freeuk.com> writes:
    On 24/05/2026 21:09, tTh wrote:
    On 5/24/26 13:30, Bart wrote:

    I took sqlite3.c sources from 2018 (sqlite3.c + shell.c + header,
    which are combined into a 235Kloc source file).

       I don't understand your obsession with “combining” different
       sources into a single file.

    It's not me doing it. sqlite3.c is an amalgamation of over 100 C files >created by the SQLite developers. It's done to simplify embedding within >your own applications. That file itself was already 220Kloc.

    It's useful to note that the developers of SQLite work with
    those 100 C files, not with the single amalgamtion.

    sqlite3.c amalgamation is a distribution vehicle for
    people who want the library embedded in their code (rare, as
    most will simply link with their favorite distributions
    sqlite package). Primarily useful for those who need a
    lightweight database in e.g. a firmware image, or small
    kernel. They even offer one using autotools, which Bart
    detests.

    The full source tree archive can be downloaded for those
    building packages in distributions, or wishing to modify
    the code in some fashion.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Mon May 25 10:34:48 2026
    From Newsgroup: comp.lang.c

    On 24/05/2026 22:09, tTh wrote:
    On 5/24/26 13:30, Bart wrote:

    I took sqlite3.c sources from 2018 (sqlite3.c + shell.c + header,
    which are combined into a 235Kloc source file).

       I don't understand your obsession with “combining” different
       sources into a single file. I'm no C expert, but I've been
       coding for 40 years, so let's take an example (simplified here)
       from my real-life experience as a programmer:

    /* here the various classic includes */
    static unsigned count;
    void count_increment()
    {
    count++;
    }
    void count_display()
    {
    printf("count is %ld\n", count);
    }
    /* just a little compil unit :) */

       If you "combine" that _independent_ code in an huge
       single file, and there was more than one compilation
       unit who use a static variable named count, two things
       can happen :
          a) your friend the compiler is barfing.
          b) nasal daemons are not accurate.

       choise you toxicity. I'm very curious...



    Combining files does not mean simply jumbling them all in one big file. (Though it is possible to write code in a way for that to be possible,
    and maybe the sqlite folks have done that.)

    /Very/ roughly speaking, you start by taking all the #include files
    mentioned in your other files. If these are well-written, with guards
    and with their own #include files so that ordering and duplication does
    not matter when used in "normal" C code, it should not be too difficult
    to paste together from these in a single group. Then you take the C
    files, and for every file-scope static declaration, you pre-pend or
    append the filename or other unique value to the name. Thus "static
    unsigned count;" becomes "static unsigned compil_unit_count;". Type definitions (typedef, struct, enum, etc.) will need similar treatment if
    there are multiple conflicting definitions, and de-duplication if there
    are multiple identical definitions.

    If the code is more complicated, with macros being re-defined and such
    like then you might be better off pre-processing all the files before
    trying to join them.

    If code is written with a view to joining to a single distribution file,
    this should all be straightforward and easily automated. If it is
    general code, it's a lot of work. But there are no doubt tools
    available to help.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Mon May 25 12:16:03 2026
    From Newsgroup: comp.lang.c

    On 24/05/2026 21:08, Dan Cross wrote:
    In article <10uprpn$1hfpm$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    On 22/05/2026 16:44, Dan Cross wrote:
    In article <10un0j7$obiv$2@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    [snip]
    There is definitely potential for a language's type system to make it
    harder to make some kinds of mistakes. But there is a risk in making
    the language too restrictive - people end up writing horrible code to
    work around restrictions, or use "unsafe" code too much.

    Interesting. I've found it to be somewhat the opposite; using a
    richer type system has lead to code that is easier to understand
    and reason about, and less buggy: type-oriented programming can
    make entire categories of errors *unrepresentable*, so it's not
    just _harder_ to make certain kinds of mistakes, but
    _impossible_. The existance of an object of some type can be
    thought of as an existence proof that the invariants the type
    represents hold.

    In general, I agree - I prefer strongly typed languages, and it's always
    best if an error is identified by the code being uncompilable rather
    than waiting for run-time testing (or testing by evil hackers). But if
    people want to do something with the language that is hard to achieve
    efficiently within the norms of the language, and there are escape
    hatches ("unsafe" code, inline assembly, calling external C functions,
    etc.) then people will use them.

    Sure; that seems pretty clear, given extensive available
    evidence.

    People do sometimes write C code that
    knowingly depends on undefined behaviour, because it makes their results
    more efficient and "it worked when I tested it".

    Yes, or they force some knob on their compiler into the required
    position to give them guaranteed results. Linux does this, for
    example; last time I worked in it, Google's massive (2BLOC) code
    base similarly.


    What you then get is code that is UB in C, but not UB in C-with-no-strict-aliasing, or whatever "augmented" language you pick.
    (I have a strict policy of putting any such knobs in "GCC optimize"
    pragmas, so that the code correctness does not depend on the flags
    picked in a makefile. Of course such code remains equally limited in
    its portability.)

    Type safety - like any kind of safety - can sometimes get in the way of
    "getting things done". A balance always needs to be found to ensure
    that people can do what they need to do without breaking rules or using
    "escapes" more than absolutely necessary. If people find that much of
    their Rust code is "unsafe", then most of the point of using Rust is
    lost. (Of course this also means that different languages are suited to
    different tasks.)

    It _can_, but I wouldn't take it as a given that it _does_, and
    what I'm trying to say is that contrary to often getting in the
    way, it can be used effectively to make programs better and
    safer, with no runtime downside. Indeed, a well-typed program
    can be faster than the alternative, since a) the compiler can
    prove that some properties hold, and b) it can be more
    aggressively optimized at a higher level. An example here are
    non-nullable references; there's no need to check whether they
    are NULL or not before indirecting through them, since by
    definition they cannot be NULL.


    I fully agree.



    Hmm. This reads less like an indictment of the idea of stronger
    typing, but rather a failure to provide adequate abstractions in
    the type system.

    It is not an indication of problems with stronger typing, but it is an
    indication that the restrictions inherent in the language and tools made
    them somewhat unsuitable for a lot of use-cases that fitted the hardware
    well. (Modern XMOS tools have changed significantly since those days,
    perhaps partly because of such issues.)

    Sure, but that's a failure of that system to provide good
    abstractions: the system was not rich enough to provide the
    functionality you needed or wanted.

    I think the central tension here is that the idea of a strong
    type system that is also semantically rich is being conflated
    with one that's highly restrictive. What I'm suggesting is that
    the opposite is true.

    To be clear - the restrictions here were not part of the type system.
    The XC language did have some differences in typing from C, if I
    remember the details correctly - rather than C pointers it had
    references which could not be null. And that's fine.

    The restrictions were enforced by whole-program analysis. I think (and
    you are the Rust expert here, not me) that the Rust borrow checker is
    similar. The rules of the language specify aspects of what can and
    cannot be done with access to data, and these are enforced at a higher
    level than the main compilation. They are language rules, but not part
    of the type system.


    I'm going to mention Rust again; apologies. When confined to
    the safe subset, it _also_ has data race freedom. The rules
    that give this property are:

    1. Every object has exactly one owner, and assignments of
    non-trivial types change ownership (they are logically a
    "move");
    2. References to an object may be "borrowed" from the owner,
    and mutable references (that is, references that may be used
    to write to the object) are distinct from immutable
    references (that is, references that may only be used to read
    from an object);
    3. Mutable and immutable references are temporally mutually
    exclusive: a mutable reference may be borrowed from an object
    iff that is the only live reference to that object at the
    time: that is, it is not permitted to borrow a mut ref to an
    object if either another mut ref or any immutable refs to it
    are live; any number of immutable references may be taken to
    an object concurrently.

    If all of these rules are obeyed (and in safe rust, they're
    verified at compile time by the borrow checker) then you cannot
    have data races.

    That's all good, by the sound of it. But if people find this gets in
    the way of efficiency - perhaps because they know that it is safe to
    hold two separate mutable references to an object for reasons the borrow
    checker can't see - the temptation to use "unsafe" or other workarounds
    comes quickly.

    Not really. The rule is that borrowing a mutable reference to
    some object is mutually exclusive with borrowing any other kind
    of reference to that same object at the same time. `unsafe`
    doesn't change that rules, or let the programmer off the hook
    for violating it: `unsafe` code isn't allowed to mix mut ref
    with other references any more than _safe_ code. All `unsafe`
    means is that the burden of upholding the rules of the language
    falls to the programmer, without compiler assistence, because
    the programmer knows something that the compiler does not (and
    probably cannot) for some reason.

    That is roughly what I was trying to express, although there is perhaps
    a grey area between "I am following the rules even though the tools
    can't see it" and "I am breaking the rules but I know it is safe to do
    so here without compromising the reason for those rules".

    In C (trying desperately to bring us back to c.l.c. :-) ), you might use
    casts to do something that looks wrong to the compiler, but which you
    know is safe and correct.


    However, if it really _is_ the case that multiple simultaneous
    writes are ok, the ability to create new types and assign them
    semantics and behavior menas that a programmer can build a _new_
    abstraction that lets them do something similar. Atomics are
    an interesting case in point: one can store to them using a
    non-mutable reference: while this seems counter-intuitive at
    first, it sort of makes sense for the same reason that a `Mutex`
    is accessed via an immutable reference: one cannot observe an
    atomic in an intermediate state due to an update, and `load` and
    `store` are explicit operations (in practice, the compiler
    lowers those to single load/store operations and whatever
    barriers are appropriate for the target architecture).

    OK. Though that is probably a level of detail that would be clearer to
    me if I read the Rust documentation first!


    A hardware device modeling a framebuffer might be another case;
    those are output-only devices, and there's no reason that two
    threads cannot write to different parts of the buffer at the
    same time, but given a suitable interface, the interface doesn't
    need to expose unsafety. And if you've got a rich type system,
    you have the rules to build that interface.

    That is the kind of thing that was difficult in XC. Perhaps it would be
    fair to say that its type system was not powerful enough to work
    conveniently with its data race and thread safety checking systems.


    That does not mean that a language should not try to have such rules,
    and enforce them at compile time - far from it. The aim should be that
    as much code as possible is correct, or at least immune to particular
    classes of bugs, checked by the compiler and tools.

    I think you're saying something different than what I'm saying;
    my point is that the safe interface shouldn't be seen as a
    burden and, if well-executed, it shouldn't add a runtime tax in
    terms of reduced performance. You seem to be taking it as a
    given that the rules of the language will make both of those
    things true, however.

    I am saying that a safe interface /can/ sometimes be a burden - not that
    it has to be.

    An interface defines what can be done, and also what cannot be done.
    It's power as an interface comes from both of these - letting you do
    useful things, and preventing you from doing harmful things. Sometimes, however, there may be things you want to do that you know are useful and
    not harmful, but that the interface disallows. If the language or
    interface is well designed, and a good fit for the tasks people want to
    do with the language, then such situations will be minimal. But I think
    it is quite easy to fall into a trap when designing an interface where
    you exclude too many useful cases. If that happens, then programmers
    have to use riskier or less efficient workarounds, or find alternative interfaces (or languages).

    As a concrete example, I recently wanted to use a std::variant<> in my
    C++ code. A std::variant<> is, approximately, a struct like :

    struct {
    enum { ... } type_tag;
    union { ... } contents;
    }

    In C++, this is all type-safe - you don't have direct access to these
    fields, but instead they are kept consistent automatically so that
    objects of different types can be stored in the union and have their constructors and destructors called correctly. But in my case, I wanted
    to access the fields independently - I wanted to fill the "contents"
    with data retrieved over a network, and set the "type_tag" according to
    other data in the network packet. I knew this was safe (since no
    constructors or destructors were needed). But there was no way to
    handle this within the interface. My options included a risky,
    non-portable and difficult workaround (digging through the <variant>
    header and directly "hacking" the variant object using unsigned char* pointers), serious run-time inefficiencies (with extra copies of the
    data), or finding a different interface. I opted for the last one,
    making my own simple variation of std::variant that gave me the access I needed.

    This, of course, does not necessarily mean the interface of
    std::variant<> is bad. If my needs were highly unusual, then a standard library does not need to support them. But it is an example where the restrictions imposed by a safe, powerful interface limited how I could
    work with what is essentially the same kind of object.



    At first glance it appears that it must suffer from the same
    drawbacks as `XC`, which you mentioned above. Except that the
    language does provide controlled ways to share data
    concurrently.

    Using the _unsafe_ subset, there is one place where it is
    permitted to have multiple, mutable references to an object: the
    `UnsafeCell`. This gives Rust interior mutability, which means
    that you can build safe abstractions for data sharing, like
    `Mutex` types that own the data they protect.

    If "unsafe" gives enough, but is rarely needed in most code, then it
    sounds like a good balance.

    Yes. And moreover, one can hide that unsafety behind a safe
    interface.


    I do like that the unsafety is marked explicitly and clearly.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Mon May 25 07:39:28 2026
    From Newsgroup: comp.lang.c

    [I am writing a single response on one topic that was being
    discussed in two posts in parallel sub-threads. Some
    extraneous material removed. Responding to:]

    scott@slp53.sl.home (Scott Lurndal) writes:
    cross@spitfire.i.gajendra.net (Dan Cross) writes:
    In article <Xj%PR.313884$yHZ7.10738@fx10.iad>,
    Scott Lurndal <slp53@pacbell.net> wrote:
    cross@spitfire.i.gajendra.net (Dan Cross) writes:

    [example of conditional operator: a = cond ? x : y; ]

    In fact, I find the ternary operator to be used rather
    sparingly, even in the [example shown above].

    I wouldn't necessarily categorize it as "sparingly"; Searching
    through the simulator source base, I find it used rather
    frequently in certain scenarios:

    diag = snprintf(cp, remaining, "Feature1:%c Feature2:%c",
    is_feature1()?'+':'-', is_feature2()?'+':'-');

    deliver_interrupt(IntVec_TIMER, is_secure()?FLAGS_SECURE:0u);

    Those are the kinds of scenarios where it can be useful; what
    percentage of code overall uses that versus `if`, though?

    Other than the cases above, 'if' dominates by orders of magnitude.

    [and to:]

    cross@spitfire.i.gajendra.net (Dan Cross) writes:
    In article <10ure81$1s756$1@dont-email.me>,
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 2026-05-22 16:57, Dan Cross wrote:

    In fact, I find the ternary operator to be used rather
    sparingly, even in the [same example as before].

    I haven't got that impression.

    Some piece of software (it's just a random example, to be clear)
    that has
    130 C-files
    250249 total LOC
    shows
    3864 lines with ?: appearing
    I think this is a lot!

    That's about 1.5% of source lines. I'd say that's pretty
    sparingly used.

    It wouldn't surprise me to find that 'if' dominates '?:' by two
    orders of magnitude in some code bases, or even many code bases.
    Probably someone has produced some statistics on such things,
    although off the top of my head I'm not aware of any (but I should
    add that I haven't looked either).

    On the other hand, I am confident that the numbers vary a lot
    depending on programming style. I have become accustomed to writing
    code in a mostly functional style, even in conventional imperative
    languages like C. Out of curiosity I tabulated some numbers from a
    project I am working on now, just for constructs related to transfer
    of control. As is my custom I calculate ratios not as a percentage
    of total source lines but as a percentage of lines inside function
    boundaries. (Looking at different code bases I see that the ratio
    of function body lines to total source lines can easily vary by a
    factor of two or more between code bases; hence I think percentage
    of function body lines gives a better measure.) Here are the
    results:

    goto/continue 0.3%
    for 0.7%
    switch 0.7%
    break 0.8% (all of these are from switch cases)
    while 2.3% (0.7% do-while / 1.6% plain while)
    if 8.1% (2.0% multi-line / 6.1% single line)
    ?: 14.3%
    return 21.4%

    Of course I'm not claiming that these results are in any way
    representative. In fact at some level that is the point - actual
    ratios and relative percentages can vary widely, depending on
    programming style. Beyond that I'm not offering any conclusions;
    I just thought people might be interested in some additional data
    points.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From cross@cross@spitfire.i.gajendra.net (Dan Cross) to comp.lang.c on Tue May 26 14:09:16 2026
    From Newsgroup: comp.lang.c

    In article <10v17h4$18mp3$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    On 24/05/2026 21:08, Dan Cross wrote:
    In article <10uprpn$1hfpm$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    [snip]
    People do sometimes write C code that
    knowingly depends on undefined behaviour, because it makes their results >>> more efficient and "it worked when I tested it".

    Yes, or they force some knob on their compiler into the required
    position to give them guaranteed results. Linux does this, for
    example; last time I worked in it, Google's massive (2BLOC) code
    base similarly.

    What you then get is code that is UB in C, but not UB in >C-with-no-strict-aliasing, or whatever "augmented" language you pick.

    Yes. An observation pointed out to me at my last gig, and that
    I now point out occasionally myself, is that Linux (for example)
    is not so much written in C but rather in Linux C, which is the
    dialect of the language defined by the compilers they use and
    the specific behaviors they force using flags, pragmas, etc, for
    those compilers. At any rate, it's certainly not strictly
    conforming ISO C: is _any_ large program strictly conforming at
    this point? I suspect many projects strive for such, but few
    actually attain it.

    (I have a strict policy of putting any such knobs in "GCC optimize"
    pragmas, so that the code correctness does not depend on the flags
    picked in a makefile. Of course such code remains equally limited in
    its portability.)

    Good idea, though I like the idea of the compiler failing with a
    usage error if an option is no longer available (or someone is
    trying to build using an old compiler or what have you).

    [snip]
    Hmm. This reads less like an indictment of the idea of stronger
    typing, but rather a failure to provide adequate abstractions in
    the type system.

    It is not an indication of problems with stronger typing, but it is an
    indication that the restrictions inherent in the language and tools made >>> them somewhat unsuitable for a lot of use-cases that fitted the hardware >>> well. (Modern XMOS tools have changed significantly since those days,
    perhaps partly because of such issues.)

    Sure, but that's a failure of that system to provide good
    abstractions: the system was not rich enough to provide the
    functionality you needed or wanted.

    I think the central tension here is that the idea of a strong
    type system that is also semantically rich is being conflated
    with one that's highly restrictive. What I'm suggesting is that
    the opposite is true.

    To be clear - the restrictions here were not part of the type system.
    The XC language did have some differences in typing from C, if I
    remember the details correctly - rather than C pointers it had
    references which could not be null. And that's fine.

    The restrictions were enforced by whole-program analysis. I think (and
    you are the Rust expert here, not me) that the Rust borrow checker is >similar. The rules of the language specify aspects of what can and
    cannot be done with access to data, and these are enforced at a higher
    level than the main compilation. They are language rules, but not part
    of the type system.

    Sorry, let me be clear here: the _language_ is not expressive
    enough to provide the kinds of abstractions that would be useful
    for the types of applications the hardware seems well-suited
    for.

    But the original context was types; my point was that a richer
    langauge can provide the building blocks so that one can build a
    safe abstraction around those kinds of behaviors, even if one
    uses `unsafe` in the implementation of those abstractions.

    This was meant in response to your earlier statement, that e.g.
    performance might cause one to want to reach for `unsafe` to
    work _around_ the language; my point is that the language gives
    you tools to work _with_ it. That is, the initial desire to
    reach for `unsafe` is _reduced_ by the ability to effectively
    hide it.

    Implicit in this is a distinction between building what one
    might call infrastructure within one's program, and using that
    infrastructure: the former might involve some controlled (and
    hopefully limited) use of `unsafe`, while the latter ideally
    does not. I find this is the opposite of what many people
    assume when coming from other language backgrounds, where the
    desire is to push `unsafe` to the point of use; often this is
    because in other languages, building up those abstractions
    requires a lot of machinery with high runtime costs.

    [snip]
    Not really. The rule is that borrowing a mutable reference to
    some object is mutually exclusive with borrowing any other kind
    of reference to that same object at the same time. `unsafe`
    doesn't change that rules, or let the programmer off the hook
    for violating it: `unsafe` code isn't allowed to mix mut ref
    with other references any more than _safe_ code. All `unsafe`
    means is that the burden of upholding the rules of the language
    falls to the programmer, without compiler assistence, because
    the programmer knows something that the compiler does not (and
    probably cannot) for some reason.

    That is roughly what I was trying to express, although there is perhaps
    a grey area between "I am following the rules even though the tools
    can't see it" and "I am breaking the rules but I know it is safe to do
    so here without compromising the reason for those rules".

    That's what I'm saying; a program really _doesn't_ get to break
    the rules and retain any guarantee of behavior, unsafe or not.
    In that sense, writing `unsafe` Rust code is _harder_ than
    writing C or another unsafe language. There is no grey area:
    either the programmer makes sure the program follows the rules,
    or all bets are off.

    In C (trying desperately to bring us back to c.l.c. :-) ), you might use >casts to do something that looks wrong to the compiler, but which you
    know is safe and correct.

    That's qualitatively different, though. If, say, I manually
    validate that a pointer points to valid memory, and is properly
    aligned, and so forth, then I can cast that pointer to some
    type. You can absolutely do that in Rust, too. But if,
    instead, you say, "I'm going to intentionally invoke UB here..."
    then both languages will bite you.

    However, if it really _is_ the case that multiple simultaneous
    writes are ok, the ability to create new types and assign them
    semantics and behavior menas that a programmer can build a _new_
    abstraction that lets them do something similar. Atomics are
    an interesting case in point: one can store to them using a
    non-mutable reference: while this seems counter-intuitive at
    first, it sort of makes sense for the same reason that a `Mutex`
    is accessed via an immutable reference: one cannot observe an
    atomic in an intermediate state due to an update, and `load` and
    `store` are explicit operations (in practice, the compiler
    lowers those to single load/store operations and whatever
    barriers are appropriate for the target architecture).

    OK. Though that is probably a level of detail that would be clearer to
    me if I read the Rust documentation first!

    A hardware device modeling a framebuffer might be another case;
    those are output-only devices, and there's no reason that two
    threads cannot write to different parts of the buffer at the
    same time, but given a suitable interface, the interface doesn't
    need to expose unsafety. And if you've got a rich type system,
    you have the rules to build that interface.

    That is the kind of thing that was difficult in XC. Perhaps it would be >fair to say that its type system was not powerful enough to work >conveniently with its data race and thread safety checking systems.

    Yes. It also doesn't give you any means to build an abstraction
    that lets you do the thing you want to do.

    That does not mean that a language should not try to have such rules,
    and enforce them at compile time - far from it. The aim should be that
    as much code as possible is correct, or at least immune to particular
    classes of bugs, checked by the compiler and tools.

    I think you're saying something different than what I'm saying;
    my point is that the safe interface shouldn't be seen as a
    burden and, if well-executed, it shouldn't add a runtime tax in
    terms of reduced performance. You seem to be taking it as a
    given that the rules of the language will make both of those
    things true, however.

    I am saying that a safe interface /can/ sometimes be a burden - not that
    it has to be.

    Well yes, of course; I thought that was a given in the context
    of this discussion.

    An interface defines what can be done, and also what cannot be done.
    It's power as an interface comes from both of these - letting you do
    useful things, and preventing you from doing harmful things. Sometimes, >however, there may be things you want to do that you know are useful and
    not harmful, but that the interface disallows. If the language or
    interface is well designed, and a good fit for the tasks people want to
    do with the language, then such situations will be minimal. But I think
    it is quite easy to fall into a trap when designing an interface where
    you exclude too many useful cases. If that happens, then programmers
    have to use riskier or less efficient workarounds, or find alternative >interfaces (or languages).

    Yes. And what I'm saying is that when working with a language
    that allows you to create useful, robust, types, you can often
    bridge the two worlds and build a new, useful abstraction. C is
    not a language where one can easily do that; you've got structs,
    unions, and functions...and that's about it. But you cannot
    restrict the behavior of a struct in the way that King described
    in the "Parse, Don't Validate" piece I linked earlier.

    An instance of a struct is Not a proof that some property holds,
    but it _may_ be in Rust or Haskell or OCaml, SML, etc. I
    suspect one cannot easily do that in C++, because it's not
    managed and doesn't make ownership a first-class property,
    though one can usefully write a number of classes and so forth
    that give much the same effect, if not actual guarantees.
    Interestingly (maybe?), I find Ada lacking in this area.

    As a concrete example, I recently wanted to use a std::variant<> in my
    C++ code. A std::variant<> is, approximately, a struct like :

    struct {
    enum { ... } type_tag;
    union { ... } contents;
    }

    In C++, this is all type-safe - you don't have direct access to these >fields, but instead they are kept consistent automatically so that
    objects of different types can be stored in the union and have their >constructors and destructors called correctly. But in my case, I wanted
    to access the fields independently - I wanted to fill the "contents"
    with data retrieved over a network, and set the "type_tag" according to >other data in the network packet. I knew this was safe (since no >constructors or destructors were needed). But there was no way to
    handle this within the interface. My options included a risky,
    non-portable and difficult workaround (digging through the <variant>
    header and directly "hacking" the variant object using unsigned char* >pointers), serious run-time inefficiencies (with extra copies of the
    data), or finding a different interface. I opted for the last one,
    making my own simple variation of std::variant that gave me the access I >needed.

    This, of course, does not necessarily mean the interface of
    std::variant<> is bad. If my needs were highly unusual, then a standard >library does not need to support them. But it is an example where the >restrictions imposed by a safe, powerful interface limited how I could
    work with what is essentially the same kind of object.

    Well, sure, that's the danger of general libraries: you're
    getting something that is a decent combination of functionality
    and expressiveness contrasted with safety for many needs, but
    (almost by definition) not for all needs. As you point out,
    that is useful for many cases, though.

    At first glance it appears that it must suffer from the same
    drawbacks as `XC`, which you mentioned above. Except that the
    language does provide controlled ways to share data
    concurrently.

    Using the _unsafe_ subset, there is one place where it is
    permitted to have multiple, mutable references to an object: the
    `UnsafeCell`. This gives Rust interior mutability, which means
    that you can build safe abstractions for data sharing, like
    `Mutex` types that own the data they protect.

    If "unsafe" gives enough, but is rarely needed in most code, then it
    sounds like a good balance.

    Yes. And moreover, one can hide that unsafety behind a safe
    interface.

    I do like that the unsafety is marked explicitly and clearly.

    It goes beyond that, though: _if_ the programmer is successful
    in providing a safe abstraction around the unsafety, then it is
    impossible to compromise the memory safety of the program by
    using that abstraction. Naturally, "if" is doing a lot of work
    here: as I said, the burden for writing `unsafe` code is higher
    in Rust than it is in C, and it requires a lot of care. But the
    resulting guarantees are much stronger.

    - Dan C.

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Tue May 26 18:16:23 2026
    From Newsgroup: comp.lang.c

    On 26/05/2026 16:09, Dan Cross wrote:

    (The posts here are getting long, and I think significantly off-topic
    for c.l.c. I am reading your posts with interest and appreciation, but
    I am afraid it is getting too time-consuming to do justice to them in
    replies. I've given a somewhat terse reply here, though an even terser version would be "I agree with most of it, you've given me lots of
    things to think about, and I really need to learn Rust :-)". I will
    re-read the post later and may reply more then.)

    In article <10v17h4$18mp3$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    On 24/05/2026 21:08, Dan Cross wrote:
    In article <10uprpn$1hfpm$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    [snip]
    People do sometimes write C code that
    knowingly depends on undefined behaviour, because it makes their results >>>> more efficient and "it worked when I tested it".

    Yes, or they force some knob on their compiler into the required
    position to give them guaranteed results. Linux does this, for
    example; last time I worked in it, Google's massive (2BLOC) code
    base similarly.

    What you then get is code that is UB in C, but not UB in
    C-with-no-strict-aliasing, or whatever "augmented" language you pick.

    Yes. An observation pointed out to me at my last gig, and that
    I now point out occasionally myself, is that Linux (for example)
    is not so much written in C but rather in Linux C, which is the
    dialect of the language defined by the compilers they use and
    the specific behaviors they force using flags, pragmas, etc, for
    those compilers. At any rate, it's certainly not strictly
    conforming ISO C: is _any_ large program strictly conforming at
    this point? I suspect many projects strive for such, but few
    actually attain it.


    I think a lot of code is fully portable C, but very few (if any) real
    programs are. Even if they don't contain compiler-specific
    requirements, they will generally depend on particular
    implementation-specific behaviours or non-standard libraries and headers.

    (I have a strict policy of putting any such knobs in "GCC optimize"
    pragmas, so that the code correctness does not depend on the flags
    picked in a makefile. Of course such code remains equally limited in
    its portability.)

    Good idea, though I like the idea of the compiler failing with a
    usage error if an option is no longer available (or someone is
    trying to build using an old compiler or what have you).


    You should get an error (in gcc) for a "#pragma GCC optimize" that is
    not supported. On the other hand, if you misspell it as "#pragma GCC optimise", it will be ignored unless you have "-Wunknown-pragmas" or
    "-Wall" enabled. And other compilers could silently ignore the GCC
    optimize pragmas.



    [snip]
    Hmm. This reads less like an indictment of the idea of stronger
    typing, but rather a failure to provide adequate abstractions in
    the type system.

    It is not an indication of problems with stronger typing, but it is an >>>> indication that the restrictions inherent in the language and tools made >>>> them somewhat unsuitable for a lot of use-cases that fitted the hardware >>>> well. (Modern XMOS tools have changed significantly since those days, >>>> perhaps partly because of such issues.)

    Sure, but that's a failure of that system to provide good
    abstractions: the system was not rich enough to provide the
    functionality you needed or wanted.

    I think the central tension here is that the idea of a strong
    type system that is also semantically rich is being conflated
    with one that's highly restrictive. What I'm suggesting is that
    the opposite is true.

    To be clear - the restrictions here were not part of the type system.
    The XC language did have some differences in typing from C, if I
    remember the details correctly - rather than C pointers it had
    references which could not be null. And that's fine.

    The restrictions were enforced by whole-program analysis. I think (and
    you are the Rust expert here, not me) that the Rust borrow checker is
    similar. The rules of the language specify aspects of what can and
    cannot be done with access to data, and these are enforced at a higher
    level than the main compilation. They are language rules, but not part
    of the type system.

    Sorry, let me be clear here: the _language_ is not expressive
    enough to provide the kinds of abstractions that would be useful
    for the types of applications the hardware seems well-suited
    for.

    Agreed.


    But the original context was types; my point was that a richer
    langauge can provide the building blocks so that one can build a
    safe abstraction around those kinds of behaviors, even if one
    uses `unsafe` in the implementation of those abstractions.

    This was meant in response to your earlier statement, that e.g.
    performance might cause one to want to reach for `unsafe` to
    work _around_ the language; my point is that the language gives
    you tools to work _with_ it. That is, the initial desire to
    reach for `unsafe` is _reduced_ by the ability to effectively
    hide it.

    Fair enough.


    Implicit in this is a distinction between building what one
    might call infrastructure within one's program, and using that infrastructure: the former might involve some controlled (and
    hopefully limited) use of `unsafe`, while the latter ideally
    does not. I find this is the opposite of what many people
    assume when coming from other language backgrounds, where the
    desire is to push `unsafe` to the point of use; often this is
    because in other languages, building up those abstractions
    requires a lot of machinery with high runtime costs.


    I believe we can agree that the ideal should be that "unsafe", or
    equivalent, should not be needed in most code. My original point was
    just that trying too hard to make a language "safe" without enough care
    for how it will be used can push people towards "unsafe" alternatives.
    But if suitable care is taken, then the result is less unsafe code and
    an overall better language for the task.

    [snip]
    Not really. The rule is that borrowing a mutable reference to
    some object is mutually exclusive with borrowing any other kind
    of reference to that same object at the same time. `unsafe`
    doesn't change that rules, or let the programmer off the hook
    for violating it: `unsafe` code isn't allowed to mix mut ref
    with other references any more than _safe_ code. All `unsafe`
    means is that the burden of upholding the rules of the language
    falls to the programmer, without compiler assistence, because
    the programmer knows something that the compiler does not (and
    probably cannot) for some reason.

    That is roughly what I was trying to express, although there is perhaps
    a grey area between "I am following the rules even though the tools
    can't see it" and "I am breaking the rules but I know it is safe to do
    so here without compromising the reason for those rules".

    That's what I'm saying; a program really _doesn't_ get to break
    the rules and retain any guarantee of behavior, unsafe or not.
    In that sense, writing `unsafe` Rust code is _harder_ than
    writing C or another unsafe language. There is no grey area:
    either the programmer makes sure the program follows the rules,
    or all bets are off.

    In C (trying desperately to bring us back to c.l.c. :-) ), you might use
    casts to do something that looks wrong to the compiler, but which you
    know is safe and correct.

    That's qualitatively different, though. If, say, I manually
    validate that a pointer points to valid memory, and is properly
    aligned, and so forth, then I can cast that pointer to some
    type. You can absolutely do that in Rust, too. But if,
    instead, you say, "I'm going to intentionally invoke UB here..."
    then both languages will bite you.

    However, if it really _is_ the case that multiple simultaneous
    writes are ok, the ability to create new types and assign them
    semantics and behavior menas that a programmer can build a _new_
    abstraction that lets them do something similar. Atomics are
    an interesting case in point: one can store to them using a
    non-mutable reference: while this seems counter-intuitive at
    first, it sort of makes sense for the same reason that a `Mutex`
    is accessed via an immutable reference: one cannot observe an
    atomic in an intermediate state due to an update, and `load` and
    `store` are explicit operations (in practice, the compiler
    lowers those to single load/store operations and whatever
    barriers are appropriate for the target architecture).

    OK. Though that is probably a level of detail that would be clearer to
    me if I read the Rust documentation first!

    A hardware device modeling a framebuffer might be another case;
    those are output-only devices, and there's no reason that two
    threads cannot write to different parts of the buffer at the
    same time, but given a suitable interface, the interface doesn't
    need to expose unsafety. And if you've got a rich type system,
    you have the rules to build that interface.

    That is the kind of thing that was difficult in XC. Perhaps it would be
    fair to say that its type system was not powerful enough to work
    conveniently with its data race and thread safety checking systems.

    Yes. It also doesn't give you any means to build an abstraction
    that lets you do the thing you want to do.


    Indeed. (And again, let me note that this was my experience with XC a
    long time ago - the tools have changed since then.)

    That does not mean that a language should not try to have such rules,
    and enforce them at compile time - far from it. The aim should be that >>>> as much code as possible is correct, or at least immune to particular
    classes of bugs, checked by the compiler and tools.

    I think you're saying something different than what I'm saying;
    my point is that the safe interface shouldn't be seen as a
    burden and, if well-executed, it shouldn't add a runtime tax in
    terms of reduced performance. You seem to be taking it as a
    given that the rules of the language will make both of those
    things true, however.

    I am saying that a safe interface /can/ sometimes be a burden - not that
    it has to be.

    Well yes, of course; I thought that was a given in the context
    of this discussion.

    An interface defines what can be done, and also what cannot be done.
    It's power as an interface comes from both of these - letting you do
    useful things, and preventing you from doing harmful things. Sometimes,
    however, there may be things you want to do that you know are useful and
    not harmful, but that the interface disallows. If the language or
    interface is well designed, and a good fit for the tasks people want to
    do with the language, then such situations will be minimal. But I think
    it is quite easy to fall into a trap when designing an interface where
    you exclude too many useful cases. If that happens, then programmers
    have to use riskier or less efficient workarounds, or find alternative
    interfaces (or languages).

    Yes. And what I'm saying is that when working with a language
    that allows you to create useful, robust, types, you can often
    bridge the two worlds and build a new, useful abstraction. C is
    not a language where one can easily do that; you've got structs,
    unions, and functions...and that's about it. But you cannot
    restrict the behavior of a struct in the way that King described
    in the "Parse, Don't Validate" piece I linked earlier.


    Indeed. C is not the language of choice for that sort of thing.

    An instance of a struct is Not a proof that some property holds,
    but it _may_ be in Rust or Haskell or OCaml, SML, etc. I
    suspect one cannot easily do that in C++, because it's not
    managed and doesn't make ownership a first-class property,
    though one can usefully write a number of classes and so forth
    that give much the same effect, if not actual guarantees.

    I would say you can do this sort of thing in C++ too, but there are
    perhaps more ways to "cheat" or break promises and guarantees than in in
    some other languages.

    Interestingly (maybe?), I find Ada lacking in this area.

    My experience with Ada is not enough to judge your opinion here, but I
    agree this is interesting (and a little surprising).


    As a concrete example, I recently wanted to use a std::variant<> in my
    C++ code. A std::variant<> is, approximately, a struct like :

    struct {
    enum { ... } type_tag;
    union { ... } contents;
    }

    In C++, this is all type-safe - you don't have direct access to these
    fields, but instead they are kept consistent automatically so that
    objects of different types can be stored in the union and have their
    constructors and destructors called correctly. But in my case, I wanted
    to access the fields independently - I wanted to fill the "contents"
    with data retrieved over a network, and set the "type_tag" according to
    other data in the network packet. I knew this was safe (since no
    constructors or destructors were needed). But there was no way to
    handle this within the interface. My options included a risky,
    non-portable and difficult workaround (digging through the <variant>
    header and directly "hacking" the variant object using unsigned char*
    pointers), serious run-time inefficiencies (with extra copies of the
    data), or finding a different interface. I opted for the last one,
    making my own simple variation of std::variant that gave me the access I
    needed.

    This, of course, does not necessarily mean the interface of
    std::variant<> is bad. If my needs were highly unusual, then a standard
    library does not need to support them. But it is an example where the
    restrictions imposed by a safe, powerful interface limited how I could
    work with what is essentially the same kind of object.

    Well, sure, that's the danger of general libraries: you're
    getting something that is a decent combination of functionality
    and expressiveness contrasted with safety for many needs, but
    (almost by definition) not for all needs. As you point out,
    that is useful for many cases, though.

    At first glance it appears that it must suffer from the same
    drawbacks as `XC`, which you mentioned above. Except that the
    language does provide controlled ways to share data
    concurrently.

    Using the _unsafe_ subset, there is one place where it is
    permitted to have multiple, mutable references to an object: the
    `UnsafeCell`. This gives Rust interior mutability, which means
    that you can build safe abstractions for data sharing, like
    `Mutex` types that own the data they protect.

    If "unsafe" gives enough, but is rarely needed in most code, then it
    sounds like a good balance.

    Yes. And moreover, one can hide that unsafety behind a safe
    interface.

    I do like that the unsafety is marked explicitly and clearly.

    It goes beyond that, though: _if_ the programmer is successful
    in providing a safe abstraction around the unsafety, then it is
    impossible to compromise the memory safety of the program by
    using that abstraction. Naturally, "if" is doing a lot of work
    here: as I said, the burden for writing `unsafe` code is higher
    in Rust than it is in C, and it requires a lot of care. But the
    resulting guarantees are much stronger.

    - Dan C.


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Wed May 27 01:05:22 2026
    From Newsgroup: comp.lang.c

    On Sun, 24 May 2026 01:14:35 +0800, makendo wrote:

    Multipass is for when you have only kilobits of core memory, where
    the only way to realistically compile your code is to dump
    intermediate results (in the assembler case, memory address of
    labels) on a magnetic tape.

    That’s a different meaning of “pass”, not what I meant at all.
    --- Synchronet 3.22a-Linux NewsLink 1.2