• =?UTF-8?Q?Re=3A_technology_discussion_=E2=86=92_does_the_world_need?= =

    From bart@21:1/5 to BGB on Fri Jul 5 14:31:44 2024
    On 05/07/2024 08:30, BGB wrote:
    On 7/4/2024 8:05 PM, Lawrence D'Oliveiro wrote:
    It’s called “Rust”.


    If anything, I suspect may make sense to go a different direction:
      Not to a bigger language, but to a more narrowly defined language.

    Basically, to try to distill what C does well, keeping its core essence intact.

    That would just be rearranging the deck chairs I think.

    Whatever the failings of C, it is so entrenched everywhere that it is
    almost impossible to dislodge. Especially if you take out all the the
    features that people apparently find indispensible.

    There are anyway plenty of big-name contenders which change a LOT more,
    and are full of modern, higher-level features that people seem to want.

    C also is the only language that is supposed to work on any kind of
    processor; most of the others require a machine which has 8/16/32/64-bit
    types and have little interest in unusual targets.

    (Actually, the nearest language to C that I have seen, is mine. It stays
    at about that level, but it has modern conveniences such as a module
    scheme and proper name-spaces. However, it is a private language, and
    also 1-based, case-insensitive and with non-brace syntax! It is not a contender.)


    *1: While not exactly that rare, and can be useful, it is debatable if
    they add enough to really justify their complexity and relative semantic fragility.

    C only has 1D arrays, but array elements can themselves be array types.

    The only complication, a big one, is that modern C allows a variable
    length for those array elements (so not known at a compile-time). This
    is tied in with VLAs and 'variable modified types' or whatever the
    feature is called.

    This applies when the multi-array data is a single block of memory.

    If using pointers, one almost invariably needs to fall back
    to doing "arr[y*N+x]"

    I've never had to do that, even if N was a variable. If using Iliffe
    vectors then C allows you do regular indexing even with runtime bounds.

    Note that multidimensional indexing via multiple levels of pointer indirection would not be effected by this.

    These are Iliffe vectors.


    Similarly, structs may not be declared at the point of use, but only as types.

    This is how it works on my language; they must be a named user-type.
    It's crazy how C allows them just anywhere, even in useless declarations
    like this:

    struct {int x,y;};

    Though, would want to do a few things differently from my current IR to
    be able to reduce memory footprint; as my current IR was designed in
    such a way that it is effectively necessary to load in the whole program before code-generation can be done. Ideally one would want the compiler
    to be able to read-in the IR as-needed and discard the parts it is
    already done with (but, existing efforts to redesign the IR stage here
    have tended to fizzle; would effectively need to redesign the compiler backend to be able to make effective use of it).

    For a new compiler, could make sense to try to "do it right this time" (though, not too much of an issue if running the compiler on a modern
    PC, as they are "not exactly RAM constrained" in this area; so reading
    in and decoding the IR and symbol tables for an entire executable image
    at the same time, is not too much of an issue).

    It's not an issue at all. My main compiler is a whole-program one; the
    entire source code for my biggest program occupies 1/8000th of the
    memory of my PC. That would be like building an 8-byte source file on my
    first computer.

    Presumably you are running on some restricted hardware of your own?

    If pulled of well, such a module system could be both faster and require
    less memory use in the compiler if compared with headers

    Headers are a bottleneck in C. 50 modules all including the same huge
    header (say the 1000 #includes involved with #include <gtk<>, which is
    for GTK2), would involve repeating all that work 50 times.

    Even with unity builds, build times can still get annoying for bigger programs. And here, I am talking like 20 seconds to rebuild a 250kLOC program.

    In my language, a 250Kloc app would take about half a second to build
    into an executable.

    My C compiler is slower since it uses a discrete ASM stage.

    (It will build sql.c which is actually 250Kloc including headers, in
    half a second, generating a 1MB EXE, however that program is 40%
    comments, and big chunks are conditional blocks. Preprocessing it
    generates 85Kloc.)


    Granted, even if parsing is fast, this still leaves the
    challenge of fast/efficient machine-code generation.

    Unoptimised code in my case (for the way I write code, for my language,
    and for my apps) is about 50% slower than when passed through C and gcc-O3.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Lawrence D'Oliveiro on Sat Jul 6 01:46:27 2024
    On 7/5/24 21:38, Lawrence D'Oliveiro wrote:
    On Fri, 5 Jul 2024 14:31:44 +0100, bart wrote:

    C also is the only language that is supposed to work on any kind of
    processor ...

    I don’t think there is anything innate in the design of C to ensure that.

    It is, however, a deliberate design goal of the C committee to make the language widely implementable. Many kinds of unspecified and undefined
    behavior are unspecified or undefined in order to allow unusual
    architectures to have a fully conforming implementation of C - a fact
    which drives bart crazy(er).

    It was simply its popularity that meant it was usually the first language implemented on a new processor.

    For example, C assumes byte addressability. ...

    True. However, C is widely implementable because of the fact that it
    defines a "byte" as containing CHAR_BIT bits, and makes the value of
    CHAR_BIT implementation-defined, subject only to the restriction that
    CHAR_BIT is an integer and >= 8. Also, bytes don't need to be hardware addressable, byte addressability can be emulated in software, and often
    is on machines with a word size > 8.

    There are machines which cannot be configured to support that model for
    any permitted value of CHAR_BIT, but not many.

    ... So that causes awkwardness on
    architectures like the PDP-10, for example.

    While I was learning C at the time, I vaguely recall programming the
    PDP-10 using Fortran IV more than five decades ago. I was far less knowledgeable about computers than I am now, so I might not have
    noticed. What problems are you referring to?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Lawrence D'Oliveiro on Sat Jul 6 10:21:36 2024
    On 06/07/2024 02:38, Lawrence D'Oliveiro wrote:
    On Fri, 5 Jul 2024 14:31:44 +0100, bart wrote:

    C also is the only language that is supposed to work on any kind of
    processor ...

    I don’t think there is anything innate in the design of C to ensure that. It was simply its popularity that meant it was usually the first language implemented on a new processor.

    For example, C assumes byte addressability.

    C didn't define a 'byte' at all. It assumed 'char' addressability, but
    allowed that 'char' to be any width at all. I think at some point a
    minimum of 8 bits was applied.

    So that causes awkwardness on
    architectures like the PDP-10, for example.

    Which was also the first machine I used, and the first I wrote a
    compiler for.

    C didn't exist on it, at least at that establishment, and was never
    mentioned. I didn't take a closer looker until 16 years after I started
    coding.

    The 36-bit words caused problems on other languages too. There was an instruction set extention to allow access to bitfields of any width, but
    that was fiddly to use.

    Some languages had to choose between 'packed' strings, and strings using
    one word per character.

    It just so happened such
    architectures became extinct at about the time the rise of 8-bit microprocessors (and their more advanced successors) made byte- addressability essentially universal.

    The next machine I wrote a compiler for was an 8-bit microprocessor,
    using twos complement, byte addressibility, some 16-bit capability, and
    16-bit addressing.

    Most of today's hardware evolved from such a model: 32- and 64-bit words
    and addresses were an obvious natural progression. C however still
    hasn't got the memo.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to BGB on Sat Jul 6 14:26:47 2024
    On 7/6/24 04:51, BGB wrote:
    ...
    Yeah, and in the 1D case, an array can be seen as functionally an
    implicitly defined pointer with an assigned size and preassigned
    backing memory.

    Granted, C generally allows one to see the backing memory, but not the implicit pointer to said backing memory. I guess one could argue that
    if one can't take the address of it, it doesn't exist, but yeah...

    C won't let you take that pointer's address because it need not exist,
    and in my experience, usually doesn't. Compilers that I'm familiar with
    often store all objects that are local to given scope in a single block
    of memory whose starting address is stored in a register. No memory is
    set aside to store separate pointer objects pointing to any of those
    individual objects. When a pointer value pointing at one of those
    objects is needed, a fixed offset is added to the address stored in that register. Are you familiar with any compilers that handle such things differently? I make no claim to wide knowledge of compilers, but the
    compilers that I used which work that way are among the most widely used
    C compilers.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Lawrence D'Oliveiro on Sat Jul 6 14:28:54 2024
    On 7/6/24 03:23, Lawrence D'Oliveiro wrote:
    On Fri, 05 Jul 2024 11:46:38 -0700, Keith Thompson wrote:

    No, arrays are not pointers.

    Except array indexing is designed to be indistinguishable from pointer arithmetic.

    Actually, C doesn't have array indexing; it only has pointer indexing.
    The subscript operator requires that one of it's operands shall have the
    type "pointer to a complete object type", and that the other shall have
    integer type. It cannot be applied to arrays; but conveniently, the
    standard mandates that:

    "Except when it is the operand of the sizeof operator, or typeof
    operators, or the unary & operator, or is a string literal used to
    initialize an array, an expression that has type "array of type" is
    converted to an expression with type "pointer to type" that points to
    the initial element of the array object ..." (6.3.2.1p3).

    It is that conversion which creates the illusion of array indexing, but
    since it's been converted to a pointer, it is actually pointer indexing.

    The key point is that an expression of array type does not always get
    converted into a pointer to the first element of that array. The clause
    above starts out with four exceptions, and an array behaves quite
    differently from a pointer when any of those exceptions apply.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to James Kuyper on Sat Jul 6 19:53:56 2024
    On 06/07/2024 19:28, James Kuyper wrote:
    On 7/6/24 03:23, Lawrence D'Oliveiro wrote:
    On Fri, 05 Jul 2024 11:46:38 -0700, Keith Thompson wrote:

    No, arrays are not pointers.

    Except array indexing is designed to be indistinguishable from pointer
    arithmetic.

    Actually, C doesn't have array indexing; it only has pointer indexing.
    The subscript operator requires that one of it's operands shall have the
    type "pointer to a complete object type", and that the other shall have integer type. It cannot be applied to arrays; but conveniently, the
    standard mandates that:

    "Except when it is the operand of the sizeof operator, or typeof
    operators, or the unary & operator, or is a string literal used to
    initialize an array, an expression that has type "array of type" is
    converted to an expression with type "pointer to type" that points to
    the initial element of the array object ..." (6.3.2.1p3).

    This is really, really pedantic. Even gcc doesn't get it right in that
    case, because if I try and compile this:

    int a, b
    a[b];

    it says:

    error: subscripted value is neither array nor pointer nor vector

    'Subscripting' I think we can agree is the same thing as 'indexing':
    what those funny square brackets do.




    It is that conversion which creates the illusion of array indexing, but
    since it's been converted to a pointer, it is actually pointer indexing.

    Isn't that how all languages work 'under the hood'? The net affect from
    the user's point of view is that you have an array object, and use
    'indexing' to access its individual elements.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to BGB on Sat Jul 6 20:15:48 2024
    On 06/07/2024 19:46, BGB wrote:

    But, yeah, for most people, writing the compiler is always "someone
    else's problem".


    Granted, even with as much suck as it was to write my own C compiler to target my ISA, it is still likely a better experience than it would have
    been trying to retarget GCC or Clang.

    GCC is sort of a tangled mess of code, trying to add a new target would likely involve weaving it in all across the codebase, which is
    comparably fairly large (well into MLOC territory IIRC).


    Any desire to retarget Clang is limited by the issue that it would
    involve needing to edit and recompile LLVM a large number of times, and
    LLVM takes an absurdly long time to recompile...

    They are like "well, use -j N".
      But, like, "-j 1": It takes like 4 hours;
      "-j 4": It still takes around 1 hour, PC still usable.
      "-j 8": It takes 45 minutes, but PC is made unusable.

    Doing so also eats most of the RAM, on a PC with 112 GB... (Can't
    install a full 128GB with my MOBO/chipset it seems).

    At "-j 8", the HDD with the pagefile is basically running at full capacity.

    Those figures are extraordinary.

    But do you really need to recompile everything in LLVM even when
    changing only target-specific components?

    How much of it is due to it using C++ rather than C?

    Maybe you should consult with David Brown who doesn't believe in the
    benefits of fast compilers, and always knows some tricks to get a
    build-time in seconds no matter how slow the compiler.


    With my own tools, that run on one core with 8GB (2GB of which seems
    tied up with the display for some reason, even though the HD display
    needs only a 0.006GB frame buffer), I can't do anything else when
    compiling, because it will have finished before I've taken my finger off
    the Enter key!

    This is largely thanks to machines now being so fast rather than my
    coding skills, but also because I've never managed to learn how to write
    huge, bloated, inefficient software.

    I don't like products like LLVM; one reason I'm still doing this stuff
    is to makes a stand.

    (About 10 years ago I needed a new side-gate for my house. Rather than
    buy one, I made one myself that was a perfect size, 6' high. If the
    people behind LLVM made garden gates, theirs would have been 9 MILES
    high! Probably 15 miles now.)



    It is a mystery why anyone uses LLVM, I would have considered it a dead
    end due to all this...

    Apparently it takes care of all that pesky back-end stuff. For me it
    would make the task a thousand times harder. Plus I'd end up with a
    compiler of which only 0.4% was my own work; 99.6% somebody else's.
    Based on typical LLVM-based compilers being 100MB.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Keith Thompson on Sun Jul 7 01:36:13 2024
    On 07/07/2024 00:04, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:
    On 06/07/2024 02:38, Lawrence D'Oliveiro wrote:
    On Fri, 5 Jul 2024 14:31:44 +0100, bart wrote:

    C also is the only language that is supposed to work on any kind of
    processor ...
    I don’t think there is anything innate in the design of C to ensure
    that.
    It was simply its popularity that meant it was usually the first language >>> implemented on a new processor.
    For example, C assumes byte addressability.

    C didn't define a 'byte' at all. It assumed 'char' addressability, but
    allowed that 'char' to be any width at all. I think at some point a
    minimum of 8 bits was applied.

    What???

    C defines a "byte" as an "addressable unit of data storage large enough
    to hold any member of the basic character set of the execution
    environment". You know that. C references going back to 1974 all talk
    about bytes (the early ones are specific to the PDP-11).

    Perhaps you meant that there's no predefined type named "byte", but
    nobody said there was.

    The requirement that a byte is at least 8 bits goes back at least to
    C89. K&R1 (1978) doesn't make this requirement explicit, but shows
    examples of 8- and 9-bit bytes.

    [...]

    Most of today's hardware evolved from such a model: 32- and 64-bit
    words and addresses were an obvious natural progression. C however
    still hasn't got the memo.

    Right, C makes it *so* difficult to support systems with 8-bit bytes and
    32- or 64-bit word.


    There's no 'byte' type. There's an odd selection of *5* char, short,
    int, long and long long types which cover the *4* 8/16/32/64 bit sizes.

    They include some wonderful denotations such as 'long int unsigned long'
    (you choose the order you like).

    None are defined by the language as any specific size, other than
    certain minimum widths, and that each successive type is no shorter than
    the last.

    IF you include a special header, THEN it also provides some fixed-width
    sizes, quite likely defined internally on top of types above. However
    these are so ugly, that every other C program prefers to define its own aliases. Plus there are 100 special macros to define format codes,
    MIN/MAX values etc etc.

    Meanwhile, format codes, and constant suffixes, are defined in terms of
    0, 1 or 2 'longs'.

    Most programs also use 'int' extensively, which although is very
    commonly 32 bits, C only guarantees it to be 16 bits. Lots also use
    'long' which is variously 32 or 64 bits.

    So it's not 'difficult', just incredibly messy. I haven't even mentioned
    least and fast types, whatever they do.

    Pretty much every language touted as a C replacement which has fixed
    width integers, has a far tidier and smaller set of more concrete types
    (eg. 8-10, compared with what, 35?).

    They also don't need CHAR_BIT to tell it that chars have 8 bits.

    My point was that this evolution was apparent at least 40 years ago.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Keith Thompson on Sat Jul 6 21:18:18 2024
    On 7/6/24 18:45, Keith Thompson wrote:
    James Kuyper <jameskuyper@alumni.caltech.edu> writes:
    [...]
    The key point is that an expression of array type does not always get
    converted into a pointer to the first element of that array. The clause
    above starts out with four exceptions, and an array behaves quite
    differently from a pointer when any of those exceptions apply.

    There are three exceptions, not four.

    The N1570 draft of C11 incorrectly says:

    I was quoting from n3096.pdf, dated April 1, 2023. It says:

    "Except when it"
    1. "is the operand of the sizeof operator,"
    2. "or typeof operators,"
    3. "or the unary & operator,"
    4. "or is a string literal used to initialize an array, ..."

    What am I miscounting?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Lawrence D'Oliveiro on Sat Jul 6 21:34:29 2024
    On 7/6/24 03:25, Lawrence D'Oliveiro wrote:
    On Fri, 05 Jul 2024 19:00:58 -0700, Keith Thompson wrote:

    C assumes byte addressibility, but it doesn't assume that bytes are 8
    bits.

    The PDP-10 had 36-bit words and could operate on bit fields of any size
    from 1 to 36 bits.

    But it couldn’t address them.

    It doesn't matter whether there's hardware support for addressing bytes
    - byte addressing can be emulated in software on any platform
    sufficiently powerful to implement C's bitwise operators. To read a byte
    on a word-addressed machine where the word size is multiple bytes, just
    read in the word, then extract the bits that represent that particular
    byte. To write a byte on such a machine, read in the current contents of
    that word, replace the bits that represent that byte with their new
    values, and write the entire word back to memory.

    On many platforms, if _Alignof(type) is less than the word size, then a
    C pointer to that type is implemented as the combination of the machine
    address of the correct word, combined with an offset within that word of
    the first byte of that object. The existence of real world
    implementations that did this is the main reason that the C standard
    does not require that all pointers have the same representation.

    This may be inefficient, maybe sufficiently so to be a reason for
    avoiding using C on such a platform, but it doesn't (and hasn't) prevent
    the existence of a fully conforming implementation of C on that platform.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to BGB on Sun Jul 7 12:35:17 2024
    On 07/07/2024 05:28, BGB wrote:
    On 7/6/2024 5:41 PM, Ben Bacarisse wrote:
    BGB <cr88192@gmail.com> writes:

    On 7/5/2024 5:40 PM, Ben Bacarisse wrote:
    BGB <cr88192@gmail.com> writes:

    On 7/5/2024 6:20 AM, Ben Bacarisse wrote:
    BGB <cr88192@gmail.com> writes:

    While eliminating structs could also simplify things; structs
    also tend to
    be a lot more useful.
    Indeed.  And I'd have to use them for this!


    Errm, the strategy I would assume is, as noted:
        int a[4][4];
        ...
        l=a[j][k];
    Becomes:
        int a[16];
        ...
        l=a[j*4+k];
    That's what you want to force me to write, but I can use and array of
    arrays despite your arbitrary ban on them by simply putting the
    array in
    a struct.
    ...
    IN most contexts, I don't really see how a struct is preferable to a
    multiply, but either way...

    And I can't see how an array of arrays is harder for your compiler than
    an array of structs.  C's indexing requires the compiler to know that
    size of the items pointed to.

    I suspect that there is something amiss with your design if you are
    considering this limiting in order to simplify the compiler.  A simple
    compiler should not care what kind of thing p points to in

       p[i]

    only what size of object p points to.



    When I designed the compiler code, the initial approach for internal
    type layout was to bit-pack it into 32 bits, say (a lot of this is from memory, so maybe wrong):
    Basic1
      (31:28): Layout of Type (0=Basic)
      (27:16): Array Size
      (15:12): Pointer Level Count
      (11: 0): Base Type
    Basic2
      (31:28): Layout of Type (1=Basic2)
      (27: 8): Array Size
      ( 7: 6): Pointer Level Count
      ( 5: 0): Base Type
    Basic3
      (31:28): Layout of Type (2=Basic3)
      (27:24): Array Size
      (23:20): Pointer Level Count
      (19: 0): Base Type
    Overflow
      (31:28): Layout of Type (3=Overflow)
      (27:24): MBZ
      (23: 0): Index into Type-Overflow Table
    And, a few other cases...


    Basic1 was the default, able to express arrays from 0..4095 elements,
    with 0..7 levels of pointer indirection, and 0..4095 for the base type.
     Where, 0=T, 1=T*, 2=T**, ..., 7=T*******
       8=T[], 9=T[][], A=T*[], B=T*[*], C=&T, ...

    That's quite a level of detail. This looks more like an encoding you
    might devise for a CPU instruction set. And yet you say elsewhere that
    the whole compiler is 250K lines? So you're not trying to squeeze it
    into a 16KB ROM or something.

    It could also be used to encode another type, which was needed for
    things like multidimensional arrays and some other complex types. But,
    this seemed like an ugly hack... (And was at odds with how I imagined
    types working, but seemed like a technical necessity).

    I can see how multi-dim arrays would be troublesome with such a scheme.

    My own approach is to use a bunch of parallel arrays (a table of structs
    didn't appeal). They are populated with the standard types at the lower
    end, then new types can be added.

    A C type is represented by an index into those arrays. One of those
    arrays is this (not C):

    [0:maxtype]int tttarget # All names start with 'tt'

    For a pointer, it is contains the index of the target type. For arrays,
    it is the index of the element type. There is no restriction on nesting,
    other than 'maxtype' (set to some large number; one day I'll make the
    limit flexible).


    One downside as-is, is that if a given variable is assigned more than
    4096 times in a given function, it can no longer be given a unique ID.
    Though uncommon, this is not entirely implausible (with sufficiently
    large functions), and there isn't currently any good way to deal with
    this (apart from raising a compiler error).


    One of my compiler benchmarks is this program:

    #include <stdio.h>

    int main(void) {
    int a, b=2, c=3, d=4;

    a=b+c*d;

    printf("%d\n", a);
    }

    Except that the 'a=b+c*d;' line is repeated 1M or 2M times, usually
    within an included file.

    Some compilers (including for other languages) find this challenging.



    But, as noted, the 3AC IR only exists in memory.

    In the IR, the operations are expressed as a sort of linear bytecode operating on a virtual stack machine; with types expressed as ASCII
    strings.

    Logically, the stack holds "ccxl_register" values, and the number of 3AC
    ops is typically less than the number of stack-machine operations (those which exist simply to shuffle registers around essentially disappear in
    the translation process).

    Say, for example:
      LOAD x
      LOAD y
      ADD
      STORE z
    Might turn into a single 3AC operation.

    I've used 3AC as an IL (several attempts), and also a stack VM. The 3AC
    always looked so simple, but it also was a lot of hard work to turn it
    into decent register-based code.

    Now I used a stack-based IL which is far easier to work with.

    I wouldn't start off with turning stack code into 3AC! (SSA is supposed
    to be /the/ way to generate code in compilers; they can keep it.)

    Those 4 stack instructions might turn into 3/4 machine instructions on
    x64 (perhaps fewere is register-resident). But if converting them to one
    3AC instruction, you then have to expand them again.

    Perhaps 3AC suits another architecture better (I think ARM uses
    3-register ops; x64 is 2-register ops).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to BGB on Sun Jul 7 10:03:10 2024
    On 7/7/24 00:55, BGB wrote:
    On 7/6/2024 5:38 PM, Keith Thompson wrote:
    ...
    No, there is no implicitly defined pointer.
    ...
    This implicit pointer need not exist at a location in memory...

    Which is why C doesn't give you access to it's location in memory -
    something you complained about earlier.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From aotto1968@21:1/5 to Bonita Montero on Sun Jul 7 20:29:27 2024
    → that's probably true
    → but the CORE of C is the ability to connect with EVERY other existing language.

    On 07.07.24 06:35, Bonita Montero wrote:
    Am 04.07.2024 um 17:16 schrieb aotto1968:
    Hi,

    1. does the world need a "new" C ?
        -> http://thedev.nhi1.de/theKernel/main/managed-object.htm

    2. is one compiler enough ?
        -> http://thedev.nhi1.de/theCompiler/main/alc-compiler.htm

    I just thought that if C would be a new language no one would use it.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to BGB on Sun Jul 7 19:22:24 2024
    On 7/7/24 16:10, BGB wrote:
    On 7/7/2024 9:03 AM, James Kuyper wrote:
    On 7/7/24 00:55, BGB wrote:
    On 7/6/2024 5:38 PM, Keith Thompson wrote:
    ...
    No, there is no implicitly defined pointer.
    ...
    This implicit pointer need not exist at a location in memory...

    Which is why C doesn't give you access to it's location in memory -
    something you complained about earlier.

    I don't think I was claiming that one should have direct access to its location or value within the language, rather that their existence and behaviors could be acknowledged in the language design (for a "not
    quite C" language).

    I think that the existence of an implicit pointer would be a bad thing
    to acknowledge, given that the language doesn't require that it exist,
    and typical implementations don't use them. From what I understand, the
    fact that your implementation does have implicit pointers makes it a rarity.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Kaz Kylheku on Mon Jul 8 00:28:53 2024
    On 7/7/24 20:02, Kaz Kylheku wrote:
    ...
    Ritchie's B language had arrays which contained a pointer to their
    first element. Via a hack, it was possible to relocate an array.

    In C, such a thing is not simply not required; it is ruled out
    by the detailed semantic description of arrays.

    The entire representation of an array of size N elements of type
    T is contained in the memory block that is sizeo(T)*N bytes wide.

    If you copy that block, you have a fully functional copy of the array.
    No extra pointer needs to be set up with the correct value.

    An implementation which took the following code:

    int array1[5], array2[5];
    memcpy(array2, array1, sizeof array1);

    and translated it into machine code that was the equivalent of

    int array1[5], array2[5];
    int *_p1 = &array1[0], *_p2 = &array2[0];
    memcpy(_p2, _p1, sizeof array1);

    would not violate any of the requirements you mention. The key point is
    that when you copy the contents of an array to a new location, you
    wouldn't want to copy the implicit pointer - it would point at the wrong location. And if the destination is itself declared as an array, it
    would already have an implicit pointer that pointed at the correct location.

    I see no point in having implicit pointers, but I don't believe that
    they are prohibited.

    Furthermore, to dynamically allocate an array, you need only
    provide sizeof(T)*N bytes of storage, and not a bit more.

    There is simply nowhere in the representation of an array where
    a pointer could hide that is part of the representation.

    Allocated memory is already accessed through a pointer; there would be
    no corresponding need to create an implicit one when there's already an explicit one.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Tue Jul 9 15:54:06 2024
    On 09/07/2024 15:31, David Brown wrote:
    On 08/07/2024 19:39, BGB wrote:

    Though, this one seems to be a common point of divergence between
    "SysV" and "Microsoft" ABIs. Sometimes a target will have an ABI
    defined, and the MS version was almost the same, just typically
    differing in that it passes structs by reference and provides a spill
    space for register arguments.


    I don't think it is helpful that you keep mixing /logical/ terms with /implementation/ terms.

    In C, there is no "pass by reference" or "return by reference".  It is
    all done by value.

    Arrays are passed by reference:

    void F(int a[20]) {}

    int main(void) {
    int x[20];
    F(x);
    }

    Although the type of 'a' inside 'F' will be int* rather than int(*)[20].

    So if you have these structs and declarations :

    struct small { uint16_t a; uint16_t b; };
    struct big { uint32_t xs[10]; };

    struct small foos(struct small y);
    struct big foob(struct big y);

    Then compilers will typically implement "x = foos(y)" as though it were:

        extern uint32_t foos(uint32_t ab);
        uint32_t _1 = foos(y.a << 16) | (y.b);
        struct small x = { _1 >> 16, _1 & 0xffff };

    And they will typically implement "x = foosb(y)" as though it were:

        extern void foob(struct big * ret, const struct big * xs);
        struct big x;
        foob(&x, &y);

    From what I've seen, structs that are not small enough to be passed in registers, are copied to a temporary, and the address of that temporary
    is passed.

    This seems to be the case even when the struct param is marked 'const'.

    (My compiler won't create a copy when the parameter is 'const'. I
    assumed that was how gcc did it; I was wrong.)

    This is for Win64 ABI, however an ABI will only say they are passed by reference; it will not stipulate making a copy. That is up to the
    language implementation.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Ben Bacarisse on Tue Jul 9 17:29:57 2024
    On 09/07/2024 16:58, Ben Bacarisse wrote:
    bart <bc@freeuk.com> writes:

    Arrays are passed by reference:

    void F(int a[20]) {}

    int main(void) {
    int x[20];
    F(x);
    }

    This is the sort of thing that bad tutors say to students so that they
    never learn C properly. All parameter passing in C is by value. All of
    it. You just have to know (a) what the syntax means and (b) what values
    get passed.

    The end result is that a parameter declared with value-array syntax is
    passed using a reference rather than by value.

    And it does so because the language says, not because the ABI requires
    it. A 2-byte array is also passed by reference.


    void F(int a[20]) ... declares a to be of type int *. Feel free to rail about that as much as you like but that is what that syntax means.

    The x in F(x) is converted to a pointer to x[0] since the x is not an
    operand of &, sizeof etc. F(x) passes a pointer by value. F receives a pointer value in a.

    Although the type of 'a' inside 'F' will be int* rather than
    int(*)[20].

    No. a is of type int *.

    And that is different 'int*' how, about from having an extra space which
    C says is not signigicant in this context?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Michael S on Tue Jul 9 20:03:04 2024
    On 09/07/2024 17:54, Michael S wrote:
    On Tue, 9 Jul 2024 16:37:31 +0200
    David Brown <david.brown@hesbynett.no> wrote:

    On 06/07/2024 21:33, BGB wrote:

    In my compiler (BGBCC), such an internal pointer exists for arrays
    and structures in the local stack frame.

    No separate pointer exists inside of things like structs, where, as
    can be noted, the array exists at a fixed size and location.


    So, eg:
      void Foo()
      {
         int a[100];
         ...
      }

    There is both the space for 100 integers reserved in the stack
    frame, and a variable 'a' which exists as an implicit pointer to
    that location.


    But, say:
      void Foo()
      {
         int a[8192];
         ...
      }

    There is no space reserved on the stack, and the array is instead
    allocated dynamically (from the heap). In this case, the "a"
    variable exists as a pointer to that location in memory.

    Similar treatment also applies to structs.



    The C standard does not require a stack or say how local data is
    implemented, it just gives rules for the scope and lifetime of
    locals. However, I would be surprised and shocked to find a compiler
    I was using allocate local data on the heap in some manner. If I
    have an array as local data, it is with the expectation that it is
    allocated and freed trivially (an add or subtract to the stack
    pointer, typically combined with any other stack frame). If I want
    something on the heap, I will use malloc and put it on the heap.

    Such an implementation as yours is not, I think, against the C
    standards
    - but IMHO it is very much against C philosophy.


    I wouldn't mind if my ABI/compiler allocates all big local objects
    together with all local VLA either on separate secondary stack or even
    on heap. Such strategy will improve locality of reference for primary
    stack. So, despite higher management overhead, it could sometimes be advantageous even for performance.

    I would mind a great deal if it used a heap. Heaps support out-of-order allocation and deallocation - this can mean significant overhead in
    space and very non-deterministic behaviour. A secondary stack would be
    a different matter entirely, and is much more reasonable. There are
    some kinds of code where a secondary stack would be a useful structure.
    (I've worked with microcontrollers where there was a separate return
    stack and data stack.) But I'd want to know about it!

    It makes a lot more sense to me to simply have compiler flags that
    complain if you have stack objects that are too big. (How much is "too
    big" will vary depending on your target.)

    The main advantage however is not performance, but reducing the
    severity of damage caused by buffer overrun bugs.


    There are much better ways to deal with that than randomly using a heap
    for some objects and not others.

    Although if "security" is a primary concern then one would want
    stricter policy than the one outlined above. I.e. not only big objects,
    but small object as well should be allocated away from primary stack as
    long as their address participate in pointer arithmetic that can't be
    proven safe by static analysis.


    Just use a more appropriate language that has better run-time checking
    when this is your prime concern. And if you are using C, keep things
    small and simple so that the programmer, the static analysis tools, and
    the code reviewer can all confirm that there are no buffer overflows.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to BGB on Tue Jul 9 16:22:41 2024
    On 7/9/24 14:55, BGB wrote:
    ...
    The pass by reference, in this context, was referring to the ABI, not
    to C itself.

    It looks from C's POV as-if it were by-value.


    Which it is, depends on if one is looking at things at the language
    level, ABI level, or IR level, ...

    The C standard doesn't explicitly specify pass by value, or pass by
    reference, or anything other passing mechanism. What it does say is what
    a programmer needs to know to use the passing mechanism. It says that
    the value of a function parameter that is seen by the code inside that
    function is a copy of the value passed as an argument to the function.
    The copy can be modified without changing the original. When a C
    function's declaration looks as though it takes an array as an argument,
    what that declaration actually means is that it takes a pointer value as
    an argument, and it is a copy of that pointer's value which is seen
    inside the function, and can be modified. The memory it points at is the
    same as the memory pointed at by the corresponding argument.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Ben Bacarisse on Tue Jul 9 21:28:49 2024
    On 09/07/2024 18:22, Ben Bacarisse wrote:
    bart <bc@freeuk.com> writes:

    On 09/07/2024 16:58, Ben Bacarisse wrote:
    bart <bc@freeuk.com> writes:

    Arrays are passed by reference:

    void F(int a[20]) {}

    int main(void) {
    int x[20];
    F(x);
    }
    This is the sort of thing that bad tutors say to students so that they
    never learn C properly. All parameter passing in C is by value. All of >>> it. You just have to know (a) what the syntax means and (b) what values >>> get passed.

    The end result is that a parameter declared with value-array syntax is
    passed using a reference rather than by value.

    And it does so because the language says, not because the ABI requires
    it. A 2-byte array is also passed by reference.

    An address value is passed by value. C has only one parameter passing mechanism. You can spin it as much as you like, but C's parameter
    passing is simple to understand, provided learner tune out voices like
    yours.

    Little about C's type system is simple. You're doing your students a
    disservice if you try and hide all the quirks.

    There's a discontinuity in the type system when it comes to parameter
    types (T is is non-array typedef; U is an array typedef):

    Non-Param Same type as Param

    T a T a
    T a[10] T *a
    T a[10][20] T (*a)[20]

    U a U *a
    U a[10] U (*a)[?] ? is the length of U's array
    U A[10][20] U (*a)[20][?]

    Any top-level array type in a parameter (that is, its description in
    English starts with 'array of'), is converted to a pointer type (now
    starts with 'pointer to'). But the lower levels are not affected.

    If you were to try the same exercise in one of my languages, if would be
    a smaller table:

    Param/Non-Param,
    T is array/non-array

    T a
    T a[10]
    T a[10][20]

    Sorry, I missed what you wrote. I don't know why even brought up int
    (*)[20] but I thought you were saying that was the type of a.

    int(*)[20] would be the type of 'a' inside my function, IF it was
    properly passed by value instead of some weird combination.

    If I define this function in one of my languages, where '&' means pass-by-reference:

    proc f([20]int32 &A)=
    end

    and tell it to transpile to C, it produces (minus the name decoration):

    static void f(i32 (*a)[20]) {
    }

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Wed Jul 10 00:04:40 2024
    On 09/07/2024 23:32, Kaz Kylheku wrote:
    On 2024-07-09, bart <bc@freeuk.com> wrote:
    On 09/07/2024 16:58, Ben Bacarisse wrote:
    bart <bc@freeuk.com> writes:

    Arrays are passed by reference:

    void F(int a[20]) {}

    int main(void) {
    int x[20];
    F(x);
    }

    This is the sort of thing that bad tutors say to students so that they
    never learn C properly. All parameter passing in C is by value. All of >>> it. You just have to know (a) what the syntax means and (b) what values >>> get passed.

    The end result is that a parameter declared with value-array syntax is
    passed using a reference rather than by value.

    And it does so because the language says, not because the ABI requires
    it. A 2-byte array is also passed by reference.

    In C, arrays are not passed to functions, period.

    Arrays can be passed by explicit reference:

    void F(int(*A)[20]) {
    printf("%zu\n", sizeof(*A)/sizeof((*A)[0])); // shows 20
    }


    That can be called like this:

    int a[20];
    F(&a);


    Here, an actual array type is involved, which is passed by an actual
    pointer to array, not a pointer to its elements.

    Therefore ABIs do not say anything about array parameters,
    (or if they do, it's not in relation to C).

    That makes a change; most people think that C invented ABIs, which were
    created in its image.

    The Win64 ABI talks about aggregate types.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Keith Thompson on Wed Jul 10 01:38:23 2024
    On 10/07/2024 00:50, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:
    [...]
    Arrays can be passed by explicit reference:

    void F(int(*A)[20]) {
    printf("%zu\n", sizeof(*A)/sizeof((*A)[0])); // shows 20
    }

    That can be called like this:

    int a[20];
    F(&a);

    On the language level, that's passing a pointer to an array object.
    The pointer itself is passed by value. Passing a pointer to an array
    is conceptually no different than passing a pointer to anything else.

    I was replying to:

    "In C, arrays are not passed to functions, period."

    C has pass-by-reference in exactly the same way that it has
    linked lists. It has neither as a language feature, but both can
    be emulated using pointers. And you can't really understand how
    C handles arrays if you start by asserting that they're "passed
    by reference".

    This is how I can pass arrays by reference in my language:

    proc F([]int &A) =
    A[2]:=777
    end

    proc main=
    static[]int A = (10,20,30)

    println A[2] # shows 20
    F(A)
    println A[2] # shows 777
    end

    Without the feature I'd need to use 'ref[]int A' and call as F(&A) (the
    extra deref inside F is added by the commpiler).

    This is how you might do the same thing in C:

    void F(int A[]) {
    A[1]=777;
    }

    int main(void) {
    int A[] = {10,20,30};

    printf("%d\n", A[1]); // shows 20
    F(A);
    printf("%d\n", A[1]); // shows 777
    }

    To get the 777 output involves somehow passing the array by reference.

    But notice how C gives exactly the same result as my code that used by-reference, even though:

    * C "doesn't pass arrays by reference"
    * C's F function uses the same parameter type (only & is missing; maybe
    by-ref is implicit...)
    * No explicit de-ref is needed inside F
    * No explicit address-of is needed when calling F

    So C behaves exactly as though it passes arrays by-reference, and yet it doesn't have pass-by-reference. In fact, C does it without even needing
    to be told!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Lawrence D'Oliveiro on Wed Jul 10 01:22:47 2024
    On 7/10/24 00:29, Lawrence D'Oliveiro wrote:
    On Sat, 06 Jul 2024 15:38:14 -0700, Keith Thompson wrote:
    ...
    If you evaluate the expression `array_object` in most contexts, it's
    implicitly converted to a pointer *value*, pointing to the 0th element
    of the array object. There is still no implicit pointer object.

    The OP said “pointer”, not “pointer object” or “pointer value”. Not sure
    what hair you are trying to split here.

    BGB referred to it as an "implicitly declared" pointer. You can declare
    objects in C, but not values. An object has a location in addressable
    memory where it is stored, a value need not exist anywhere in
    addressable memory.

    In every implementation that I'm sufficiently familiar with, no memory
    is set aside to store such a pointer object. A pointer value is formed,
    if needed, by taking an address stored in a register and adding an object-specific offset; the address stored in that address is of a group
    of objects with the same scope, it only incidentally happens to also be
    the address of one of those objects. And this is just as true of
    pointers to individual objects as it is of pointers to the first element
    of an array. That's convenient, since for the purposes of pointer
    arithmetic, C considers single objects to be equivalent to a 1-element
    array of that object's type.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to bart on Wed Jul 10 02:38:39 2024
    On Sat, 6 Jul 2024 19:53:56 +0100, bart wrote:

    On 06/07/2024 19:28, James Kuyper wrote:

    ... an expression that has type "array of type" is
    converted to an expression with type "pointer to type" that points to
    the initial element of the array object ..." (6.3.2.1p3).

    This is really, really pedantic. Even gcc doesn't get it right in that
    case, because if I try and compile this:

    int a, b> a[b];

    it says:

    error: subscripted value is neither array nor pointer nor vector

    There is no expression that has the type "array of type" in the above
    code. How is that relevant to what I wrote?

    For the subscript operator:
    "One of the expressions shall have type "pointer to complete object
    type", the other expression shall have integer type," (6.5.2.1p1)

    Neither a nor b has the type "pointer to complete object type". Both a
    and b have the type 'int'. How did you expect that code to be meaningful?

    Note that a[&b] would be valid, since &b is treated for this purpose as
    a pointer to the first element of a 1-element array.

    Note that the standard doesn't mandate which expression have the pointer
    type; that's because a[&b] is defined as *(a + &b), and you can add a
    pointer to an integer in either order, so you can subscript an array as array[5] or 5[array].

    'Subscripting' I think we can agree is the same thing as 'indexing':
    what those funny square brackets do.

    I can agree that subscripting is indeed what those square brackets do.
    The C standard never mentions indexing, but I do agree that there is a correspondence. However, if you derive any conclusions from that
    correspondence that contradict what the C standard says about
    subscripting, those conclusions are invalid.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Lawrence D'Oliveiro on Wed Jul 10 03:07:38 2024
    On 7/10/24 01:55, Lawrence D'Oliveiro wrote:
    On Sat, 06 Jul 2024 15:23:47 -0700, Keith Thompson wrote:

    Lawrence D'Oliveiro <ldo@nz.invalid> writes:

    On Fri, 05 Jul 2024 11:46:38 -0700, Keith Thompson wrote:

    No, arrays are not pointers.

    Except array indexing is designed to be indistinguishable from pointer
    arithmetic.

    No, arrays are not pointers.

    Can you point out any situation where this construct

    &a[b]

    might be valid, but this

    (a + b)

    (with the same declarations of “a” and “b”) might not?

    &a[b] is defined as equivalent to &*(a+b), and which in turn is
    equivalent to (a+b), so of course that's not a problem. The fact that an
    array is not a pointer is not visible in that context, but is visible in several others:

    #include <stdio.h>

    int main(void)
    {
    char *pointer = "array 1";
    char array[] = "array 2";
    // "array 2" is an lvalue of array type, and in most contexts,
    // gets implicitly converted into a pointer to the first character
    // of that array, in this context it does not, and this:
    // char array[] = pointer;
    // would therefore be a constraint violation;

    //These aren't guaranteed to be unequal, but they are allowed to
    // be, and are unlikely to be equal on most implementations.
    printf("%zu != %zu", sizeof array, sizeof pointer);
    // Even if the types did have the same size, you can demonstrate
    // that they are different using _Generic()


    // The following declarations declare objects of different types.
    typeof(array) newarray;
    typeof(pointer) newpointer;

    printf("%zu != %zu", sizeof array, sizeof pointer);

    // The following two statements would be constraint violations
    // if you swapped "pointer" with "array":
    char **ppchar = &pointer;
    char (*parray)[8] = &array;
    }

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Lawrence D'Oliveiro on Wed Jul 10 03:16:18 2024
    On 7/9/24 20:57, Lawrence D'Oliveiro wrote:
    On Sat, 6 Jul 2024 21:34:29 -0400, James Kuyper wrote:

    On many platforms, if _Alignof(type) is less than the word size, then a
    C pointer to that type is implemented as the combination of the machine
    address of the correct word, combined with an offset within that word of
    the first byte of that object.

    Which is a terrific idea, except it cannot be carried to its logical conclusion (addressing of arbitrarily-aligned dynamically-defined
    bitfields) because of the requirement in the C spec that the size of a “byte” be at least 8 bits.

    I will grant you that I failed to mention the fact that this is a
    feasible way of implementing C only on platforms with a word size of 16
    bits or longer. I assumed that would be obvious - I apologize for not anticipating that it might not be.

    On platforms where the word size is smaller than 8 bits, the opposite
    strategy applies: instead of storing multiple bytes in a word, an implementation would have to use multiple words to store a byte.

    I know that there's a fair number of implementations that use the first approach, I have no idea whether there are any that use the second
    approach. It should be feasible, but it would probably be more trouble
    than it's worth. The key point is that the addressable unit in C can be emulated - it needn't bear any particularly close relationship to the
    hardware addressable unit.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Keith Thompson on Wed Jul 10 11:12:09 2024
    On 10/07/2024 02:18, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:
    On 10/07/2024 00:50, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:
    [...]
    Arrays can be passed by explicit reference:

    void F(int(*A)[20]) {
    printf("%zu\n", sizeof(*A)/sizeof((*A)[0])); // shows 20
    }

    That can be called like this:

    int a[20];
    F(&a);
    On the language level, that's passing a pointer to an array object.
    The pointer itself is passed by value. Passing a pointer to an array
    is conceptually no different than passing a pointer to anything else.

    I was replying to:

    "In C, arrays are not passed to functions, period."

    Which is a correct statement.

    [...]

    But notice how C gives exactly the same result as my code that used
    by-reference, even though:

    * C "doesn't pass arrays by reference"
    * C's F function uses the same parameter type (only & is missing; maybe
    by-ref is implicit...)
    * No explicit de-ref is needed inside F
    * No explicit address-of is needed when calling F

    Right. The C rules that make all this possible have been explained
    to you many times. I won't waste my time explaining them to you
    again. If you were interested in learning, you would read section
    6 of the comp.lang.c FAQ.

    Yes, some of C's rules make it *look like* arrays are passed by
    reference.

    So C behaves exactly as though it passes arrays by-reference, and yet
    it doesn't have pass-by-reference. In fact, C does it without even
    needing to be told!

    If you actually believed that C has pass-by-reference for arrays, it
    would indicate that you don't understand C. But you're only pretending
    to believe it.

    If C had pass-by-reference for arrays, then presumably you could obtain
    the size of an array parameter by applying sizeof to its name,


    That's what my example did. But only if the array has a specific bound
    in the parameter type, not if it's unbounded, since (1) a function can
    be passed arrays of different sizes; (2) C arrays don't normally contain
    their length.

    and you
    could get the address of an array parameter by applying unary "&" to its name. I know why that doesn't work. And so do you.

    The by-ref in my language has a "&" has part of the parameter type;
    which would be cancelled by the "&" in "&a", so it would print the value
    of the passed pointer - the address of the array in the caller.

    The C version doesn't need that "&" in the parameter (it can't be
    written), so it doesn't needthe "&" in "&a". Here then you just write
    "a" , and that gives the address of the array in the caller.

    What I'm getting at, is that there is no appreciable difference between
    arrays passed by-reference in my language, and arrays as they are
    idiomatically passed in C.

    So if arrays aren't passed by value in C, and they aren't passed by
    reference, then how the hell ARE they passed?! Pretend you have to give
    a quick answer to a child; or an alien.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to James Kuyper on Wed Jul 10 10:58:30 2024
    On 10/07/2024 07:38, James Kuyper wrote:
    On Sat, 6 Jul 2024 19:53:56 +0100, bart wrote:

    On 06/07/2024 19:28, James Kuyper wrote:

    ... an expression that has type "array of type" is
    converted to an expression with type "pointer to type" that points to
    the initial element of the array object ..." (6.3.2.1p3).

    This is really, really pedantic. Even gcc doesn't get it right in that
    case, because if I try and compile this:

    int a, b> a[b];

    it says:

    error: subscripted value is neither array nor pointer nor vector

    There is no expression that has the type "array of type" in the above
    code. How is that relevant to what I wrote?

    For the subscript operator:
    "One of the expressions shall have type "pointer to complete object
    type", the other expression shall have integer type," (6.5.2.1p1)

    Neither a nor b has the type "pointer to complete object type". Both a
    and b have the type 'int'. How did you expect that code to be meaningful?

    Note that a[&b] would be valid, since &b is treated for this purpose as
    a pointer to the first element of a 1-element array.

    Note that the standard doesn't mandate which expression have the pointer type; that's because a[&b] is defined as *(a + &b), and you can add a
    pointer to an integer in either order, so you can subscript an array as array[5] or 5[array].

    'Subscripting' I think we can agree is the same thing as 'indexing':
    what those funny square brackets do.

    I can agree that subscripting is indeed what those square brackets do.
    The C standard never mentions indexing, but I do agree that there is a correspondence. However, if you derive any conclusions from that correspondence that contradict what the C standard says about
    subscripting, those conclusions are invalid.

    My post was about what gcc says. Your point I believe was about the fact
    that indexing / subscripting can only be meaningful with a pointer type, according to C, and not an 'array' or 'vector'.

    But gcc does mention subscripting in connection with arrays and vectors.

    So /my/ point is that sometimes it is helpful not to be so strict if it
    helps understanding.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Ben Bacarisse on Wed Jul 10 13:51:02 2024
    On 10/07/2024 00:35, Ben Bacarisse wrote:
    bart <bc@freeuk.com> writes:

    On 09/07/2024 18:22, Ben Bacarisse wrote:
    bart <bc@freeuk.com> writes:

    On 09/07/2024 16:58, Ben Bacarisse wrote:
    bart <bc@freeuk.com> writes:

    Arrays are passed by reference:

    void F(int a[20]) {}

    int main(void) {
    int x[20];
    F(x);
    }
    This is the sort of thing that bad tutors say to students so that they >>>>> never learn C properly. All parameter passing in C is by value. All of >>>>> it. You just have to know (a) what the syntax means and (b) what values >>>>> get passed.

    The end result is that a parameter declared with value-array syntax is >>>> passed using a reference rather than by value.

    And it does so because the language says, not because the ABI requires >>>> it. A 2-byte array is also passed by reference.
    An address value is passed by value. C has only one parameter passing
    mechanism. You can spin it as much as you like, but C's parameter
    passing is simple to understand, provided learner tune out voices like
    yours.

    Little about C's type system is simple.

    Parameter passing is relatively simple though since there is only one mechanism -- pass by value.

    Except when it comes to arrays. Obviously arrays aren't passed by value,
    but that's because of X and Y.

    What would be genuinely simple is:

    void F(T x) {}
    void G(T* y) {}

    where an object of type T is always passed by value, and that of type T*
    always passes a pointer to the value. Here, T represents exactly the
    same type as when it used outside a parameter list:

    T a;
    T* p;

    but only in the context of C is it necessary to mention that, since it
    C, that may not be the case.

    (The mechanisms of the underlying ABI do not affect this; they just make implementation harder.)

    You're doing your students a
    disservice if you try and hide all the quirks.

    If. Always with the if. There are lots of things I don't do that would
    be doing my students a disservice were I to do them. Beautiful spin!


    Have a look at forums like the Reddit one on C programming, to see the
    kinds of things that people learning C get confused about. Lots of them
    are to do with the funny quirks of C, and a big proportion are due to
    how arrays and pointers are so intertwined in that language.

    Well, C can't be fixed. But what would you do: explain the flaws in the language, the discontinuities, or brush them under the carpet?

    Or maybe explain the quirks, but pretend they are by design?

    What I find especially wonderful is that every time you see a pointer
    type like T*, C gives you an extra array dimension on top, whether or
    not T actually points to the first element of an array.

    So you never know whether T* is intended to represent a 0- or
    1-dimensioned object, or a 1- or 2-dimensioned one.

    And what I find frustrating is that people learning C get the idea that
    all this quirkiness goes hand-in-hand with lower-level programming.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Ben Bacarisse on Wed Jul 10 15:49:11 2024
    On 10/07/2024 14:32, Ben Bacarisse wrote:
    bart <bc@freeuk.com> writes:

    On 10/07/2024 00:35, Ben Bacarisse wrote:
    bart <bc@freeuk.com> writes:

    On 09/07/2024 18:22, Ben Bacarisse wrote:
    bart <bc@freeuk.com> writes:

    On 09/07/2024 16:58, Ben Bacarisse wrote:
    bart <bc@freeuk.com> writes:

    Arrays are passed by reference:

    void F(int a[20]) {}

    int main(void) {
    int x[20];
    F(x);
    }
    This is the sort of thing that bad tutors say to students so that they >>>>>>> never learn C properly. All parameter passing in C is by value. All of
    it. You just have to know (a) what the syntax means and (b) what values
    get passed.

    The end result is that a parameter declared with value-array syntax is >>>>>> passed using a reference rather than by value.

    And it does so because the language says, not because the ABI requires >>>>>> it. A 2-byte array is also passed by reference.
    An address value is passed by value. C has only one parameter passing >>>>> mechanism. You can spin it as much as you like, but C's parameter
    passing is simple to understand, provided learner tune out voices like >>>>> yours.

    Little about C's type system is simple.
    Parameter passing is relatively simple though since there is only one
    mechanism -- pass by value.

    Except when it comes to arrays.

    The oddity is that, in C, one can't pass arrays to functions at all.
    That is one of the quirks that people learning C need to learn. It does
    not alter the fact that there is only parameter passing mechanism -- by value.

    Your plan, of course, is to take that one place where C is relatively
    simple

    It is not that simple. It is confusing. It is error prone.

    I earlier asked this:

    "So if arrays aren't passed by value in C, and they aren't passed by
    reference, then how the hell ARE they passed?!"

    I haven't had a reply yet. I still consider arrays in C to be 'passed'
    by a mechanism which is near-indistinguishable from actual
    pass-by-reference.

    If somebody had proposed adding pass-by-reference for arrays, you'd say
    C doesn't need it, because whatever benefits it might have you, C
    already has!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Janis Papanagnou on Wed Jul 10 16:40:29 2024
    On 10/07/2024 15:54, Janis Papanagnou wrote:

    Values passed (including values of pointers [used for arrays]) are
    handled (in the functions) as copies and cannot change the original
    entities (values or dereferenced objects) in the calling environment.

    To make it possible to change entities in the calling environment
    in "C" you have to implement the necessary indirection by pointers.


    You don't have to do anything at all:

    #include <stdio.h>
    typedef unsigned char byte;
    typedef byte vector[4];

    void F(vector a) {
    a[0]+=3;
    a[1]+=3;
    }

    int main(void) {
    vector v = {10,20,30,40};

    printf("%d %d %d %d\n", v[0], v[1], v[2], v[3]); // 10 20 30 40
    F(v);
    printf("%d %d %d %d\n", v[0], v[1], v[2], v[3]); // 13 23 30 49
    }

    Here it looks superficially as though 'v' is passed by value (and it is
    of a size that the ABI /would/ pass by value), yet F changes its
    caller's data, perhaps unintentionally.

    Your insistence is amazing.
    /I/ am amazed at everyone's insistence that there is nothing remarkable
    about this, and that it is nothing at all like pass-by-reference.

    So, how do I write F in C so that the caller's data is unchanged?

    Sure, true pass-by-reference has some extra properties, but if I wanted
    to duplicate the behaviour of the above in my language, I have to use pass-by-reference.

    In C you get that behaviour anyway (possibly to the surprise of many),
    in a language which only has pass-by-value, and without needing explicit pointers.

    That really is remarkable. And not unsafe at all!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to bart on Wed Jul 10 11:54:44 2024
    On 10.07.2024 16:49, bart wrote:
    ...
    "So if arrays aren't passed by value in C, and they aren't passed by reference, then how the hell ARE they passed?!"

    The problem with that question is the same as the problem with the
    question "How are Justices of the US Supreme Court elected?". They
    aren't elected, so the question cannot be answered. Arrays cannot be
    passed in C, so the question of how they are passed also cannot be answered.

    You can pass a pointer to the start of an array or a pointer to the
    whole array; either way, the pointer is passed by value. You could also
    pass a struct containing an array; that struct is passed by value.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to James Kuyper on Wed Jul 10 17:48:57 2024
    On 10/07/2024 16:54, James Kuyper wrote:
    On 10.07.2024 16:49, bart wrote:
    ...
    "So if arrays aren't passed by value in C, and they aren't passed by
    reference, then how the hell ARE they passed?!"

    The problem with that question is the same as the problem with the
    question "How are Justices of the US Supreme Court elected?". They
    aren't elected, so the question cannot be answered. Arrays cannot be
    passed in C, so the question of how they are passed also cannot be answered.

    You can pass a pointer to the start of an array or a pointer to the
    whole array; either way,

    How is that interestingly different from pass-by-reference?

    Which as you know involves a pointer to the value of an object, so the
    values are not passed, only the value of the pointer.

    (You may also know that every language will use pass-by-value for
    everything if you delve deeply enough.)

    In C, if you have this char[4] array at this address:

    01003F8 65 66 67 00

    and try to pass it to a function defined with a char[4] argument, it
    will end up passing the address 01003F8.

    If I pass the same array in a similar language but using explicit pass-by-reference, it will also pass the address 01003F8.

    So, if you were to examine the machine code of such a program which was generated from one of those two languages, could you tell whether it was
    from the one with true pass-by-reference, or from C which only has pass-by-value?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Tim Rentsch on Wed Jul 10 18:30:54 2024
    On 10/07/2024 16:48, Tim Rentsch wrote:
    bart <bc@freeuk.com> writes:

    I earlier asked this:

    "So if arrays aren't passed by value in C, and they aren't passed
    by reference, then how the hell ARE they passed?!"

    They aren't. C allows lots of things to be passed as an argument
    to a function: several varieties of numeric values, structs,
    unions, and pointers, including both pointers to object types and
    pointers to function types. C does not have a way for a function
    to take an argument that is either an array or a function. There
    is a way to take pointers to those things, but not the things
    themselves. Arrays and functions are second-class values in C.

    That's a good point. It's not just arrays that can't be passed by value (because the language says so) but also functions (because its not
    meaningful).

    Yet, although pointers to arrays and function can be passed (without
    even doing anything special like using &), you are not allowed to say
    that anything is passed by reference in C!

    The automatic conversion to a pointer, which is also a feature of true pass-by-reference, doesn't count.

    Not needing an explicit deref inside the callee (another characteristic
    of pass-by-reference) doesn't count either.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Michael S on Wed Jul 10 20:04:35 2024
    On 10/07/2024 19:39, Michael S wrote:
    On Wed, 10 Jul 2024 18:30:54 +0100
    bart <bc@freeuk.com> wrote:

    On 10/07/2024 16:48, Tim Rentsch wrote:
    bart <bc@freeuk.com> writes:

    I earlier asked this:

    "So if arrays aren't passed by value in C, and they aren't passed
    by reference, then how the hell ARE they passed?!"

    They aren't. C allows lots of things to be passed as an argument
    to a function: several varieties of numeric values, structs,
    unions, and pointers, including both pointers to object types and
    pointers to function types. C does not have a way for a function
    to take an argument that is either an array or a function. There
    is a way to take pointers to those things, but not the things
    themselves. Arrays and functions are second-class values in C.

    That's a good point. It's not just arrays that can't be passed by
    value (because the language says so) but also functions (because its
    not meaningful).

    Yet, although pointers to arrays and function can be passed (without
    even doing anything special like using &), you are not allowed to say
    that anything is passed by reference in C!

    The automatic conversion to a pointer, which is also a feature of
    true pass-by-reference, doesn't count.

    Not needing an explicit deref inside the callee (another
    characteristic of pass-by-reference) doesn't count either.

    It does not count, because automatic conversion to a pointer is not
    something that happens only during parameter passing. For arrays, it
    happens in all contexts except sizeof(). For functions, it happens in
    all contexts except function call. Or, may be, including function call,
    in this case (but not in case of arrays) it depends on point of view.


    Suppose that was to happen in all contexts, not just for arrays and
    functions, but for all types.

    That means that if A, B, C were numbers, then any call such as F(A, B,
    C) would pass the addresses of the numbers rather than their values.

    According to what people have said, C would STILL be a language that
    passed thing by value, and never by automatic reference.

    Yet in my scenario that now sounds ludicrous.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Michael S on Wed Jul 10 21:28:15 2024
    On 10/07/2024 19:14, Michael S wrote:
    On Wed, 10 Jul 2024 08:48:05 -0700
    Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:

    bart <bc@freeuk.com> writes:

    I earlier asked this:

    "So if arrays aren't passed by value in C, and they aren't passed
    by reference, then how the hell ARE they passed?!"

    They aren't. C allows lots of things to be passed as an argument
    to a function: several varieties of numeric values, structs,
    unions, and pointers, including both pointers to object types and
    pointers to function types. C does not have a way for a function
    to take an argument that is either an array or a function. There
    is a way to take pointers to those things, but not the things
    themselves. Arrays and functions are second-class values in C.


    I'd like to see an example of the language that permits ahead-of-time compilation and has functions as first-class values.


    Haskell is the first the comes to mind for me, but you could pick any
    compiled functional programming language.

    I am by no means a Haskell expert, and I am not at all familiar with the
    way the language is compiled. But it is quite clear that it is an
    example of a language that has functions as first-class objects, and
    which is ahead-of-time compiled. The example below defines an
    int-to-int function "doubler", and also a function-to-function function "doTwice", and a function quadrupler that is defined as the result of
    applying the higher-order function doTwice to doubler. These are all
    compiled to assembly.

    <https://godbolt.org/z/Tb7hGYsdv>


    module Example where

    doubler :: Int -> Int
    doubler x = 2 * x

    doTwice :: (Int -> Int) -> (Int -> Int)
    doTwice f x = f (f x)

    quadrupler = doTwice doubler

    shouldBeEighty = quadrupler 20



    You can write much the same in C++ using lambdas (which are objects and
    can be passed around and returned as such) and templates (which are
    needed because the type of lambdas is hidden). Unfortunately, this also
    means that the functions don't get individually generated functions in assembly:

    <https://godbolt.org/z/KvPWz3n8z>

    auto doubler = [](int x) -> int { return 2 * x; };

    auto doTwice = [](auto f) -> auto
    {
    return [f](int x) -> int { return f(f(x)); };
    };

    auto quadrupler = doTwice(doubler);

    auto shouldBeEiqhty = quadrupler(20);

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Wed Jul 10 21:46:11 2024
    On 10/07/2024 17:40, bart wrote:
    On 10/07/2024 15:54, Janis Papanagnou wrote:

    Values passed (including values of pointers [used for arrays]) are
    handled (in the functions) as copies and cannot change the original
    entities (values or dereferenced objects) in the calling environment.

    To make it possible to change entities in the calling environment
    in "C" you have to implement the necessary indirection by pointers.


    Your insistence is amazing.
    /I/ am amazed at everyone's insistence that there is nothing remarkable
    about this, and that it is nothing at all like pass-by-reference.

    Nobody has said anything close to "it is nothing at all like pass-by-reference". Many people have made it clear that using pointers
    - explicitly, or implicitly via the language's automatic decay of array
    and function expressions to pointers in certain contexts, can be used to
    give a similar effect to passing by reference. Indeed, it is precisely
    the reason C does not have "pass by reference" - it is not needed, as
    you can conveniently use pointers and "pass by value".


    So, how do I write F in C so that the caller's data is unchanged?

    You use a type that you /can/ pass as an argument in C. You can't pass
    arrays to functions, or return them. A convenient method is to wrap the
    array in a struct :

    typedef struct {
    uint8_t data[4];
    } vector;

    If you want to pass around copies of arrays without a fixed predefined
    size in C, you need to do it all manually - allocate space for copies,
    copy them, pass the pointer to the first element and the size of the
    array. It is one of the things that C does not do for you and you need
    to do manually, just like most memory management.


    Sure, true pass-by-reference has some extra properties, but if I wanted
    to duplicate the behaviour of the above in my language, I have to use pass-by-reference.

    In C you get that behaviour anyway (possibly to the surprise of many),
    in a language which only has pass-by-value, and without needing explicit pointers.

    I really don't think any of this is a surprise to many people who make
    more than a basic effort to learn the language. It is not hard to
    understand, as long as you don't fixate on wrong interpretations of how
    it all works in C.


    That really is remarkable. And not unsafe at all!


    C is a language that requires more responsibility by the programmer for
    things that higher level languages handle automatically. That's the way
    the language is. If you don't understand it, you'll make mistakes.

    (To be clear here - people have been explaining the facts, not opinions.
    Some people here would surely have liked C to be able to pass arrays
    into and out of functions like other C objects.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to BGB on Wed Jul 10 21:52:04 2024
    On 10/07/2024 19:59, BGB wrote:
    On 7/10/2024 2:41 AM, David Brown wrote:
    On 09/07/2024 23:43, BGB wrote:
    On 7/9/2024 3:22 PM, James Kuyper wrote:
    On 7/9/24 14:55, BGB wrote:
    ...
    The pass by reference, in this context, was referring to the ABI, not >>>>> to C itself.

    It looks from C's POV as-if it were by-value.


    Which it is, depends on if one is looking at things at the language
    level, ABI level, or IR level, ...

    The C standard doesn't explicitly specify pass by value, or pass by
    reference, or anything other passing mechanism. What it does say is
    what
    a programmer needs to know to use the passing mechanism. It says that
    the value of a function parameter that is seen by the code inside that >>>> function is a copy of the value passed as an argument to the function. >>>> The copy can be modified without changing the original. When a C
    function's declaration looks as though it takes an array as an
    argument,
    what that declaration actually means is that it takes a pointer
    value as
    an argument, and it is a copy of that pointer's value which is seen
    inside the function, and can be modified. The memory it points at is
    the
    same as the memory pointed at by the corresponding argument.




    We can probably agree that, in C:
       typedef struct Foo_s Foo;
       struct Foo_s {
         int x, y, z, a, b, c;
       };

       int FooFunc(Foo obj)
       {
         obj.z = obj.x + obj.y;
         return(obj.z);
       }

       int main()
       {
         Foo obj;
         int z1;
         obj.x=3;
         obj.y=4;
         obj.z=0;
         z1=FooFunc(obj);
         printf("%d %d\n", obj.z, z1);
       }

    Should print "0 7" regardless of how the structure is passed in the ABI. >>>

    ABI's are irrelevant to how the language is defined and how these
    expressions are evaluated.  ABI's go along with details of the target
    - they can affect implementation-dependent behaviour but no more than
    that.  (A clear example would be that alignment of fundamental types
    would normally be specified by an ABI.)

    So code that does not depend on implementation-dependent behaviour,
    such as your code here, will necessarily give the same results on all
    conforming C implementations.


    These sorts of things may bleed through depending on implementation
    choices (and "optimizations").

    No, they will not.

    If a compiler makes manipulation of the code and gives results contrary
    to the requirements of the language, it is a compiler bug, not an
    optimisation.

    Part of the compiler writing fun is
    trying to get good performance while also not breaking C semantics.

    Sure. But you always aim to err on the side of "slow and correct"
    rather than "fast but possibly wrong". And you read the language
    standards so that you know what "correct" means.


    But, a language more aggressively tuned for performance would have even
    more wonky semantic edge cases than it does already.

    Nope. You can make a wonky, buggy compiler if you like, but don't
    expect anyone to be happy about it.

    (And to be clear - you can, if you want, write a compiler that only
    works for a subset of C, or that works for a language that is only
    similar to C. It could be useful for what you need for your own work,
    but it won't be a conforming C compiler. And if you get this particular
    stuff wrong, it's not just a minor non-conformance - it's a killer bug
    for anyone else who writes "normal" C.)



    Though, one thing is that some of my code (both my Boot ROM) and
    kernel/shell program, has a bunch of sanity check stuff to try to detect
    if ISA features are broken or if the compiler breaks C semantics in
    various ways. But, there are limits to how much coverage there is with
    such checks.



    Though, one possibility being to relax the language such that both "0
    7" and "7 7" are valid possibilities (the latter potentially allowing
    more performance by not needing to make a temporary local copy).
    Though, AFAIK, C doesn't really allow for this.

    It continues to astound me that people who claim to have written C
    compilers themselves - such as you and Bart - regularly show
    misunderstandings or ignorance about things that are clear in the C
    standards and which I would expect any experienced C programmer to know.

    All arguments to function calls in C are passed by value.  This is in
    6.5.2.2p4 - it is a two sentence paragraph that you really ought to
    have read before even considering writing a C compiler.

    Some languages do have pass by reference (or other types of parameter
    passing systems), and would give "7 7".  C is not such a language.
    (And I have never heard of a language that would either result.)


    Note that I did say: "C doesn't really allow for this".

    No, you wrote "AFAIK" ... That says you are not sure.


    As-in, such a tweak would allow things to be slightly faster, but would violate the language rules as it stands.



    An implementation could be clever though and only make local copies
    in cases where the structure is modified by the callee, as in the
    example above.


    A clever implementation would turn the whole of main() into a single
    puts("0 7") call.

    Compliers can generate whatever code they like, as long as the results
    are correct hin the end.


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Keith Thompson on Wed Jul 10 22:40:20 2024
    On 10/07/2024 21:47, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:
    On 10/07/2024 15:54, Janis Papanagnou wrote:
    Values passed (including values of pointers [used for arrays]) are
    handled (in the functions) as copies and cannot change the original
    entities (values or dereferenced objects) in the calling environment.
    To make it possible to change entities in the calling environment
    in "C" you have to implement the necessary indirection by pointers.


    You don't have to do anything at all:

    #include <stdio.h>
    typedef unsigned char byte;
    typedef byte vector[4];

    void F(vector a) {
    a[0]+=3;
    a[1]+=3;
    }

    int main(void) {
    vector v = {10,20,30,40};

    printf("%d %d %d %d\n", v[0], v[1], v[2], v[3]); // 10 20 30 40
    F(v);
    printf("%d %d %d %d\n", v[0], v[1], v[2], v[3]); // 13 23 30 49
    }

    Here it looks superficially as though 'v' is passed by value (and it
    is of a size that the ABI /would/ pass by value), yet F changes its
    caller's data, perhaps unintentionally.

    Here's a modified version of your program:

    ```
    #include <stdio.h>
    typedef unsigned char byte;
    typedef byte vector[4];

    void F(vector a) {
    a[0]+=3;
    a[1]+=3;
    printf("In F\n");
    printf(" a is of type %s\n",
    _Generic(a, vector: "vector", byte*: "byte*"));
    printf(" a = %p\n", (void*)a);
    printf(" a+1 = %p\n", (void*)(a+1));
    printf(" sizeof a = %zu\n", sizeof a);
    printf(" *a = %d\n", *a);
    }

    int main(void) {
    vector v = {10,20,30,40};

    printf("%d %d %d %d\n", v[0], v[1], v[2], v[3]); // 10 20 30 40
    F(v);
    printf("%d %d %d %d\n", v[0], v[1], v[2], v[3]); // 13 23 30 49
    }
    ```

    The output is:
    ```
    10 20 30 40
    In F
    a is of type byte*
    a = 0x7ffdf0158d44
    a+1 = 0x7ffdf0158d45
    sizeof a = 8
    *a = 13
    13 23 30 40
    ```
    (The pointer values will vary.)

    Your insistence is amazing.
    /I/ am amazed at everyone's insistence that there is nothing
    remarkable about this, and that it is nothing at all like
    pass-by-reference.

    So, how do I write F in C so that the caller's data is unchanged?

    Make a copy of the array data if you need to change a local copy, or
    define it as const so the function can't change it, or wrap the array in
    a struct.

    Sure, true pass-by-reference has some extra properties, but if I
    wanted to duplicate the behaviour of the above in my language, I have
    to use pass-by-reference.

    So your language has pass-by-reference. Great. I'm sure we're all very happy for you.

    Apparently so has C! At least, there is enough of a mish-mash between
    by-value, by-reference, and explicit pointers, that it provides most of
    the benefits that true by-reference gives.

    What the effective passing mode is, is unknown. But I think ascribing
    the behaviour of your program above as due to 'pass by value' is
    misleading. The 'value' of what?

    'pass by reference' doesn't quite cover it, but it's a closer match to
    that behaviour.

    In C you get that behaviour anyway (possibly to the surprise of many),
    in a language which only has pass-by-value, and without needing
    explicit pointers.

    Yes.

    That really is remarkable. And not unsafe at all!

    Yes, it's remarkable. Yes, it can be unsafe.

    It's particularly unsafe for someone who doesn't understand it, perhaps because they took your advice.
    Which is more unsafe out these:

    * Someone thinks that that 'v' parameter is passed by-value (because
    everything in C is), and the caller's data is therefore safe. No need to
    use 'const' either.

    * Someone thinks, even eroneously, that the 'v' parameter is passed by reference, so theyt take care not to directly modify it, or they will
    think about using 'const'.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Michael S on Wed Jul 10 18:29:06 2024
    On 7/10/24 14:39, Michael S wrote:
    ...
    ... For functions, it happens in
    all contexts except function call. Or, may be, including function call,

    Actually, it is the latter. In a function call expression:

    "The expression that denotes the called function104) shall have type
    pointer to function ..." (6.5.2.2p1).

    Conveniently,

    "A function designator is an expression that has function type. Except
    when it is the operand of the sizeof operator69) , a typeof operator, or
    the unary & operator, a function designator with type "function
    returning type" is converted to an expression that has type "pointer to function returning type"." (6.3.2.1p4).

    In K&R C, the function designator had to have a function type, and there
    was no implicit conversion to a pointer, but both of those things were
    changed when the language was first standardized.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Ben Bacarisse on Thu Jul 11 01:21:52 2024
    On 11/07/2024 00:01, Ben Bacarisse wrote:
    bart <bc@freeuk.com> writes:

    On 10/07/2024 14:32, Ben Bacarisse wrote:

    I still consider arrays in C to be 'passed' by a
    mechanism which is near-indistinguishable from actual
    pass-by-reference.

    I don't really care how you consider it, but I do care about how you misrepresent the facts in public.

    In another post you said that your language has pass by reference, and
    we also know you have implemented C. Either you are just very confused
    and your language simply has call by value (after all, you think C has
    pass by reference), or you know that pass by reference in your language
    needs something from the implementation that was not needed when you implemented C. I can't decide if you are confused or just lying.


    The way it works in my language is very simple (this is what I do after
    all):

    type T = int

    proc F(T x)= # Pass by value
    println x.typestr
    end

    proc G(ref T x)= # Manual pass-by-reference
    println x^.typestr
    end

    proc H(T &x)= # Auto pass-by-reference
    println x.typestr
    end

    proc main=
    T a

    F(a)
    G(&a)
    H(a)
    end

    I've written 3 functions using pass-by-value, pass-by-value emulating pass-by-reference, and actual pass-by-reference.

    The G function and the call to G show what the compiler has to add when
    it processes function H: address-of ops and derefs. The cost is a single
    & in the parameter list to get that convenience.

    This programs works just the same if T was changed to an array:

    type T = [4]int

    (The output is 3 lots of '[4]i64' instead of 3 lots of 'i64'; 'int' is
    an alias for int64/i64.)

    This is regular and orthogonal, a complete contrast to C even though
    both languages supposedly operate at the same level.

    The behaviour of F, when written in C, is like my F function when T is
    an int (obviously the C won't have '.typestr').

    But when T is an array, its behaviour is more like that of my H function.

    So, my remark about arrays in C being passed by reference is understandable.

    If somebody had proposed adding pass-by-reference for arrays, you'd say C
    doesn't need it, because whatever benefits it might have you, C already
    has!

    I see you are running out of statements to argue against so you have
    started to make up your own. I am sure you have thoroughly refuted this
    made up person in your head.

    Anyone proposing adding pass-by-reference for arrays would be told (by
    me at last) to start by allowing arrays to be passed by value first.
    Why anyone would propose adding pass by reference for a type that can't
    be currently be passed at all is a mystery that only you (as the
    inventor of this person) can know.

    This is my point. Clearly true pass-by-reference for arrays wouldn't add anything in C; it already works alike that! But due to complicated set
    of rules and quirks, which quite different from the type model
    illustrated above.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Michael S on Thu Jul 11 11:04:13 2024
    On 11/07/2024 09:54, Michael S wrote:
    On Thu, 11 Jul 2024 01:21:52 +0100
    bart <bc@freeuk.com> wrote:

    On 11/07/2024 00:01, Ben Bacarisse wrote:
    bart <bc@freeuk.com> writes:

    On 10/07/2024 14:32, Ben Bacarisse wrote:

    I still consider arrays in C to be 'passed' by a
    mechanism which is near-indistinguishable from actual
    pass-by-reference.

    I don't really care how you consider it, but I do care about how you
    misrepresent the facts in public.

    In another post you said that your language has pass by reference,
    and we also know you have implemented C. Either you are just very
    confused and your language simply has call by value (after all, you
    think C has pass by reference), or you know that pass by reference
    in your language needs something from the implementation that was
    not needed when you implemented C. I can't decide if you are
    confused or just lying.


    The way it works in my language is very simple (this is what I do
    after all):

    type T = int

    proc F(T x)= # Pass by value
    println x.typestr
    end

    proc G(ref T x)= # Manual pass-by-reference
    println x^.typestr
    end

    proc H(T &x)= # Auto pass-by-reference
    println x.typestr
    end

    proc main=
    T a

    F(a)
    G(&a)
    H(a)
    end

    I've written 3 functions using pass-by-value, pass-by-value emulating
    pass-by-reference, and actual pass-by-reference.

    The G function and the call to G show what the compiler has to add
    when it processes function H: address-of ops and derefs. The cost is
    a single & in the parameter list to get that convenience.

    This programs works just the same if T was changed to an array:

    type T = [4]int

    (The output is 3 lots of '[4]i64' instead of 3 lots of 'i64'; 'int'
    is an alias for int64/i64.)

    This is regular and orthogonal, a complete contrast to C even though
    both languages supposedly operate at the same level.

    The behaviour of F, when written in C, is like my F function when T
    is an int (obviously the C won't have '.typestr').

    But when T is an array, its behaviour is more like that of my H
    function.

    So, my remark about arrays in C being passed by reference is
    understandable.


    No, it isn't.
    If [in C] it was possible to pass arrays to functions, either by value
    or by reference, then callee would know the length of passed array. As
    it is, callee does not know it.

    The length can be passed in a separate parameter, but then it does not
    have to be the same as an original.

    That's rather specious. In my language (probably in C too), most passed
    arrays are unbounded, allowing the same function to work with arrays of different sizes.

    So that would need a separate Length parameter, even using by-reference.

    In that regard, it's no different from the C: my array by-ref and C's
    alledged by-ref both cannot determine the array length solely from the parameter.

    (In my language, attempts to get the length yields 0, which makes sense
    as the parameter's bounds are zero. In C, it will yield the size of the pointer.)

    But when the array IS bounded, then in C:

    typedef byte vector[4];

    void F(vector a) {
    printf("%zu\n", sizeof(a));
    printf("%zu\n", sizeof(vector));
    }

    The first printf shows 8 (the pointer size); the second shows 4 (the
    array size). So it /can/ access the bounds.

    (For unbounded arrays, my language offers an alternative: slices, which
    contain their length. This is available whatever the calling mechanism.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Michael S on Thu Jul 11 12:20:39 2024
    On 11/07/2024 11:15, Michael S wrote:
    On Thu, 11 Jul 2024 08:41:14 -0000 (UTC)
    Kaz Kylheku <643-408-1753@kylheku.com> wrote:

    On 2024-07-11, Michael S <already5chosen@yahoo.com> wrote:
    On Wed, 10 Jul 2024 21:28:15 +0200
    David Brown <david.brown@hesbynett.no> wrote:

    On 10/07/2024 19:14, Michael S wrote:


    I'd like to see an example of the language that permits
    ahead-of-time compilation and has functions as first-class
    values.

    Haskell is the first the comes to mind for me, but you could pick
    any compiled functional programming language.



    I fail to see a material difference between first class function
    values in Haskell and C++ and first class function pointer values
    in C:


    Thank you.
    Your example confirms my suspicion that the difference between first
    and second class of functions doesn't become material until language
    supports closures.


    I think that is fair to say, yes. The real power comes when there are
    captures - not "doTwice", but "doNTimes".

    Again, this can be done in compiled Haskell, and surely in any compiled functional programming language or compiled language that supports
    functional programming paradigms. (OCaml is another popular choice.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Michael S on Thu Jul 11 11:17:04 2024
    On 11/07/2024 10:15, Michael S wrote:
    On Thu, 11 Jul 2024 08:41:14 -0000 (UTC)
    Kaz Kylheku <643-408-1753@kylheku.com> wrote:

    On 2024-07-11, Michael S <already5chosen@yahoo.com> wrote:

    I fail to see a material difference between first class function
    values in Haskell and C++ and first class function pointer values
    in C:

    int doubler(int x) {
    return x*2;
    }
    int doTwice(int (*foo)(int), int x) {
    return foo(foo(x));
    }
    int quadrupler(int x) {
    return doTwice(doubler, x);
    }

    I am willing to believe that the difference exists, but your
    example is too basic to demonstrate it.

    First class functions could do something like this:

    // multiplier takes a coefficient and returns a pointer to
    // function

    int (*multiplier(int coefficient))(int) {
    // fantasy lambda syntax. Return type int is written after
    // parameter list.
    return lambda(int x) int {
    return coefficient * x;
    }
    }

    int (*doubler)(int) = multiplier(2);

    int x = doubler(42); // x becomes 84

    Even though the lambda is returned out of multiplier, whose execution
    terminates, when the returned function is invoked, it can refer to the
    coefficient, which is captured in a lexical closure.

    With a C-like typedef, we can declutter the definition of mutiplier:

    typedef int (*int_int_fn)(int);

    int_int_fn multiplier(int coefficient) {
    return lambda(int x) int {
    return coefficient * x;
    }
    }


    Thank you.
    Your example confirms my suspicion that the difference between first
    and second class of functions doesn't become material until language
    supports closures.


    There are multiple classes of functions. Closures are part of it but
    there are several versions of closures too:

    Reference Closure
    Normal functions - -
    Function pointers Y -
    Anonymous functions Y -
    Anonymous functions Y 1
    Anonymous functions Y 2
    Anonymous functions Y ...

    C supports the first two, as does my static language. My dynamic
    language supports the first 3 (and can emulate the next couple of levels)

    With closures, they can have different capabilities (as you discover
    when you manage to implement something that runs the simpler Wikipedia examples, then move on to the next which fails).

    For example, in managing to still work after the containing function in
    the above example returns. Or in being able to update that 'coefficient' variable in a still-active enclosing function.

    Whatever you do, there will exist a more elaborate, subtler example that
    will fail. But it will also be one that few can understand or predict
    anyway.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Lawrence D'Oliveiro on Thu Jul 11 12:46:18 2024
    On 11/07/2024 04:51, Lawrence D'Oliveiro wrote:
    On Wed, 10 Jul 2024 03:16:18 -0400, James Kuyper wrote:

    On 7/9/24 20:57, Lawrence D'Oliveiro wrote:

    On Sat, 6 Jul 2024 21:34:29 -0400, James Kuyper wrote:

    On many platforms, if _Alignof(type) is less than the word size, then
    a C pointer to that type is implemented as the combination of the
    machine address of the correct word, combined with an offset within
    that word of the first byte of that object.

    Which is a terrific idea, except it cannot be carried to its logical
    conclusion (addressing of arbitrarily-aligned dynamically-defined
    bitfields) because of the requirement in the C spec that the size of a
    “byte” be at least 8 bits.

    I will grant you that I failed to mention the fact that this is a
    feasible way of implementing C only on platforms with a word size of 16
    bits or longer.

    Don’t you think C needs a better way of handling bitfields than shift-and- mask? Many architectures have bitfield instructions, but C cannot easily
    make use of them without the ability to have arbitrarily-bit-aligned pointers.

    There are pros and cons of different ways to support bitfields in a
    language, and there are certainly aspects of C's bitfields that many
    people think are less than ideal.

    But (good) C compilers have no problem using bitfield instructions on architectures that have these, so that at least is not an issue.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Thu Jul 11 12:41:40 2024
    On 11/07/2024 12:04, bart wrote:
    On 11/07/2024 09:54, Michael S wrote:

    No, it isn't.
    If [in C] it was possible to pass arrays to functions, either by value
    or by reference, then callee would know the length of passed array. As
    it is, callee does not know it.

    The length can be passed in a separate parameter, but then it does not
    have to be the same as an original.

    That's rather specious. In my language (probably in C too), most passed arrays are unbounded, allowing the same function to work with arrays of different sizes.

    So that would need a separate Length parameter, even using by-reference.

    No.

    Like any object, an array has data, and characteristics including the
    type of the elements, and for an array, the length of the array. If you
    pass an object, by reference or by value, the receiver (function
    parameter, or caller if we are talking about return values) gets the characteristics as well as the data.

    In C, if you write code that looks a bit like it is passing an array,
    the object's characteristics don't follow along with it - therefore you
    are not passing the array. If in your language, the characteristics
    also do not follow, as part of the parameter, then your language cannot
    pass arrays either.

    If you need to have a separate manual length parameter, then your
    language is doing the same as C - you are passing a pointer by value
    without the full object information.

    So perhaps your confusion here lies in a misunderstanding of how arrays
    as passed in your own language, and you have carried that confusion over
    to C.


    In that regard, it's no different from the C: my array by-ref and C's alledged by-ref both cannot determine the array length solely from the parameter.

    (In my language, attempts to get the length yields 0, which makes sense
    as the parameter's bounds are zero. In C, it will yield the size of the pointer.)

    But when the array IS bounded, then in C:

      typedef byte vector[4];

      void F(vector a) {
          printf("%zu\n", sizeof(a));
          printf("%zu\n", sizeof(vector));
      }

    The first printf shows 8 (the pointer size); the second shows 4 (the
    array size). So it /can/ access the bounds.

    No, it cannot access the bounds of the "array parameter" here. (I use quotation marks because there are no array parameters in C - just things
    that look a little like them.)

    F takes a pointer to byte as a parameter, not a "vector" or an array of
    bytes or a pointer to an array. The function has no more access to the
    size of a "vector" than it has access to the size of a "float".


    (For unbounded arrays, my language offers an alternative: slices, which contain their length. This is available whatever the calling mechanism.)


    A slice is presumably an object that encapsulates a pointer to data and
    size information - a struct. It might give a nice syntax in your
    language, but it is a different concept entirely.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Thu Jul 11 12:22:13 2024
    On 11/07/2024 11:41, David Brown wrote:
    On 11/07/2024 12:04, bart wrote:
    On 11/07/2024 09:54, Michael S wrote:

    No, it isn't.
    If [in C] it was possible to pass arrays to functions, either by value
    or by reference, then callee would know the length of passed array. As
    it is, callee does not know it.

    The length can be passed in a separate parameter, but then it does not
    have to be the same as an original.

    That's rather specious. In my language (probably in C too), most
    passed arrays are unbounded, allowing the same function to work with
    arrays of different sizes.

    So that would need a separate Length parameter, even using by-reference.

    No.

    Like any object, an array has data, and characteristics including the
    type of the elements, and for an array, the length of the array.  If you pass an object, by reference or by value, the receiver (function
    parameter, or caller if we are talking about return values) gets the characteristics as well as the data.

    In C, if you write code that looks a bit like it is passing an array,
    the object's characteristics don't follow along with it

    If the original array has type T[N], then the T is passed, but the N is
    lost. The [] is also lost:; it turns into *. But in C, that doesn't
    matter too much; it can still index that object!

    So 1 out of 3 characteristics make it into the callee. (Here I'm talking
    about info attached to the parameter name; the type itself may still
    have that N. Have I mentioned that C is mess?)

    - therefore you
    are not passing the array.  If in your language, the characteristics
    also do not follow, as part of the parameter, then your language cannot
    pass arrays either.

    If the original type is [N]T, then all 3 of those characteristics are accessible in the callee, and are characteristics of both the type, and
    the parameter name:

    type T = [4]int

    proc F(T x)=
    println T.typestr, T.len, T.bytes
    println x.typestr, x.len, x.bytes, x[1].typestr
    end

    This shows:

    [4]i64 4 32
    [4]i64 4 32 i64

    (It also reports an error if I attempt to pass a [5]int type; the C
    version won't care. You don't need to explain why.)


    If you need to have a separate manual length parameter, then your
    language is doing the same as C - you are passing a pointer by value
    without the full object information.

    This was the problem with the original Pascal: arrays of different fixed
    sizes were strictly different types. You couldn't write a function that
    took either an int[10] or int[20], or some array allocated at runtime.

    Here my language uses the type []T (or [0]T). The empty bounds serve rhe
    same purpose as void* type: they will match any array bounds.

    In this case, while those 3 characteristics are still passed, the length
    one is not useful as it's always 0. Additional info is needed.


    So perhaps your confusion here lies in a misunderstanding of how arrays
    as passed in your own language, and you have carried that confusion over
    to C.

    Actually my language has a much tidier and more orthogonal type system,
    and there are no discontinuities such as expressions suddenly decaying
    to pointers whenever they threaten to yield an array type.

    As such, it would much easier to teach, and could serve as a model
    against which C can be compared, and points of deviance noted.

    (I'm sure there are mainstream languages that could serve that purpose
    too! But mostly they are too different.)

    A slice is presumably an object that encapsulates a pointer to data and
    size information - a struct.  It might give a nice syntax in your
    language, but it is a different concept entirely.

    It combines the A and N parameters commonly passed to functions, into a composite object. It is not entirely different!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Tim Rentsch on Thu Jul 11 14:00:21 2024
    On 11/07/2024 12:49, Tim Rentsch wrote:
    bart <bc@freeuk.com> writes:


    According to what people have said, C would STILL be a language that
    passed thing by value, and never by automatic reference.

    First, the scheme that you outline is either dumb or disingenuous
    (or perhaps both).

    Second, the argument you're making is purely ad hominem: it
    isn't about what is true but about what it is people will say, or
    at least what you think they would say.

    Third, none of this changes the underlying reality. Whatever
    people might say about your hypothetical scenario, or whatever it
    is you think they would say, it doesn't alter the fact that in C
    all function arguments are passed by value, and not by reference.


    People don't write software based on the the precise, pedantic details
    of what a language reference says, which are always to use the same
    carefully selected set of terms.

    They want to write programs that do useful tasks.

    If that task calls for a function that manipulates arrays as though they
    were passed by reference, then, guess what, they will use a C function
    that the standard says always passes things by value.

    For that purpose, in the mind of the user, it does the same job as 'by
    by reference'. That it does so by some other quirks (array decay, and
    the ability to index pointers as thought they were arrays), is by the by.

    I understand that in this newsgroup, most posters are only interested in
    what the Standard says and little else, and will pounce upon any turns
    of phrase, any nomenclature, that deviate even slightly from what it
    says in that document.

    I also understand that this is not comp.std.c

    Meanwhile, the internet abounds with quotes like this about C:

    "When we pass the address of an array while calling a function then this
    is called function call by reference."

    "Basically, in C, function parameters that are arrays are passed by
    reference, by default."

    Yes, I get that such lax informality would annoy the people here.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Lawrence D'Oliveiro on Thu Jul 11 14:50:58 2024
    On 11/07/2024 03:51, Lawrence D'Oliveiro wrote:
    On Wed, 10 Jul 2024 03:16:18 -0400, James Kuyper wrote:

    On 7/9/24 20:57, Lawrence D'Oliveiro wrote:

    On Sat, 6 Jul 2024 21:34:29 -0400, James Kuyper wrote:

    On many platforms, if _Alignof(type) is less than the word size, then
    a C pointer to that type is implemented as the combination of the
    machine address of the correct word, combined with an offset within
    that word of the first byte of that object.

    Which is a terrific idea, except it cannot be carried to its logical
    conclusion (addressing of arbitrarily-aligned dynamically-defined
    bitfields) because of the requirement in the C spec that the size of a
    “byte” be at least 8 bits.

    I will grant you that I failed to mention the fact that this is a
    feasible way of implementing C only on platforms with a word size of 16
    bits or longer.

    Don’t you think C needs a better way of handling bitfields than shift-and- mask?

    Yes. But because it's not hard for a million programmers to each create
    their own macros like GETBIT and SETBIT, that's not going to happen.

    Although bit operations are unusual in other languages too.


    Many architectures have bitfield instructions, but C cannot easily
    make use of them without the ability to have arbitrarily-bit-aligned pointers.

    When you look in depth at working with bits and bitfields, it is a
    surprisingly wide area with lots of possibilities

    * Define named bits and bitfields within a struct (C already has this,
    although with little control). These bitfields are fixed length and
    location known at compile-time (although only to the compiler!), and can
    be of size 1 to 64 bits

    * Extract a bit/bitfield from an integer value. These can have a
    position and length only known at runtime.

    * Inject such a bit/bitfield into an integer value

    * Allow bitfields that can straddle a word boundary.

    * Allow small arrays of bitfields of width 1, 2 or 4 bits that fit
    within an integer. (8-bit bitfields are just bytes.)

    * Allow full arrays of such bitfields, and slices of such arrays

    * Allow arrays of bitfields of any width 1..64 bits

    * Allow pointers to a bitfield of a certain fixed width 1..64 bits

    * Allow that pointer to to stepped either to the next bitfield, or by
    any arbitrary number of bits

    * Allow a bit-pointer with a variable target bit-width

    * Allow bitwise ops between arrays of bits

    * ....

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

    For my static language, I have:

    * Bitfields within structs with more control than C, and are defined
    inside a regular integer field.

    * Bit indexing to extract or inject bits/bitfields: A.[i] and A.[i..j]

    My dynamic language also has arbitrary arrays of 1/2/4 bits, bit-slices,
    and bit-pointers to fields of 1..64 bits. It also has bit-sets of any
    size on which bitwise ops can be used.

    At the level of C, I believe that bit-arrays are a possibility, without
    also needing to have bit-pointers that are bit-aligned. Pointers to the
    start of such arrays are fine, as they will be byte-aligned.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Tim Rentsch on Thu Jul 11 15:04:37 2024
    On 11/07/2024 14:26, Tim Rentsch wrote:
    bart <bc@freeuk.com> writes:

    On 11/07/2024 12:49, Tim Rentsch wrote:

    bart <bc@freeuk.com> writes:

    According to what people have said, C would STILL be a language that
    passed thing by value, and never by automatic reference.

    First, the scheme that you outline is either dumb or disingenuous
    (or perhaps both).

    Second, the argument you're making is purely ad hominem: it
    isn't about what is true but about what it is people will say, or
    at least what you think they would say.

    Third, none of this changes the underlying reality. Whatever
    people might say about your hypothetical scenario, or whatever it
    is you think they would say, it doesn't alter the fact that in C
    all function arguments are passed by value, and not by reference.

    People don't write software based on the the precise, pedantic details
    of what a language reference says, which are always to use the same
    carefully selected set of terms.

    They want to write programs that do useful tasks.

    If that task calls for a function that manipulates arrays as though
    they were passed by reference, then, guess what, they will use a C
    function that the standard says always passes things by value.

    For that purpose, in the mind of the user, it does the same job as 'by
    by reference'. That it does so by some other quirks (array decay, and
    the ability to index pointers as thought they were arrays), is by the
    by.

    I understand that in this newsgroup, most posters are only interested
    in what the Standard says and little else, and will pounce upon any
    turns of phrase, any nomenclature, that deviate even slightly from
    what it says in that document.

    I also understand that this is not comp.std.c

    Meanwhile, the internet abounds with quotes like this about C:

    "When we pass the address of an array while calling a function then
    this is called function call by reference."

    "Basically, in C, function parameters that are arrays are passed by
    reference, by default."

    Yes, I get that such lax informality would annoy the people here.

    You have proven once again that your goal is not to inform
    but to annoy. Congratulations. Mission accomplished.

    And yours (plural as it's not just you) is also to annoy with pointless pedantry.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Lawrence D'Oliveiro on Thu Jul 11 12:09:33 2024
    On 7/10/24 22:51, Lawrence D'Oliveiro wrote:
    On Wed, 10 Jul 2024 03:16:18 -0400, James Kuyper wrote:

    On 7/9/24 20:57, Lawrence D'Oliveiro wrote:

    On Sat, 6 Jul 2024 21:34:29 -0400, James Kuyper wrote:

    On many platforms, if _Alignof(type) is less than the word size, then
    a C pointer to that type is implemented as the combination of the
    machine address of the correct word, combined with an offset within
    that word of the first byte of that object.

    Which is a terrific idea, except it cannot be carried to its logical
    conclusion (addressing of arbitrarily-aligned dynamically-defined
    bitfields) because of the requirement in the C spec that the size of a
    “byte” be at least 8 bits.

    I will grant you that I failed to mention the fact that this is a
    feasible way of implementing C only on platforms with a word size of 16
    bits or longer.

    Don’t you think C needs a better way of handling bitfields than shift-and- mask? Many architectures have bitfield instructions, but C cannot easily
    make use of them without the ability to have arbitrarily-bit-aligned pointers.

    Note: on such a platform, the C shift and mask instructions could be
    translated internally to bitfield instructions. I suppose that would be
    a nice feature to try out, but what does that have to do with what we
    were talking about?

    The key point is that the addressable unit of the hardware doesn't have
    to match the addressable unit implemented by C code. It can be emulated.

    On a bit-addressable machine, a C compiler could implement a char* by
    using a machine address that moves forward 8 bits every time you add 1
    to it.

    On a machine where the addressable memory locations are 64 bits apart, a
    C compiler could implement a char* by using a machine address combined
    with 3 bits set aside to designate which 8 bits within the 64 bit word
    are being pointed at. Adding 1 to such a pointer increments the offset;
    if it's already pointing at byte 7, the offset would wrapping around to
    0 bytes and the machine address would be incremented.

    Furthermore, in the intermediate ranges, systems with a bit size greater
    than 8 and less than 16 bits or so, there's also the option to choose
    CHAR_BIT to match the size of the addressable unit. Some real world DSPs
    set CHAR_BIT to 16.

    The C standard gives implementors enough freedom to implement byte addressability, as required by the C standard, on machines with just
    about any size of addressable unit. It is simply not the problem you
    suggested it was in your message that was posted at 2024-07-06 01:38 +0000.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Thu Jul 11 17:58:34 2024
    On 11/07/2024 13:22, bart wrote:
    On 11/07/2024 11:41, David Brown wrote:
    On 11/07/2024 12:04, bart wrote:
    On 11/07/2024 09:54, Michael S wrote:

    No, it isn't.
    If [in C] it was possible to pass arrays to functions, either by value >>>> or by reference, then callee would know the length of passed array. As >>>> it is, callee does not know it.

    The length can be passed in a separate parameter, but then it does not >>>> have to be the same as an original.

    That's rather specious. In my language (probably in C too), most
    passed arrays are unbounded, allowing the same function to work with
    arrays of different sizes.

    So that would need a separate Length parameter, even using by-reference.

    No.

    Like any object, an array has data, and characteristics including the
    type of the elements, and for an array, the length of the array.  If
    you pass an object, by reference or by value, the receiver (function
    parameter, or caller if we are talking about return values) gets the
    characteristics as well as the data.

    In C, if you write code that looks a bit like it is passing an array,
    the object's characteristics don't follow along with it

    If the original array has type T[N], then the T is passed, but the N is
    lost. The [] is also lost:; it turns into *. But in C, that doesn't
    matter too much; it can still index that object!

    In C, writing a function parameter as an array is equivalent to writing
    it as a pointer to an element of the array. And if you use an array
    expression as an argument when calling a function, it is implicitly
    converted to a pointer to its first element.

    And then since in C the subscript operator is just a way of writing
    pointer arithmetic, of course you can use that pointer to access the
    data in the array. But you can't use it to get any information about
    the array itself, such as its length.


    So 1 out of 3 characteristics make it into the callee.

    Right - you get the type of the elements pointed to.

    With pass by reference, you'd expect the fact that it is an array to
    pass through, along with the size of the array. That's what you get
    when you use a language that supports pass by reference, and supports
    passing arrays, such as using C++ with std::array<> types. (C++ does
    not support passing C-style arrays to or from functions.)

    (Here I'm talking
    about info attached to the parameter name; the type itself may still
    have that N. Have I mentioned that C is mess?)

    You've mentioned very clearly that your understanding of C is a mess. C
    itself is quite simple here, and the rules are not hard to understand.
    (You might disagree with the choice of rules, or the syntax for them -
    and I think most C users have some disagreements there. But that
    doesn't mean they don't understand them.)


    - therefore you are not passing the array.  If in your language, the
    characteristics also do not follow, as part of the parameter, then
    your language cannot pass arrays either.

    If the original type is [N]T, then all 3 of those characteristics are accessible in the callee, and are characteristics of both the type, and
    the parameter name:

      type T = [4]int

    That is completely different. Your language has a way to attach the
    size of an array into a type, and the characteristics follow that type
    declared in the function prototype. That's fine, but it is a different
    thing entirely from saying that it is passed with the array itself.
    What you are doing here is much the same as wrapping a C array in a
    struct and passing that (as a value). You are just moving the goalposts
    in an attempt to claim you were not wrong earlier.


      proc F(T x)=
          println T.typestr, T.len, T.bytes
          println x.typestr, x.len, x.bytes, x[1].typestr
      end

    This shows:

      [4]i64 4 32
      [4]i64 4 32 i64

    (It also reports an error if I attempt to pass a [5]int type; the C
    version won't care. You don't need to explain why.)


    If you need to have a separate manual length parameter, then your
    language is doing the same as C - you are passing a pointer by value
    without the full object information.

    This was the problem with the original Pascal: arrays of different fixed sizes were strictly different types. You couldn't write a function that
    took either an int[10] or int[20], or some array allocated at runtime.


    And in C you can put a fixed size array in a struct (or a union) to
    declare a new type, as that is how you make new types in C. ("typedef", despite its name, does not define types - it merely defines type aliases.)

    Here my language uses the type []T (or [0]T). The empty bounds serve rhe
    same purpose as void* type: they will match any array bounds.

    OK, so you do the same as C but with a different syntax. Presumably you
    think your syntax is vastly nicer than that of C, and will get your
    knickers in a twist if anyone says differently, but "niceness" is a
    matter of opinion.

    You are still not passing indefinitely sized arrays by reference.


    In this case, while those 3 characteristics are still passed, the length
    one is not useful as it's always 0. Additional info is needed.


    So perhaps your confusion here lies in a misunderstanding of how
    arrays as passed in your own language, and you have carried that
    confusion over to C.

    Actually my language has a much tidier and more orthogonal type system,
    and there are no discontinuities such as expressions suddenly decaying
    to pointers whenever they threaten to yield an array type.

    As such, it would much easier to teach, and could serve as a model
    against which C can be compared, and points of deviance noted.

    And yet, strangely, the world has been quite happy using C for half a
    century while I have yet to hear anyone say your language looks great.
    (But to be fair I have said in the past that there are some features of
    it that I like.)


    (I'm sure there are mainstream languages that could serve that purpose
    too! But mostly they are too different.)

    A slice is presumably an object that encapsulates a pointer to data
    and size information - a struct.  It might give a nice syntax in your
    language, but it is a different concept entirely.

    It combines the A and N parameters commonly passed to functions, into a composite object. It is not entirely different!


    Yes, it is.

    This thread would be so much simpler if you knew what you were talking
    about, or at least accepted that other people do.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Thu Jul 11 18:25:03 2024
    On 11/07/2024 16:58, David Brown wrote:
    On 11/07/2024 13:22, bart wrote:

    If the original array has type T[N], then the T is passed, but the N
    is lost. The [] is also lost:; it turns into *. But in C, that doesn't
    matter too much; it can still index that object!


    (Here I'm talking about info attached to the parameter name; the type
    itself may still have that N. Have I mentioned that C is mess?)

    You've mentioned very clearly that your understanding of C is a mess.  C itself is quite simple here,

    Not it isn't. The fact that I can do this:

    void F(vector a) {} // typedef byte vector[100];

    and get the type of 'a' as 'byte*', sizeof(a) as 8, sizeof(*a) as 1, but sizeof(vector) as 100, suggests all sorts of shenanigans.

    and the rules are not hard to understand.

    Ha ha ha! Of course you would say that. Let me try that same example:

    type vector = [50]u16 # mix it up a little

    proc F(vector a)=
    println a.bytes # shows 100
    # println a^.bytes # error: not a pointer
    println vector.bytes # shows 100
    println a.len, vector.len # both show 50

    end

    So the info for 'a' and 'vector' matches exactly, since after all
    'vector' IS the type of 'a'!

    I can't do the equivalent of *a, as 'a' is not a pointer. C gives me 3 different sizes for a byte-size; I can't even toss a coin to choose the
    right one.

    I've stated several times now that the type system on my systems
    language is simple, consistent and orthogonal. Parameter passing is
    consistent from the language POV (despite the efforts of the ABI).

    C's on the other hand, especially combined with its parameter passing,
    is a fucking mess. Everyone here secretly knows that; I don't know what
    they're trying to gain from pretending otherwise, and trying to put the
    blame on people who are calling it out.

    I'm not trying to gloat here; I've use my type system for decades and
    it's nothing new or remarkable, just common sense. It's what C's should
    have been.

       type T = [4]int

    That is completely different.  Your language has a way to attach the
    size of an array into a type, and the characteristics follow that type declared in the function prototype.  That's fine, but it is a different thing entirely from saying that it is passed with the array itself. What
    you are doing here is much the same as wrapping a C array in a struct
    and passing that (as a value).

    I'm defining a fixed-size array of 4 ints. It can be passed to a
    function expecting exactly that type, OR to a function expecting an
    unbounded array (OR to a function expecting a slice of 'int').

    That's not possible with an array buried inside a struct. Which of
    course is not an array at all; the struct could have 50 fields, of,
    which could include half a dozen fixed arrays, some inside a nested struct.

    /That/ is completely different.


    Here my language uses the type []T (or [0]T). The empty bounds serve
    rhe same purpose as void* type: they will match any array bounds.

    OK, so you do the same as C but with a different syntax.  Presumably you think your syntax is vastly nicer than that of C, and will get your
    knickers in a twist if anyone says differently, but "niceness" is a
    matter of opinion.

    You are still not passing indefinitely sized arrays by reference.

    Oh, what makes you say that?



    In this case, while those 3 characteristics are still passed, the
    length one is not useful as it's always 0. Additional info is needed.


    So perhaps your confusion here lies in a misunderstanding of how
    arrays as passed in your own language, and you have carried that
    confusion over to C.

    Actually my language has a much tidier and more orthogonal type
    system, and there are no discontinuities such as expressions suddenly
    decaying to pointers whenever they threaten to yield an array type.

    As such, it would much easier to teach, and could serve as a model
    against which C can be compared, and points of deviance noted.

    And yet, strangely, the world has been quite happy using C for half a
    century while I have yet to hear anyone say your language looks great.

    I haven't mentioned syntax at all. I've talked about type systems and
    array passing, and how, when I enumerated 3 kinds of parameter passing
    in my language, C's parameter passing most matched either number (1) or
    (3) of mine depending on parameter type. (3) was pass-by-reference.


    It combines the A and N parameters commonly passed to functions, into
    a composite object. It is not entirely different!


    Yes, it is.

    This thread would be so much simpler if you knew what you were talking
    about, or at least accepted that other people do.

    I have some doubts that /you/ do!

    A slice would be a natural addition to C's arrays. Passing a slice
    passes a pointer to the array data, just like C does anyway (the slice
    is passed by value; the data by reference!).

    Applied to char arrays, it gives you counted strings.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Keith Thompson on Thu Jul 11 20:56:00 2024
    On 11/07/2024 19:53, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:
    [...]
    For that purpose, in the mind of the user, it does the same job as 'by
    by reference'. That it does so by some other quirks (array decay, and
    the ability to index pointers as thought they were arrays), is by the
    by.
    [...]

    Those "quirks" are a rich source of confusion and bugs for anyone who
    doesn't understand how this stuff is actually defined. (Yes, I'm acknowledging, yet again, that the way C specifies its treatment of
    arrays is confusing.)

    A user who thinks that arrays are simply "passed by reference" is likely
    to try to apply sizeof to an array parameter (and might or might not get
    a diagnostic from the compiler). A slightly more sophisticated user is
    still likely to be unsure of just where the "quirks" are.

    What have you ever done to help make that kind of error less likely?
    What is your goal?


    This my first comment on the subject:

    "Arrays are passed by reference:
    ...
    Although ..."


    (Note that 'Although'.) And the first reply was:

    BB:
    "All parameter passing in C is by value. All of it."

    And there's been no let up since then.

    Nobody has acknowledged that there's more going on with passing array
    types than it simply being due to 'pass by value', if it's not full
    'pass by reference'.

    The language could have helped a little by making this invalid:

    int A[20];

    void F(int B[20]) {}

    The type of B looks just like that of A, but it isn't; the T[N] type is silently changed to T*. The language could insist that you write:

    void F(int* B) {}

    This way, it is far clearer that a pointer is being passed, and 'pass by
    value' now makes more sense. The way B will be used is now consistent
    with the same declaration anywhere else.

    My goals might be to make the language a little more accessible,
    although that cuts little ice here where most are C experts of long
    standing; they won't know or will have forgotten what it's like to be a beginner or outsider, or coming to C from saner languages.

    However I use /my/ saner language every day.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Keith Thompson on Thu Jul 11 21:36:20 2024
    On 11/07/2024 21:29, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:

    The language could have helped a little by making this invalid:

    int A[20];

    void F(int B[20]) {}

    The type of B looks just like that of A, but it isn't; the T[N] type
    is silently changed to T*. The language could insist that you write:

    void F(int* B) {}

    But it doesn't. Why should we waste time in comp.lang.c explaining how
    C *could* have been defined? It's hard enough to explain how it
    actually is defined, especially with your contributions.

    This way, it is far clearer that a pointer is being passed, and 'pass
    by value' now makes more sense. The way B will be used is now
    consistent with the same declaration anywhere else.

    But that's not C.

    Why isn't it C?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Keith Thompson on Thu Jul 11 21:37:40 2024
    On 11/07/2024 21:29, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:
    On 11/07/2024 19:53, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:
    [...]
    For that purpose, in the mind of the user, it does the same job as 'by >>>> by reference'. That it does so by some other quirks (array decay, and
    the ability to index pointers as thought they were arrays), is by the
    by.
    [...]
    Those "quirks" are a rich source of confusion and bugs for anyone
    who
    doesn't understand how this stuff is actually defined. (Yes, I'm
    acknowledging, yet again, that the way C specifies its treatment of
    arrays is confusing.)
    A user who thinks that arrays are simply "passed by reference" is
    likely
    to try to apply sizeof to an array parameter (and might or might not get >>> a diagnostic from the compiler). A slightly more sophisticated user is
    still likely to be unsure of just where the "quirks" are.
    What have you ever done to help make that kind of error less likely?
    What is your goal?


    This my first comment on the subject:

    "Arrays are passed by reference:
    ...
    Although ..."

    And that statement was incorrect, even with the "Although".

    So arrays are passed by value? Gotcha.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Fri Jul 12 07:54:11 2024
    On 11/07/2024 22:37, bart wrote:
    On 11/07/2024 21:29, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:

    This my first comment on the subject:

    "Arrays are passed by reference:
       ...
    Although ..."

    And that statement was incorrect, even with the "Although".

    So arrays are passed by value? Gotcha.


    False dichotomy - and you know that.

    Arrays are not passed at all in C.

    Try writing that out on a post-it and attach it to your screen, so that
    you will re-read it before every post you make.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Fri Jul 12 07:53:01 2024
    On 11/07/2024 22:36, bart wrote:
    On 11/07/2024 21:29, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:

    The language could have helped a little by making this invalid:

        int A[20];

        void F(int B[20]) {}

    The type of B looks just like that of A, but it isn't; the T[N] type
    is silently changed to T*. The language could insist that you write:

        void F(int* B) {}

    But it doesn't.  Why should we waste time in comp.lang.c explaining how
    C *could* have been defined?  It's hard enough to explain how it
    actually is defined, especially with your contributions.

    This way, it is far clearer that a pointer is being passed, and 'pass
    by value' now makes more sense. The way B will be used is now
    consistent with the same declaration anywhere else.

    But that's not C.

    Why isn't it C?

    Are you trying to blame us for how C is defined? Or is this a serious
    question about the historical process of design decisions in C? My only
    guess here - and it is only a guess - is it goes back to the way
    function parameters were written in K&R C before prototypes, and
    supported because in C, declarations follow usage. You can write B[10]
    whether you have "int B[20]" or "int * B", so it seems natural that you
    could use either form when declaring the parameter.

    (Note that I too would have preferred a different syntax. IMHO it would
    have been better either to allow passing arrays by value in some way, or
    not to allow code that /looks/ like it passes arrays.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Fri Jul 12 08:00:30 2024
    On 11/07/2024 19:25, bart wrote:
    On 11/07/2024 16:58, David Brown wrote:
    On 11/07/2024 13:22, bart wrote:

    If the original array has type T[N], then the T is passed, but the N
    is lost. The [] is also lost:; it turns into *. But in C, that
    doesn't matter too much; it can still index that object!


    (Here I'm talking about info attached to the parameter name; the type
    itself may still have that N. Have I mentioned that C is mess?)

    You've mentioned very clearly that your understanding of C is a mess.
    C itself is quite simple here,

    Not it isn't. The fact that I can do this:

        void F(vector a) {}          // typedef byte vector[100];

    and get the type of 'a' as 'byte*', sizeof(a) as 8, sizeof(*a) as 1, but sizeof(vector) as 100, suggests all sorts of shenanigans.

    and the rules are not hard to understand.

    Ha ha ha! Of course you would say that.



    Well, yes, I /did/ say that.

    The rules for C are not the way I would have preferred, but they are straightforward to learn and consistent. All you have to do is listen
    to what people tell you, read reliable information (such as the
    standards), and stop insisting that your confusing misunderstandings are correct.

    I can understand when someone new to C gets mixed up about how arrays
    work. I don't understand how someone can remain so stubbornly confused
    when they have been told how C /actually/ works.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Fri Jul 12 12:03:50 2024
    On 12/07/2024 06:53, David Brown wrote:
    On 11/07/2024 22:36, bart wrote:
    On 11/07/2024 21:29, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:

    The language could have helped a little by making this invalid:

        int A[20];

        void F(int B[20]) {}

    The type of B looks just like that of A, but it isn't; the T[N] type
    is silently changed to T*. The language could insist that you write:

        void F(int* B) {}

    But it doesn't.  Why should we waste time in comp.lang.c explaining how >>> C *could* have been defined?  It's hard enough to explain how it
    actually is defined, especially with your contributions.

    This way, it is far clearer that a pointer is being passed, and 'pass
    by value' now makes more sense. The way B will be used is now
    consistent with the same declaration anywhere else.

    But that's not C.

    Why isn't it C?

    Are you trying to blame us for how C is defined?

    KT has chosen not to answer, and now you are evading it too. I'm asking
    why this:

    void F(int* B) {}

    is 'not C' according to KT.

    To be clear, I was proposing that:

    void F(int B[20])

    is an error, and requiring people to write:

    void F(int* B) {}

    instead. I tried to enforce that in my C compiler, and it was a one-line change. But it can only be used for new programs, and so much existing
    code uses those value-array declarations, for example:

    LUALIB_API int (luaL_checkoption) (lua_State *L, int arg, const char *def,
    const char *const lst[]);

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Janis Papanagnou on Fri Jul 12 12:21:02 2024
    On 12/07/2024 12:12, Janis Papanagnou wrote:
    On 12.07.2024 08:00, David Brown wrote:
    [...]

    I can understand when someone new to C gets mixed up about how arrays
    work.

    I can't understand that if I presume that the person has read any
    basic textbook about "C".

    I don't understand how someone can remain so stubbornly confused
    when they have been told how C /actually/ works.

    Especially if the one has said to have written an own language that
    is close enough to "C" where I'd expect the knowledge to be there
    already before that person is designing and implementing his project.

    I wonder why he refuses to look up things if he thinks that all the
    experts here are wrong about a well documented fact.

    If you had to describe to someone how a function accesses array data in
    its caller, what would you say?

    It's clearly not by value. It's apparently not by reference. You can't
    get away with saying they are not passed, as clearly functions *can*
    access array data via parameters.

    Or would you merely refer to the relevant section in the language reference?


    Just curious.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to bart on Fri Jul 12 07:34:17 2024
    On 11/07/2024 19:25, bart wrote:
    On 11/07/2024 16:58, David Brown wrote:
    ...
    You've mentioned very clearly that your understanding of C is a mess.
    C itself is quite simple here,

    Not it isn't. The fact that I can do this:

        void F(vector a) {}          // typedef byte vector[100];

    and get the type of 'a' as 'byte*', sizeof(a) as 8, sizeof(*a) as 1, but sizeof(vector) as 100, suggests all sorts of shenanigans.

    No, just one shenanigan. It suggests that when a function parameter is
    declared as an array of 'type', that it is treated as a declaration of a pointer to that type.

    Every fact that you listed above follows from this rule which is very explicitly stated in the C standard and any C textbook, and which is
    simple enough that most C newbies figure it out fairly quickly. It has
    already been explained to you hundreds of times over the past decade or
    so - but has apparently never actually gotten into your brain

    and the rules are not hard to understand.

    Ha ha ha! Of course you would say that.

    Keep in mind that you've proven repeatedly that your ability to
    understand even the simplest features of C is quite negligible. The fact
    that you can't understand it proves almost nothing about how hard it is
    for most people to understand.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Fri Jul 12 14:36:23 2024
    On 12/07/2024 13:03, bart wrote:
    On 12/07/2024 06:53, David Brown wrote:
    On 11/07/2024 22:36, bart wrote:
    On 11/07/2024 21:29, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:

    The language could have helped a little by making this invalid:

        int A[20];

        void F(int B[20]) {}

    The type of B looks just like that of A, but it isn't; the T[N] type >>>>> is silently changed to T*. The language could insist that you write: >>>>>
        void F(int* B) {}

    But it doesn't.  Why should we waste time in comp.lang.c explaining how >>>> C *could* have been defined?  It's hard enough to explain how it
    actually is defined, especially with your contributions.

    This way, it is far clearer that a pointer is being passed, and 'pass >>>>> by value' now makes more sense. The way B will be used is now
    consistent with the same declaration anywhere else.

    But that's not C.

    Why isn't it C?

    Are you trying to blame us for how C is defined?

    KT has chosen not to answer, and now you are evading it too.

    I am not evading it in any way. I answered, and you snipped the answer.

    I'm asking
    why this:

         void F(int* B) {}

    is 'not C' according to KT.

    Keith did not say anything of the sort.

    Why do you keep ignoring what people write, then argue against things
    that no one said?


    To be clear, I was proposing that:

     void F(int B[20])

    is an error, and requiring people to write:

     void F(int* B) {}

    instead.

    And to be clear, no one has said that would be a bad idea (which does
    not mean that no one /thinks/ it would be a bad idea). But it is not
    how C is defined as a language, and the C language - its compilers and
    existing code base - is not going to change on the whims of some guy off
    the internet who can't be bothered to learn the language properly.

    I tried to enforce that in my C compiler, and it was a one-line
    change. But it can only be used for new programs, and so much existing
    code uses those value-array declarations, for example:

    LUALIB_API int (luaL_checkoption) (lua_State *L, int arg, const char *def,
                                       const char *const lst[]);



    One person's private sort-of-C compiler is of no more relevance to the C community than one person's private language. You are welcome to make
    as many non-conforming changes to your own tools as you like, but they
    do not make a difference to C. No one else will ever use your tool, so
    no one else will ever care about any incompatible changes you make to
    it. If /you/ are happier having such changes in your tools, then that
    is great for you.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Janis Papanagnou on Fri Jul 12 14:59:01 2024
    On 12/07/2024 12:44, Janis Papanagnou wrote:
    On 11.07.2024 22:37, bart wrote:
    On 11/07/2024 21:29, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:

    This my first comment on the subject:

    "Arrays are passed by reference:
    ...
    Although ..."

    And that statement was incorrect, even with the "Although".

    So arrays are passed by value? Gotcha.

    Neither is true. - Tertium datur!

    "Array passing" is in "C" realized using a pointer passing
    mechanism where the pointer is passed "by value".

    Neither an array is passed [by value] nor there's a "call
    by reference" mechanism in "C".

    So how are the elements of the caller's array accessed?

    No copies have been supplied to the caller. So access is by ... ?

    Look, there are only two choices: 'pointer' and 'reference', which in C
    are more or less the same thing:

    "6.2.5p20 ... A pointer type describes an object whose value provides a reference to an entity of the referenced type."

    So I said 'arrays are passed by reference'; maybe I should have said
    'array elements are passed by reference' (which suggests that each has
    its own reference), so shoot me.

    But everyone was so keen to prove me wrong and incapable of understanding.



    This has been explained (also with references to original
    sources) to you many times.


    Could you be a bit more patronising, please?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Michael S on Fri Jul 12 15:44:47 2024
    On 12/07/2024 13:42, Michael S wrote:
    On Fri, 12 Jul 2024 13:12:53 +0200
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    But maybe he has looked up some things, since lately he's squirming
    by introducing terms like "_true_ pass-by-reference" [emphasis by me]
    obviously trying to bend the semantics of the established technical
    "pass-by-reference" term to fit his argument. (Introducing new terms
    for existing mechanisms or bending semantics of existing terms with
    well established meaning is certainly not helpful in any way.)

    But, yes, that person is a phenomenon.

    Janis


    I don't share your optimistic belief that the term "pass by reference"
    is really established. Very few terms in computer science (science?
    really?) are established firmly. Except, may be, in more theoretical
    branches of it.


    Pass-by-reference can mean almost anything. Many languages and their implementations are too diverse for it to have a precise meaning.

    All you might assume about pass-by-reference is that the data you're
    accessing has not been passed by value!

    In Python for example, everything is famously passed 'by reference'.

    But named objects (ie. variables) in Python are in two parts:

    [ABC] -> [OBJECT]

    You have a variable ABC which is bound - via an object reference - to
    some object value.

    When you call F(ABC), it passes the reference to the object (which is
    then bound to a local parameter). That is, it passes '-> OBJECT'

    I don't consider that true pass-by-reference, which would allow a callee
    to bind ABC in the caller to a different object.

    My dynamic language allows that by having name references.

    But because complex types use object references anyway (Python does so
    for all types) I don't need true by-reference to MODIFY the caller's
    data, but I will need it to REPLACE it, ie. assign a new value.

    The code below shows two functions where values are passed in two
    different ways.

    Both F1 and F2 can modify the elements of the list (which needs to be
    mutable), but the object will always be a list.

    Only F2 can assign something else entirely to the caller's variable.

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

    proc F1(x) = # complex obj passed by value-reference
    x := "cat"
    end

    proc F2(&x) = # complex obj passed by true-reference
    x := "dog"
    end

    proc main =
    a := (10, 20, 30) # complex types using references
    b := (40, 50, 60)

    F1(a)
    F2(b)

    println a # (10, 20, 30) unchanged
    println b # dog replaced
    end

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Keith Thompson on Fri Jul 12 12:46:41 2024
    On 7/12/24 11:46, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:
    [...]
    KT has chosen not to answer, and now you are evading it too. I'm
    asking why this:

    void F(int* B) {}

    is 'not C' according to KT.

    I never said that's "not C". It is in fact a perfectly valid function definition. And it's not the question you originally asked.

    In your message dated 2024-07-11 13:29 -0700, you wrote:
    The language could insist that you write:

    void F(int* B) {}

    But it doesn't. Why should we waste time in comp.lang.c explaining how
    C *could* have been defined? It's hard enough to explain how it
    actually is defined, especially with your contributions.

    This way, it is far clearer that a pointer is being passed, and 'pass
    by value' now makes more sense. The way B will be used is now
    consistent with the same declaration anywhere else.

    But that's not C.

    So you did say that something is not C, Bart has merely misidentified
    what it is. "void F(int*B)" is C, and you never denied that it was.
    Insisting that you write "void F(int*B)" - in other words, disallowing
    "void F(int B[20])" - is not C, and that's you did say.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Fri Jul 12 19:13:40 2024
    On 12/07/2024 13:36, David Brown wrote:

    One person's private sort-of-C compiler is of no more relevance to the C community than one person's private language.  You are welcome to make
    as many non-conforming changes to your own tools as you like, but they
    do not make a difference to C.  No one else will ever use your tool, so
    no one else will ever care about any incompatible changes you make to
    it.  If /you/ are happier having such changes in your tools, then that
    is great for you.

    Jesus, you just can't resist putting the boot in at every opportunity
    and being incredibly patronising, can you?

    I made the tweak to see how hard it would be to detect value-arrays
    declared in parameter list (it was very easy), and what the consequences
    would be on existing code (significant).

    The example I posted showed a type (const char* x[]) where there was no advantage to having that value array notation. Using 'const char**'
    would be a more accurate description of the actual parameter type.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to bart on Fri Jul 12 19:32:00 2024
    On 12/07/2024 19:13, bart wrote:
    On 12/07/2024 13:36, David Brown wrote:

    One person's private sort-of-C compiler is of no more relevance to the
    C community than one person's private language.  You are welcome to
    make as many non-conforming changes to your own tools as you like, but
    they do not make a difference to C.  No one else will ever use your
    tool, so no one else will ever care about any incompatible changes you
    make to it.  If /you/ are happier having such changes in your tools,
    then that is great for you.

    Jesus, you just can't resist putting the boot in at every opportunity
    and being incredibly patronising, can you?

    I made the tweak to see how hard it would be to detect value-arrays
    declared in parameter list (it was very easy), and what the consequences would be on existing code (significant).

    The example I posted showed a type (const char* x[]) where there was no advantage to having that value array notation. Using 'const char**'
    would be a more accurate description of the actual parameter type.

    Incidentally if sizeof(x) is used, gcc will about it if using 'char*
    x[]', and not with 'char** x'.

    I wonder why, since after all EVERYBODY who uses C understands what they
    are doing.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Keith Thompson on Fri Jul 12 19:33:18 2024
    On 12/07/2024 16:58, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:
    [...]
    If you had to describe to someone how a function accesses array data
    in its caller, what would you say?

    The same thing I've written to you many times over the last several
    years in this newsgroup.

    It's clearly not by value. It's apparently not by reference. You can't
    get away with saying they are not passed, as clearly functions *can*
    access array data via parameters.

    Yes, function can access array data by means other than passing arrays
    as parameters.

    Or would you merely refer to the relevant section in the language reference?

    That, and to section 6 of the comp.lang.c FAQ, which is an excellent description of C's treatment of arrays and pointers. You've repeatedly declined to say whether you've read it. I encourage you to do so.

    Just curious.

    Sure you are.

    Actually, I /was/ genuinely curious as to how a function's access to its caller's array elements are described without using 'pointer' or
    'reference'.

    Because as AFAIK this whole subthread seems to about people trying to
    catch me out on some pedantry.

    All it needed was for someone to agree that yes, arrays are
    /effectively/ passed by reference, even if it's not true by-reference
    and not a general purpose feature of the language.

    People can't seem to help throwing insults.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to James Kuyper on Fri Jul 12 19:39:45 2024
    On 12/07/2024 17:46, James Kuyper wrote:
    On 7/12/24 11:46, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:
    [...]
    KT has chosen not to answer, and now you are evading it too. I'm
    asking why this:

    void F(int* B) {}

    is 'not C' according to KT.

    I never said that's "not C". It is in fact a perfectly valid function
    definition. And it's not the question you originally asked.

    In your message dated 2024-07-11 13:29 -0700, you wrote:
    The language could insist that you write:

    void F(int* B) {}

    But it doesn't. Why should we waste time in comp.lang.c explaining how
    C *could* have been defined? It's hard enough to explain how it
    actually is defined, especially with your contributions.

    This way, it is far clearer that a pointer is being passed, and 'pass
    by value' now makes more sense. The way B will be used is now
    consistent with the same declaration anywhere else.

    But that's not C.

    So you did say that something is not C, Bart has merely misidentified
    what it is. "void F(int*B)" is C, and you never denied that it was.
    Insisting that you write "void F(int*B)" - in other words, disallowing
    "void F(int B[20])" - is not C, and that's you did say.


    If you use:

    -Werror=sizeof-array-argument

    with gcc, then it makes using sizeof() on such a parameter an error.

    Presumably somebody though that was worth checking for. Maybe somebody
    will think that using array-parameters at all is worth checking for too.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Sat Jul 13 11:37:59 2024
    On 12/07/2024 20:13, bart wrote:
    On 12/07/2024 13:36, David Brown wrote:

    One person's private sort-of-C compiler is of no more relevance to the
    C community than one person's private language.  You are welcome to
    make as many non-conforming changes to your own tools as you like, but
    they do not make a difference to C.  No one else will ever use your
    tool, so no one else will ever care about any incompatible changes you
    make to it.  If /you/ are happier having such changes in your tools,
    then that is great for you.

    Jesus, you just can't resist putting the boot in at every opportunity
    and being incredibly patronising, can you?


    If you say stupid things, repeatedly, you should not be surprised if
    people try to dumb down the way they speak to you.

    Start applying a bit of your intelligence (you say stupid things
    sometimes, but I know you are far from stupid), and you'll find the
    level of conversation going up.

    I made the tweak to see how hard it would be to detect value-arrays
    declared in parameter list (it was very easy), and what the consequences would be on existing code (significant).

    No, the consequences are non-existent because no one uses your tool, and
    no one will ever copy that change in other tools (of significance).


    The example I posted showed a type (const char* x[]) where there was no advantage to having that value array notation. Using 'const char**'
    would be a more accurate description of the actual parameter type.


    You can write your code the way you want to write it - it will not
    change the way anyone else writes their code. It really is that simple.
    Why is this so difficult for you to understand?

    Do you really suppose that if /you/ make "foo(char x[])" a syntax error
    in /your/ compiler, it will have the slightest effect on how other
    people write their C code? Or on what other C compilers do? Or on how
    half a century of existing C code is written?


    Personally, I don't like that C allows something that /looks/ like
    arrays can be passed to functions, but doesn't work that way. I don't
    think I have ever written a function with an array-like parameter - I
    use a pointer parameter if I mean a pointer, or have the array wrapped
    in a struct if I want to pass an array by value. But I don't think my
    opinions make a difference to C, and even if I were elected dictator of
    C for a day, I don't think my opinions should count more than any one
    else's - including those that like the way C works here.

    And I don't confuse my opinions or preferences with how C actually works
    and how it is actually defined, and I certainly don't spread such
    confusions and misunderstandings to others.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Sat Jul 13 11:46:08 2024
    On 12/07/2024 20:32, bart wrote:
    On 12/07/2024 19:13, bart wrote:
    On 12/07/2024 13:36, David Brown wrote:

    One person's private sort-of-C compiler is of no more relevance to
    the C community than one person's private language.  You are welcome
    to make as many non-conforming changes to your own tools as you like,
    but they do not make a difference to C.  No one else will ever use
    your tool, so no one else will ever care about any incompatible
    changes you make to it.  If /you/ are happier having such changes in
    your tools, then that is great for you.

    Jesus, you just can't resist putting the boot in at every opportunity
    and being incredibly patronising, can you?

    I made the tweak to see how hard it would be to detect value-arrays
    declared in parameter list (it was very easy), and what the
    consequences would be on existing code (significant).

    The example I posted showed a type (const char* x[]) where there was
    no advantage to having that value array notation. Using 'const char**'
    would be a more accurate description of the actual parameter type.

    Incidentally if sizeof(x) is used, gcc will about it if using 'char*
    x[]', and not with 'char** x'.

    I wonder why, since after all EVERYBODY who uses C understands what they
    are doing.


    Everybody makes mistakes sometimes, no matter how good an understanding
    they have.

    And many people have already agreed with you that this is a point in C
    which people misunderstand. You've worked with C for decades and
    demonstrate a considerable level of misunderstanding. I have said - and
    I stand by it - that the rules of C arrays are relatively simple and straightforward, and if you learn the language properly you will get
    them right. But I don't claim that everyone who programs in C, even all
    those that have done so for a long time, actually /has/ learned this
    properly. There are plenty of C references, books, courses, teachers,
    etc., who get it wrong too, and pass on their misunderstanding to others.

    That is why people here are arguing with you. It's not because we
    /like/ every aspect of C, or think that the way C is defined here is
    better than alternatives (including some of your ideas). It is because
    C is the way C is, and people who want to use the language need to learn
    how it works, without being subjected to a barrage of misinformation and muddled thoughts from someone who is determined to make things as
    difficult as possible for people learning C.

    I get that you hate C. What I don't get is why you feel the need to
    harm programmers learning C.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to BGB on Wed Jul 17 12:38:15 2024
    On 13/07/2024 10:39, BGB wrote:

    But, as I see it, no real point in arguing this stuff (personally, I
    have better stuff to be doing...).

    We all do. But this group seems to be about arguing about pointless
    stuff and you might come here when you want a respite from proper work.

    However (here I assume you've gone back to Quake but that other
    interested parties might be reading this), consider the program below.

    That sets up an array and then sums its elements by calling 3 different functions to do the job:

    (1) Using normal C pass-by-value

    (2) Using C pass-by-value to emulate call-by-reference

    (3) Using fantasy true call-by-reference as it might appear if C had the
    feature

    (I'd hoped C++ would run this, but it didn't even like the middle function.)

    I'm asking people to compare the first and third functions and their
    calls, and to see if there's any appreciable difference between them.
    There will obviously be a difference in how the A parameter is declared.

    ---------------------------------------------
    #include <stdio.h>

    typedef int T;

    int sum_byvalue(T* A, int n) {
    int i, sum=0;
    for (i=0; i<n; ++i) sum += A[i];
    return sum;
    }

    int sum_bymanualref(T(*A)[], int n) {
    int i, sum=0;
    for (i=0; i<n; ++i) sum += (*A)[i];
    return sum;
    }

    int sum_bytrueref(T (&A)[], int n) {
    int i, sum=0;
    for (i=0; i<n; ++i) sum += A[i];
    return sum;
    }

    int main(void) {
    enum {N = 10};
    T A[N] = {10,20,30,40,50,60,70,80,90,100};
    int total=0;

    total += sum_byvalue (A, N);
    total += sum_bymanualref (&A, N);
    total += sum_bytrueref (A, N);

    printf("%d\n", total); // would show 1650
    }
    ---------------------------------------------

    Find anything? I thought not.

    Those findings might suggest that C doesn't need call-by-reference, not
    for arrays anyway. Except that at present you can do this:

    T x=42;
    sum_byvalue(&x, N);

    which would not be possible with call-by-reference. Nor with
    sum_bymanualref, but apparently nobody wants to be doing with all that
    extra, fiddly syntax. Better to be unsafe!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Wed Jul 17 14:42:51 2024
    On 13/07/2024 10:37, David Brown wrote:

    If you say stupid things, repeatedly,

    Start applying a bit of your intelligence (you say stupid things
    sometimes, but I know you are far from stupid), and you'll find the
    level of conversation going up.

    I made the tweak to see how hard it would be to detect value-arrays
    declared in parameter list (it was very easy), and what the
    consequences would be on existing code (significant).

    No, the consequences are non-existent because no one uses your tool, and
    no one will ever copy that change in other tools (of significance).

    You are spectacularly missing the point. IT DOESN'T WHOSE TOOL IT IS.
    Somebody could have done the same exercise with gcc, and come to the
    same conclusion: too many programs use array parameters.




    The example I posted showed a type (const char* x[]) where there was
    no advantage to having that value array notation. Using 'const char**'
    would be a more accurate description of the actual parameter type.


    You can write your code the way you want to write it - it will not
    change the way anyone else writes their code.  It really is that simple.
     Why is this so difficult for you to understand?

    Do you really suppose that if /you/ make "foo(char x[])" a syntax error
    in /your/ compiler, it will have the slightest effect on how other
    people write their C code?

    What WOULD be the effect if a compiler did that? How would a particular codebase be affected?

    You can just modify a compiler and try it, which is what I did. What
    difference does it make which compiler it is? You just have a blind,
    irrational hatred for anything I do.

    Another way to do it is for someone to painstakingly go through every
    line of a codebase by hand, expanding macros and typedefs as needed, and checking whether any parameters declared top-level array types.

    I think if you were given that job to do, then applying my toy compiler wouldn't be so bad after all!

    Or on what other C compilers do?  Or on how
    half a century of existing C code is written?


    Personally, I don't like that C allows something that /looks/ like
    arrays can be passed to functions, but doesn't work that way.

    gcc could conceivably have an option that detects and warns about that.
    Whoever is thinking about doing that might well do a test exactly like mine.

      I don't
    think I have ever written a function with an array-like parameter - I
    use a pointer parameter if I mean a pointer, or have the array wrapped
    in a struct if I want to pass an array by value.

    So all /your/ code would still pass; great!

      But I don't think my
    opinions make a difference to C, and even if I were elected dictator of
    C for a day, I don't think my opinions should count more than any one
    else's - including those that like the way C works here

    Half of the programming language you call "C" is defined by the way you
    invoke your compiler. So that already allows for myriad, slightly
    different dialects.

    Somebody had to think up all those options, and they would have been
    influence by people's opinions. This is just one more, which I would
    happily have as a default.


    And I don't confuse my opinions or preferences with how C actually works
    and how it is actually defined, and I certainly don't spread such
    confusions and misunderstandings to others.

    C does that well enough by itself. There are any number of behaviours
    where: (1) saying nothing and passing; (2) warning and passing; (3)
    reporting an error and failing are all perfectly valid.

    You just choose which one you want, at least if using an equivocal
    compiler like gcc.

    This is like taking an examination and being able to choose how strictly
    it should be marked! Yeah, I think I'll got a pass today...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Michael S on Wed Jul 17 16:56:15 2024
    On 17/07/2024 14:34, Michael S wrote:
    On Wed, 17 Jul 2024 12:38:15 +0100
    Bart <bc@freeuk.com> wrote:

    On 13/07/2024 10:39, BGB wrote:

    But, as I see it, no real point in arguing this stuff (personally,
    I have better stuff to be doing...).

    We all do. But this group seems to be about arguing about pointless
    stuff and you might come here when you want a respite from proper
    work.

    However (here I assume you've gone back to Quake but that other
    interested parties might be reading this), consider the program below.

    That sets up an array and then sums its elements by calling 3
    different functions to do the job:

    (1) Using normal C pass-by-value

    (2) Using C pass-by-value to emulate call-by-reference

    (3) Using fantasy true call-by-reference as it might appear if C had
    the feature

    (I'd hoped C++ would run this, but it didn't even like the middle
    function.)

    I'm asking people to compare the first and third functions and their
    calls, and to see if there's any appreciable difference between them.
    There will obviously be a difference in how the A parameter is
    declared.

    ---------------------------------------------
    #include <stdio.h>

    typedef int T;

    int sum_byvalue(T* A, int n) {
    int i, sum=0;
    for (i=0; i<n; ++i) sum += A[i];
    return sum;
    }

    int sum_bymanualref(T(*A)[], int n) {
    int i, sum=0;
    for (i=0; i<n; ++i) sum += (*A)[i];
    return sum;
    }

    int sum_bytrueref(T (&A)[], int n) {
    int i, sum=0;
    for (i=0; i<n; ++i) sum += A[i];
    return sum;
    }

    int main(void) {
    enum {N = 10};
    T A[N] = {10,20,30,40,50,60,70,80,90,100};
    int total=0;

    total += sum_byvalue (A, N);
    total += sum_bymanualref (&A, N);
    total += sum_bytrueref (A, N);

    printf("%d\n", total); // would show 1650
    }
    ---------------------------------------------

    Find anything? I thought not.

    Those findings might suggest that C doesn't need call-by-reference,
    not for arrays anyway. Except that at present you can do this:

    T x=42;
    sum_byvalue(&x, N);

    which would not be possible with call-by-reference. Nor with
    sum_bymanualref, but apparently nobody wants to be doing with all
    that extra, fiddly syntax. Better to be unsafe!

    The C++ syntax your are looking for is sum_bytrueref(std::array<T,10>&A,

    As written, the function's parameters match arrays of any size, with the
    length passed separately.

    And indeed, the generated code is the same.
    https://godbolt.org/z/dYGoWsdjE
    But why is it the same? Because in C++ arrays are also 2nd class
    citizen, like in C ! std::array is not a 'native' C++ type, but a
    wrapper around array-within-struct pattern.

    Is it? Then that's not what I want; this not about emulating value arrays.

    If C++ has pass-by-reference, then it should apply to any type. But it
    has trouble even with the middle function which is valid C.

    The proper comparison would be vs language that has arrays as 1st class citizen.

    That is not neccessary here, since I'm trying to show that there is
    little difference in the calling code and the function body between the idiomatic C code for passing arrays, and what would be used with true pass-by-reference.

    But it is true that my fantasy & parameter would probably interfere with
    the rules of C to do with reducing T[] parameter types to T*.

    However I'm not allowed to use my language as an example. But the
    differences can be summarised here; This example passes an array A to
    the 3 finds of function:

    Call Array access in callee

    (1) C call-by-value F(A) A[i]

    (2) C call-by-value
    emulating call-by-ref G(&A) (*A)[i]

    (3) true call-by-reference H(A) A[i]

    What the user has to write is what's important, and here it is clear
    that they write the same thing in (1) and (3).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Thu Jul 18 14:00:04 2024
    On 18/07/2024 13:41, David Brown wrote:
    On 18/07/2024 12:05, BGB wrote:

    The "magical auto dereferencing pointers" interpretation gives better
    performance, when it works. In this case, it didn't work...

    It's very easy to make high performance code if correctness doesn't
    matter!  Obviously, correctness is more important.

    It is useful to explore ways of making some scenarios faster. Here there
    is simply a bug in one of those ways. But you don't just give up
    completely; you can try fixing the bug.


    Sadly, there is no good way at the moment to know whether or not it
    will work, for now forcing the slower and more conservative option.

    I would think the simple test is that for data that is never changed (or
    not changed within the function), you can use a constant reference - otherwise you cannot.  It is not by coincidence that in C++, it is
    common to use pass by /const/ reference as an efficient alternative to
    pass by value for big objects.

    You said the other day that my C compiler was wrong to do that: to use efficient pass-by-pointer for structs marked as 'const' in the function signature; they always have to be copied no matter what.



    If "foo_t" is 2000 bytes long, then "foo_t temp" makes a 2000 byte
    space in your local variables (the stack, on virtually every
    platform) and "temp = arr[i];" does a 2000 byte memcpy().  The same
    thing applies if "foo_t" is 2 bytes long, or 2 megabytes long.  And
    if there is a stack overflow making "temp", that's the programmer's
    problem.


    For now:
    1 - 16 bytes, goes in registers, except when accessing a member where
    it needs to be in-memory; unless it is a SIMD type which is special
    and allows accessing members with the value still in registers.

    17 bytes to 15.999K: Accessed by an implicit reference, uses hidden
    copying to mimic by-value semantics (not quite foolproof as of yet it
    seems).

    16K and beyond, quietly turned into a heap allocation (with a compiler
    warning). Should otherwise look the same as the prior case.


    The normal system is that local objects are data on the stack -
    regardless of the size or type, scaler or aggregate.  Parameter passing
    is done by register for some types (for the first few parameters), or
    the stack otherwise.  Returns are in a register or two for some times,
    or by a stack slot assigned by the caller.  For struct parameters or
    return values, it can be efficient for the caller to pass hidden
    pointers, but that's not strictly necessary if you have a fixed stack
    frame layout.  (Struct parameters still get copied to the stack to
    ensure value semantics - the hidden pointer points to the stack copy.)

    Trying to have special cases for different sizes,

    That's exactly what common 64-bit ABIs do. In fact the SYS V ABI is so complicated that I can't understand its struct passing rules at all.

    (If I ever have to use that, I'd need to write test code for each
    possible size of struct, up to 100 bytes or so (past the largest machine register), and see how an existing compliant compiler handles each case.)

    Here the context appears to be a custom ISA, so anything is possible.

    You are trying to be too smart here, IMHO - the compiler's job is to let
    the programmer be smart.  It's always nice to have optimisations, but
    not at the expense of correctness.

    That's an odd remark from a devotee of gcc. Your usual attitude is to
    let the programmer write code in the most natural manner, and let a
    smart optimising compiler sort it out.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Tim Rentsch on Thu Aug 15 13:48:25 2024
    On 15/08/2024 09:43, Tim Rentsch wrote:
    Bart <bc@freeuk.com> writes:

    [on comparing array arguments in C with call-by-reference]

    [...] the differences [between C rules and true call-by-reference]
    can be summarised here; [...]

    Call Array access in callee

    C call-by-value F(A) A[i]

    true call-by-reference H(A) A[i]

    What the user has to write is what's important, and here it is clear
    that they write the same thing [in the two cases shown].


    The comparison above is misleading because it is incomplete.
    Let's compare the two modes more fully:


    C call-by-value call-by-reference
    =============== =================
    at call:

    (array argument) F(A) H(A)

    (pointer argument) F(p) (disallowed)

    My posts were about passing *arrays* and the fact that C's pass-by-value
    was remarkably similar to pass-by-reference.

    However your entry for pointers is not correct: you can pass pointers by reference (in C, it means passing a T** type instead of T* to emulate that).


    (null argument) F(0) (disallowed)

    Pass-by-reference necessarily requires an lvalue at the call-site since
    it effectively applies & to the argument.


    inside function:

    (access) A[i] A[i]

    (update) A[i] = ... A[i] = ...

    sizeof A (pointer size) (array size)

    That's one of the small differences. But you only get the array size in
    a language where the array type includes its length. Otherwise, you only
    get it if it's part of the parameter type.

    A++ (changes A variable) (disallowed)

    (In my language ++A is allowed. I'm not sure why, it's likely a bug.)

    A = (new value) (changes A variable) (disallowed)

    This is allowed too in my language, if the array has a fixed size. It
    reassigns the whole array.

    In C you can't do A = B for other reasons, since arrays aren't
    manipulated by value. But you can do this:

    memcpy(A, B, n);

    and it will overwrite caller's array with B. That other language can
    also choose to call memcpy().


    The more complete comparion illustrate why C semantics should not
    be thought of as call-by-reference.

    It was made clear more than once that it wasn't exact call-by-reference.

    It was also made clear that there were enough similarities that adding
    real call-by-reference arrays to C would buy you very little.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Ben Bacarisse on Thu Aug 15 17:08:12 2024
    On 15/08/2024 15:33, Ben Bacarisse wrote:
    Bart <bc@freeuk.com> writes:

    On 15/08/2024 09:43, Tim Rentsch wrote:
    Bart <bc@freeuk.com> writes:

    C call-by-value call-by-reference
    =============== =================
    at call:

    (array argument) F(A) H(A)

    (pointer argument) F(p) (disallowed)

    My posts were about passing *arrays* and the fact that C's pass-by-value
    was remarkably similar to pass-by-reference.

    Which is why, presumably, you didn't show the differences. Your
    post was all polemic not part of a collegiate discussion of the
    similarities and differences.

    However your entry for pointers is not correct:

    No, the entry is correct. H(p) would be (is?) disallowed when H's
    parameter is a reference to an array.

    Sorry, what language does the right-hand column pertain to? /Any/
    language that has call-by-reference, or Tim's hypthetical language?

    Or any that could be used to prove him right?

    In general there is no reason, in a language with true
    call-by-reference, why any parameter type T (which has the form U*, a
    pointer to anything), cannot be passed by reference. It doesn't matter
    whether U is an array type or not.



    you can pass pointers by
    reference (in C, it means passing a T** type instead of T* to emulate
    that).

    H's parameter is /not/ what you claim "emulates" a reference to a
    pointer -- it's a hypothetical reference to an array.

    In my original post it was G() that was C emulating pass-by-reference.

    It wasn't stated that p was an array pointer, only pointer, but it
    doesn't make any difference in H which we can assume is not C, and
    therefore doesn't have C's hangups about arrays and pointers.


    Maybe you missed what Tim was doing. He is showing a fuller comparison
    for one F and one H. A different function could, in this hypothetical
    C with call-by-reference, be passed a pointer, but then it could not be passed an array.

    Neither of you have explained this well. Are you both projecting C's
    craziness with arrays and pointers onto this hypothetical language?

    If so, then if this fantasy language has true pass-by-reference, then
    let it have real value-arrays too!

    (null argument) F(0) (disallowed)

    Pass-by-reference necessarily requires an lvalue at the call-site

    Yes, that is one of the differences you failed to illustrate.

    I illustrated some conveniences which C's array-passing idiom which
    match those of true pass-by-reference.


    since it effectively applies & to the argument.

    No. Passing a reference is /not/ like using & to pass a pointer.

    Funny, in my language that is EXACTLY how it is implemented. Instead of
    my writing:

    F(&x) and in F having to write: *x

    you instead write:

    F(x) and in F you write: x

    So it will insert & and * operators for you. Why, how would /you/ do
    it? (I'm talking about this level of language; higher level ones might
    do everthing with references anyway, and might not have & or * operators.)



    inside function:

    (access) A[i] A[i]

    (update) A[i] = ... A[i] = ...

    sizeof A (pointer size) (array size)

    That's one of the small differences. But you only get the array size in a
    language where the array type includes its length. Otherwise, you only get >> it if it's part of the parameter type.

    A++ (changes A variable) (disallowed)

    (In my language ++A is allowed. I'm not sure why, it's likely a bug.)

    In the hypothetical C with call-by-reference, it's another difference
    you chose not to show.

    Yes, as I said, I concentrate on the ones which allowed you to write
    F(A), and A[i] inside F, just like pass-by-reference, rather than F(&A),
    and (*A[i]) inside F. Did I claim it was full by pass-by-reference?

    A = (new value) (changes A variable) (disallowed)

    This is allowed too in my language, if the array has a fixed size. It
    reassigns the whole array.

    Your language (for which we have no reference material) is beside the
    point. This is another difference you failed to point out in your
    comparison

    It doesn't matter; any call-by-reference worth its salt will allow you
    to change variables in the caller. That's half the point it!

    C doesn't have A=B syntax for arrays, but it can do memcpy for the same
    effect.



    In C you can't do A = B for other reasons, since arrays aren't manipulated >> by value.

    One way or another, this is a difference you failed to illustrate. If
    this hypothetical C is very close to C then the difference is as Tim
    posted. But in a another quasi-C that permits array assignment there
    would still be a difference: the pointer assignment would be disallowed
    but the array assignment (by reference) would be permitted.

    What pointer assignment? You're mixing actual C with fantasy C. You
    can't do that: if you add array assignment to C, then it changes everything.


    So why did you give only one side of the comparison? You have nothing
    to lose by being open about the differences if your objective is an
    open, collegiate discussion of C. By presenting a polemic, you make the whole exchange more antagonistic than it needs to be.

    These were my original comments on the subject made to DB:


    DB:
    . In C, there is no "pass by reference" or "return by reference". It
    is all done by value.

    BC:

    Arrays are passed by reference:

    void F(int a[20]) {}

    int main(void) {
    int x[20];
    F(x);
    }

    Although the type of 'a' inside 'F' will be int* rather than int(*)[20].

    It was in reply to DB which appear to imply that arrays were passed by
    value. Obviously they're not passed by value, so what then? (Please,
    don't rerun the thread! This is where everyone jumped in.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Tim Rentsch on Thu Aug 15 23:22:39 2024
    On 15/08/2024 22:36, Tim Rentsch wrote:
    Bart <bc@freeuk.com> writes:

    On 15/08/2024 09:43, Tim Rentsch wrote:

    Bart <bc@freeuk.com> writes:

    [on comparing array arguments in C with call-by-reference]

    [...] the differences [between C rules and true call-by-reference]
    can be summarised here; [...]

    Call Array access in callee

    C call-by-value F(A) A[i]

    true call-by-reference H(A) A[i]

    What the user has to write is what's important, and here it is clear
    that they write the same thing [in the two cases shown].

    The comparison above is misleading because it is incomplete.
    Let's compare the two modes more fully:


    C call-by-value call-by-reference
    =============== =================
    at call:

    (array argument) F(A) H(A)

    (pointer argument) F(p) (disallowed)

    My posts were about passing *arrays* and the fact that C's
    pass-by-value was remarkably similar to pass-by-reference.

    I see. So your point is, if we ignore all the ways that the two
    modes are different then they are exactly the same.

    Brilliant deduction, Dr. Watson.

    So your approach is to totally ignore all the ways that the two modes
    are identical. OK. (Maybe we can also say that arrays and pointers are
    entirely separate concepts in C, because they are different in /some/ ways!)

    Suppose we write functions that sum an array's elements then zeros the
    elements in the caller's array at the same time (to give the reference
    element some observable behaviour in the caller). We do three versions
    F, G and H:

    F uses C's call-by-value. The characteristics here are:

    Param syntax: int A[]
    Access in func: A[i]
    Call syntax: F(A)

    G emulates call-by-reference. The characteristics are:

    Param syntax: int (*A)[]
    Access in func: (*A)[i]
    Call syntax: F(&A)

    H uses a fantasy version of C with proper pass-by-reference. Those
    characterics are now:

    Param syntax: int &A[] (in this fantasty, no () is needed)
    Access in func: A[i]
    Call syntax: F(A)

    Eagle-eyed readers may notice a remarkable similarity between F and H.
    The only point of difference is that lone &, written once in the program (compared with perhaps dozens of calls and accesses).

    But according to you, that is pure coincidence; nothing to see, move on!

    To me however it is highly significant. Most of the downsides of G,
    namely the ugly syntax, are done away with F. And yet, while F doesn't
    use call-by-reference in any way, it still can magically update the
    caller's array even though it uses only call-by-value.

    I'd say that F confers most of the benefits of pass-by-reference, but
    you presumably consider F to provide 0% of the benefits? (What extra
    benefits could H provide in my scenario.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Kaz Kylheku on Fri Aug 16 01:46:38 2024
    On 16/08/2024 00:29, Kaz Kylheku wrote:
    On 2024-08-15, Bart <bc@freeuk.com> wrote:
    On 15/08/2024 22:36, Tim Rentsch wrote:
    I see. So your point is, if we ignore all the ways that the two
    modes are different then they are exactly the same.

    Brilliant deduction, Dr. Watson.

    So your approach is to totally ignore all the ways that the two modes
    are identical.

    Almost any two different things have some attributes that are identical.
    If we consider a bicycle and a fish, we can probably come up with common attributes.

    Passing pointers by value similar to call-by-reference, but also
    different.

    I'm going to show a demo in my language because C doesn't support both
    by-value and by-reference.

    However, here is the F function written in C (one point of difference is
    that my 'int' is 64 bits):

    void F(int* A) {
    int i;
    A[i]=0; // same as (A+i)=0
    }

    The above uses the C idiom for passing and using arrays, which I will
    try and emulate here:

    proc F(ref int A) = # equivalent to C func above
    int i
    (A+i-1)^:=0 # ptr-relative must be 0-based
    end

    proc H([]int &A) = # pass-by-reference
    int i
    A[i]:=0
    end

    proc main =
    [10]int A

    F(cast(&A)) # Pass A to F, C-style
    H(A) # Pass A to H, by-reference
    end

    (What I can't emulate is the call to F which in C looks like F(A),
    exactly like the call H.)

    So Tim, and apparently you, claim there are no points of similarity, or
    that they don't have any bearing on anyway. Well here are the bodies of
    F and H when translated to x64 assembly, excluding entry/exit code:

    mov u64 [D10+D3*8-8], 0 # body of F ('i' resides in D3)

    mov u64 [D10+D3*8-8], 0 # body of H

    You can see that they are identical. And here the two calls:

    lea D10, [D14+t.main.a]
    call t.f
    lea D10, [D14+t.main.a]
    call t.h

    That's quite remarkable: the guts of a call-by-value function and its
    call, being identical to that of a call-by-reference function and /its/
    call.

    A bit like comparing the insides of a bicycle and a fish, and finding
    they are the same!




    The program can calculate a bad pointer and pass that by value.

    Call-by-reference implementations can take measures to ensure that
    a a bad reference is not passed.

    Yes, both my G and H examples have better type-checking than the F
    function which was idiomatic C (you can see I had to use an explicit ...
    I mean just cast, above).

    That doesn't really affect the substance of the discussion that what is actually passed in registers is not the value of the array elements. But
    C people are cagey about exactly how F works if it's not really by-value
    and not really by-reference.

    If your point of view is that pointers are what is "real under the
    hood", then call-by-reference is just "syntactic sugar" for pointers.

    No, my point of view, first expressed on 9-Jul-24, was in response to a
    claim that everything in C was passed by value, and that was that arrays
    are passed by reference. But I left out the work 'effectively'.

    Of course, everybody know that is actually the case, but everybody also
    wanted to argue otherwise.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From tTh@21:1/5 to Bart on Fri Aug 16 03:37:41 2024
    On 8/16/24 02:46, Bart wrote:

    I'm going to show a demo in my language because

    May be you can ask Usenet's Gods for the creation of comp.lang.bart


    tTh

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to tTh on Fri Aug 16 12:14:06 2024
    On 16/08/2024 02:37, tTh wrote:
    On 8/16/24 02:46, Bart wrote:

    I'm going to show a demo in my language because

    May be you can ask Usenet's Gods for the creation of comp.lang.bart

    You snipped what comes after 'because'.

    If C supported the H function then my example would have used C.

    You are welcome to show the generated code for F and H of any language
    you like.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Michael S on Fri Aug 16 12:28:05 2024
    On 16/08/2024 10:38, Michael S wrote:
    On Fri, 16 Aug 2024 02:18:15 -0700
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:

    Michael S <already5chosen@yahoo.com> writes:
    [...]
    IMHO, C++ is a particularly bad example.
    Yes, C++ has call-by-reference misfeature. But arrays in C++ are
    2nd class citizen, same as in C. They can't be assigned and can't be
    passed to callee, neither by value nor by reference.

    Also, I suspect that if you ask Ken Thompson, he will tell you that
    C++ does not really have 'call by reference'. Instead, it has
    references as 1st class object, so, naturally, values of references
    can be used during 'call by value'.
    I have enough of respect to KT to consider that his POV is not a
    nonsense.

    Is that an attempt at proof by authority?

    Yes!

    Not only does Ken Thompson
    have very little to do with C++, but you're basing your conclusion on
    what you *suspect* he would say.


    Yes, but it's not baseless.
    It is based on following section Go language FAQs that I assumed to be
    either authored or approved by KT.
    https://go.dev/doc/faq#pass_by_value


    This seems to about what happens when a language has complex, two-level
    types where you have a descriptor, which has a pointer to the actual data.

    The user may not be aware of that, and may think that passing such an
    object 'by-value' will copy the data. If the language only does
    'shallow' pass-by-value as seems to happen here, there may be some pass-by-reference semantics.

    This can get confusing. However, whatever the machinations of a
    particular type, if a language offers *explicit* pass-by-reference, then
    you will always be able to provide real pass-by-reference on top.

    So in the case of those descriptors, it will pass a reference to the descriptor. This allows a caller to modify what's in the descriptor, and
    have that change visible in the caller.

    (My dynamic language has some 3-level types! There, reference parameters
    make a tangible difference.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Fri Aug 16 12:45:54 2024
    On 16/08/2024 09:04, David Brown wrote:
    On 15/08/2024 18:08, Bart wrote:

    These were my original comments on the subject made to DB:


    DB:
    ;. In C, there is no "pass by reference" or "return by reference".
    It is all done by value.

    BC:

    ;Arrays are passed by reference:

    ;  void F(int a[20]) {}

    ;  int main(void) {
    ;    int x[20];
    ;    F(x);
    ;  }

    Although the type of 'a' inside 'F' will be int* rather than
    int(*)[20].

    It was in reply to DB which appear to imply that arrays were passed by
    value. Obviously they're not passed by value, so what then? (Please,
    don't rerun the thread! This is where everyone jumped in.)


    I am not sure if you want an answer here or not - you asked "so what
    then", but also asked to avoid a re-run of the thread.

    I can give a summary - and I also hope this doesn't lead to a re-run of
    the discussion.  However, since you are asking the same question as you
    did at the start, and the language C has not changed in the meantime,
    the factual and correct answers will inevitably be the same:

    1. C has no "pass by reference" - it is all "pass by value".

    2. In C, you cannot pass an array as a function parameter.

    3. The automatic conversion of many array expressions to pointer
    expressions, along with the similar conversions of function parameter
    types, gives C users a syntax that is similar - but not identical to -
    what you would have if the language supported passing arrays by reference.

    So, you agree that it is similar to. And not just the resulting syntax,
    but the semantics and even the generated code can be the same (as I demonstrated but somebody complained).

    Would you agree that they are effectively passed by-reference for all
    practical purposes?

    All the other differences in detail are mostly due to the weird way that
    C handles arrays anyway.



    4. Adding "pass by reference" and "arrays as first class objects" would
    both be very significant changes to C


    Adding pass-by-reference would not be a huge change. I added that using
    a cheap and cheerful approach that seems work well enough (a parameter
    marked as by-ref, would have '&' automatically applied on arguments, and
    '*' automatically applied to parameter accesses in the callee**).

    But what would complicate it in C is how it interacts with how arrays
    currently work. For example, passing array A already passes '&A[0]'; it
    can't really pass '&&A[0]' if it's marked as being by-reference!


    (** There were some side-effects: while you can pass a char or short to
    an int parameter for example and it will promote it, if the int is by-reference, you can only pass an exact int type. And also, I wasn't
    able to apply default values to optional by-reference parameters.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Ben Bacarisse on Fri Aug 16 15:19:38 2024
    On 16/08/2024 01:08, Ben Bacarisse wrote:
    Bart <bc@freeuk.com> writes:

    On 15/08/2024 15:33, Ben Bacarisse wrote:
    Bart <bc@freeuk.com> writes:

    On 15/08/2024 09:43, Tim Rentsch wrote:
    Bart <bc@freeuk.com> writes:

    C call-by-value call-by-reference >>>>> =============== ================= >>>>> at call:

    (array argument) F(A) H(A)

    (pointer argument) F(p) (disallowed)

    My posts were about passing *arrays* and the fact that C's pass-by-value >>>> was remarkably similar to pass-by-reference.
    Which is why, presumably, you didn't show the differences. Your
    post was all polemic not part of a collegiate discussion of the
    similarities and differences.

    However your entry for pointers is not correct:
    No, the entry is correct. H(p) would be (is?) disallowed when H's
    parameter is a reference to an array.

    Sorry, what language does the right-hand column pertain to? /Any/ language >> that has call-by-reference, or Tim's hypthetical language?

    Tim said that case was "disallowed". You call that an error on his
    part. What language did you have in mind that permits such a gross
    warping of types? I would describe /any/ language that allowed it as
    having a design error.

    You or he would have to go into more detail, such as an actual example,
    to demonstrate whatever it is that you think is wrong about passing a
    pointer argument by-reference.

    I assume here that the language in the right column is not C.


    Or any that could be used to prove him right?

    In general there is no reason, in a language with true call-by-reference,
    why any parameter type T (which has the form U*, a pointer to anything),
    cannot be passed by reference. It doesn't matter whether U is an array type >> or not.

    I can't unravel this. Take, as a concrete example, C++. You can't pass
    a pointer to function that takes an array passed by reference. You can,
    of course, pass a pointer by reference, but that is neither here nor
    there.

    OK. So why do you agree with this:

    C call-by-value call-by-reference
    =============== =================
    (pointer argument) F(p) (disallowed)

    What is 'pointer argument' here?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Keith Thompson on Sat Aug 17 12:26:57 2024
    On 16/08/2024 19:56, Keith Thompson wrote:
    David Brown <david.brown@hesbynett.no> writes:
    [...]
    In C++, you can't pass arrays as parameters at all - the language
    inherited C's handling of arrays. You can, of course, pass objects of
    std::array<> type by value or by reference, just like any other class
    types.

    #include <cassert>

    typedef int array42[42];

    void func(array42& param) {
    assert(sizeof param == 42 * sizeof(int));
    }

    int main() {
    array42 arg = { };
    func(arg);
    }

    I suggest that this is not the best place to discuss the nuances of C++.


    Perhaps it is not the best place - but I learned something new about C++
    here, so thank you for that!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Keith Thompson on Sat Aug 17 11:38:05 2024
    On 16/08/2024 18:56, Keith Thompson wrote:
    David Brown <david.brown@hesbynett.no> writes:
    [...]
    In C++, you can't pass arrays as parameters at all - the language
    inherited C's handling of arrays. You can, of course, pass objects of
    std::array<> type by value or by reference, just like any other class
    types.

    #include <cassert>

    typedef int array42[42];

    void func(array42& param) {
    assert(sizeof param == 42 * sizeof(int));
    }

    int main() {
    array42 arg = { };
    func(arg);
    }

    This works in C too. You don't need by-reference parameters, but you do
    need to switch sizeof to work directly on the type:


    #include <assert.h>

    typedef int array42[42];

    void func(array42 param) {
    assert(sizeof(array42) == 42 * sizeof(int));
    }

    int main() {
    array42 arg = {0};
    func(arg);
    }

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Tim Rentsch on Sat Aug 17 18:07:38 2024
    On 17/08/2024 15:41, Tim Rentsch wrote:
    Bart <bc@freeuk.com> writes:

    OK. So why do you agree with this:

    C call-by-value call-by-reference >>>>>>> =============== ================= >>>>>>> (pointer argument) F(p) (disallowed)

    What is 'pointer argument' here?

    Try thinking harder. Everyone else understood.

    I could equally say that everyone understood what was meant by 'implicit
    cast'.

    But here you really have to explain what you mean by a pointer argument,
    since there is no reason why such a type can't be passed by reference.

    Lacking such an explanation, I'd have to say still that 'disallowed' is generally incorrect. Try harder with your arguments.

    (I earlier toyed with a proposal to add by-reference parameters to C.

    But I decided not to actually implement it to see how well it would; I
    was satisified that it probably would. (I've been doing this stuff a
    long time.)

    Under that scheme, ANY parameter type can be passed by reference.

    But it would be incompatible with C's array-decay behaviour. So currently:

    void F(int A[]) { A[i];} // A has type int*

    int x[10]; F(x); // Passes &x[10] of type int*

    It would be:

    void F(&int A[]) { A[i];} // A has apparent type int[]
    // but is really int(*)[]

    int x[10]; F(x); // Apparently passes type int[10]
    // but really &x which is int(*)[10]

    (A pointer to an array 10 should be compatible with a pointer to array 0.)

    The advantage is better type-safety: you can only pass an actual array
    type and not the address of random integer. Neither can you pass NULL.

    But no array-to-pointer decay happens with F(x) when F is by-ref, since
    it is really treated as F(&x).)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Tim Rentsch on Sun Aug 18 12:35:42 2024
    On 18/08/2024 02:22, Tim Rentsch wrote:
    Bart <bc@freeuk.com> writes:

    On 17/08/2024 15:41, Tim Rentsch wrote:

    Bart <bc@freeuk.com> writes:

    OK. So why do you agree with this:

    C call-by-value call-by-reference >>>>> =============== ================= >>>>> (pointer argument) F(p) (disallowed)

    What is 'pointer argument' here?

    Try thinking harder. Everyone else understood.

    I could equally say that everyone understood what was meant by
    implicit cast'.

    It would be a useful exercise for you to compare and contrast
    those two statements, listing their similarities and differences.

    But here you really have to explain what you mean by a pointer
    argument, since there is no reason why such a type can't be passed
    by reference.

    Lacking such an explanation, I'd have to say still that
    'disallowed' is generally incorrect.

    What is incorrect is your understanding of what was meant.

    Let me elaborate on that. Some of the people who post here are
    interested in listening, and usually make an effort to understand in
    cases where a first reading leaves them confused. Others, not so
    much. More generally, there is a spectrum of interest/effort, with
    people who make a large effort at one end, and people who make
    little or not effort at the other end.

    Somewhat paradoxically, it is the people who are most intent on
    listening who are the ones most worth listening to. Conversely,
    people who don't make much of an effort to listen and understand
    are usually not worth listening to.

    You are definitely closer to the no effort end of the spectrum than
    you are to the other end. You are much more focused on what you
    want to say than you are in what the other person is saying.
    That's a lot of the reason people dismiss your comments. It also
    reduces the chance that you will get useful responses. Your first
    response up above is a case in point. It's typical of you. Given
    this entirely predictable reaction, I have very little incentive to
    try to explain anything, because I don't think you're going to hear
    the explanation.

    I don't expect any of the above to change the way you act, but just
    in case, here is a suggestion. When you read something that seems
    not to make sense, ask yourself a question: What might have been
    meant here so that this statement is right?

    Or what the poster may have written instead to make his meaning clearer
    and less ambiguous, and not requiring the reader to have to make
    assumptions or double-guess what the poster had in mind.

    There is also the possibility that the poster may have made a mistake!

    If you don't look for
    alternative interpretations you won't ever find any.

    I did exactly that: see a few lines up.

    On the flip
    side, the more often you look for alternative interpretations and
    the more effort you put into doing so, the more likely you are to
    have meaningful interactions with other people in the group, unlike
    the meaningless interactions you usually have.

    So, there is no further explanation of what you meant, which would have
    taken a tenth of that text. So I can only conclude that you wrote
    something that was incorrect, but don't want to admit it. That's fine.

    However you seem intent on making personal attacks instead. That is not
    fine.

    That's a lot of the reason people dismiss your comments.

    That's up to them. But AFAIK I'm am the only person participating who
    actively devises and implements languages at this level, languages that
    include working implementations of pass-by-reference.

    People here know that, but they might not like it. So they resort to
    personal insults, or to picking on casual turns of phrase.

    They also choose to either belittle my experience or ignore it. For
    example you have not commented on my proposal to add pass-by-reference
    to C. This is the bigger reason why I didn't waste half a day
    implementing it.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Ben Bacarisse on Mon Aug 19 01:57:22 2024
    On 19/08/2024 01:01, Ben Bacarisse wrote:
    Bart <bc@freeuk.com> writes:


    You or he would have to go into more detail, such as an actual example, to >> demonstrate whatever it is that you think is wrong about passing a pointer >> argument by-reference.

    No one has said any such thing, so I can't see how any more detail could help. I suspect you've lost track of the point being made.

    Probably, and perhaps not just me! But I'd still quite like to know
    exactly what it is that is marked as 'disallowed'.

    I can't unravel this. Take, as a concrete example, C++. You can't pass >>> a pointer to function that takes an array passed by reference. You can, >>> of course, pass a pointer by reference, but that is neither here nor
    there.

    OK. So why do you agree with this:

    C call-by-value call-by-reference >>>>>>> =============== ================= >>>>>>> (pointer argument) F(p) (disallowed)

    What is 'pointer argument' here?

    I can't see what is confusing you about this. I agree with the above
    for exactly the reasons I wrote.

    Do you mean: 'You can't pass a pointer to a function that takes an array
    passed by reference'?

    (I assume you mean a pointer as argument to a function, rather than a
    'pointer to a function'.)

    I'd still need to disentangle that via a concrete example (and
    preferably not C++). With pass-by-reference, that is usually done on top
    of whatever type choices you've made. Pass-by-reference does not affect
    those types, but internally there may be extra indirection.

    However, as I wrote a day or two ago, if attempting to retrofit pass-by-reference to C, it will interfere a little with normal
    array-to-pointer decay (since there will be an implicit & applied, that
    stops that happening).


    It would be wrong (and is wrong in
    C++) to pass a pointer where an array reference is expected.

    Well, putting aside pass-by-reference, that would be wrong in C too, if
    you interpret 'array reference' to be a type like T(*)[], and a
    'pointer' a type like T*.

    With pass-by-reference added to C, there would be stronger type
    checking, so if something is disallowed, it would be for a good reason,
    not just because Tim said so.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Ben Bacarisse on Mon Aug 19 12:29:49 2024
    On 19/08/2024 02:30, Ben Bacarisse wrote:
    Bart <bc@freeuk.com> writes:

    On 19/08/2024 01:01, Ben Bacarisse wrote:
    Bart <bc@freeuk.com> writes:


    You or he would have to go into more detail, such as an actual example, to >>>> demonstrate whatever it is that you think is wrong about passing a pointer >>>> argument by-reference.
    No one has said any such thing, so I can't see how any more detail could >>> help. I suspect you've lost track of the point being made.

    Probably, and perhaps not just me! But I'd still quite like to know exactly >> what it is that is marked as 'disallowed'.

    I don't know how to explain it any better. If you really want to know,
    maybe you could say what /you/ think was incorrectly marked as
    disallowed so I can see how you were interpreting the table.

    I think I already said that several times!

    But I need to use concrete code, and there is no existing mainstream
    language with pass-by-reference that is simple enough to use for such
    examples. Except for mine and that is not acceptable here.

    So I'll use an extended C where a '&' in front of a parameter's
    base-type marks it as pass-by-reference.

    First an example written in standard C:

    #include <stdio.h>

    void F(int* p) {
    printf("%d\n", *p);
    ++p;
    }

    int main(void) {
    int A[] = {10,20,30};
    int* p = &A[0];

    F(p);
    printf("%d\n", *p);
    }

    This passes a normal pointer. The output is 10 10 from both printfs,
    because the ++p within F does not affect the original pointer in 'main'.

    Now the same program, but using pass-by-reference for 'p'; I won't show
    the whole thing, as the program looks exactly the same except for this line:

    void F(&int* p) {

    The output should now be 10 20 (as verified using my language; if
    interested, that is shown below, and below that is the standard C that
    might be generated by a transpiler from this fantasy C).

    So this is passing a pointer, by reference, and it is allowed!


    Or maybe, just maybe, Tim said it for good reason.


    And yet, he continues to be cagey about it, and you're defending him.
    Just give me a freakin' example!

    WHAT EXACTLY IS IT THAT IS DISALLOWED?

    Just saying 'pointer argument' does not cut it.

    (I'm sure this is some sort of game he's playing as he does this a lot.

    Hinting at something but refusing to give details and suggesting people
    work it out for themselves.)

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

    proc F(ref int &p) =
    println p^
    ++p
    end

    proc main =
    static []int A = (10,20,30)
    ref int p := &A[1]

    F(p)
    println p^
    end

    Shows:
    10
    20


    Equivalent to:

    void F(int** p) {
    printf("%d\n", **p);
    ++(*p);
    }

    int main(void) {
    int A[] = {10,20,30};
    int* p = &A[0];

    F(&p);
    printf("%d\n", *p);
    }

    Also shows 10 20.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Ben Bacarisse on Mon Aug 19 21:18:47 2024
    On 19/08/2024 15:14, Ben Bacarisse wrote:
    David Brown <david.brown@hesbynett.no> writes:

    On 19/08/2024 03:03, Tim Rentsch wrote:
    Ben Bacarisse <ben@bsb.me.uk> writes:

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

    On 16/08/2024 12:00, Ben Bacarisse wrote:

    David Brown <david.brown@hesbynett.no> writes:
    ...
    In C++, you can't pass arrays as parameters at all - the language >>>>>>> inherited C's handling of arrays. You can, of course, pass
    objects of std::array<> type by value or by reference, just like >>>>>>> any other class types.

    The best way to think about C++ (in my very non-expert opinion) is >>>>>> to consider references as values that are passed by, err...,
    value. But you seem prepared to accept that some things can be
    "passed by reference" in C++.

    That seems a subtle distinction - I'll have to think about it a
    little. I like your description of arguments being like local
    variable initialisation - it makes sense equally well regardless of
    whether the parameter is "int", "int*", or "int&". (It's probably
    best not to mention the other one in this group...)

    So if this:
    #include <iostream>
    void g(int &i) { std::cout << i << "\n"; }
    int main(void)
    {
    int I{0};
    g(I);
    }
    shows an int object, I, being passed to g, why does this
    #include <iostream>
    void f(int (&ar)[10]) { std::cout << sizeof ar << "\n"; }
    int main(void)
    {
    int A[10];
    f(A);
    }
    not show an array, A, being passed to f?

    That's backwards compatibility with C array handling at play.

    I'm not sure how this answers my question. Maybe you weren't
    answering it and were just making a remark...
    My guess is he didn't understand the question. The code shown
    has nothing to do with backwards compatibility with C array
    handling.

    I had intended to make a brief remark and thought that was all that was
    needed to answer the question. But having thought about it a bit more
    (prompted by these last two posts), and tested the code (on the assumption >> that the gcc writers know the details better than I do), you are correct - >> I did misunderstand the question. I was wrong in how I thought array
    reference parameters worked in C++, and the way Ben worded the question
    re-enforced that misunderstanding.

    I'm sorry I wasn't more clear.

    I interpreted his question as saying that the code "f" does not show an
    array type being passed by reference, with the implication that the
    "sizeof" showed the size of a pointer, not the size of an array of 10 ints, >> and asking why C++ was defined that way. The answer, as I saw it, was that >> C++ made reference parameters to arrays work much like pointer parameters
    to arrays, and those work like in C for backwards compatibility.

    Ah. Then is was an answer to the question you thought I was asking.

    The trouble is, it never occurred to me you would not know that C++
    array references behave just like all other references -- as aliases to
    the objects they reference. So I was asking for some explanation of how
    you were using terms: "if this is 'passing an int', in what sense is this
    not 'passing an array'?".

    It had never occurred to me that my assumptions here were wrong, and it
    is not a feature I have ever needed in my C++ programming so I had
    simply not thought about it. Your posts, and Tim's follow-up, made me
    think and figure out what should have been obvious to me in the first
    place. (And that is better than if you had spotted that I was talking
    nonsense and corrected me directly. It is slightly less embarrassing
    when I spotted the mistake myself :-) )


    (I hope you don't think I'm being rude. C++ is a gruesomely huge
    language, littered with special cases and array reference must be almost never used, especially now. But I started with the first release of
    Cfront, so I learned all the primitive bits first. It's the new bits I
    can't fathom.)

    No, no, you are not being rude.


    Of course, it turns out I was completely wrong about how array type
    reference parameters work in C++. It's not something I have had use for in >> my own C++ programming or something I have come across in other code that I >> can remember, and I had made incorrect assumptions about it. Now that I
    corrected that, it all makes a lot more sense.

    And so I presume Ben was actually asking why I /thought/ this was not
    passing an array type (thus with its full type information, including its
    size). Then answer there is now obvious - I thought that because I had
    jumped to incorrect conclusions about array reference parameters in
    C++.

    Well, it was little more like I thought you were maybe using the term "passing by reference" is some way that I'd missed. That's why I
    started with a brief explanation of how I used to explain it.


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Ben Bacarisse on Tue Aug 20 12:42:54 2024
    On 20/08/2024 00:33, Ben Bacarisse wrote:
    Bart <bc@freeuk.com> writes:

    On 19/08/2024 02:30, Ben Bacarisse wrote:
    Bart <bc@freeuk.com> writes:

    On 19/08/2024 01:01, Ben Bacarisse wrote:
    Bart <bc@freeuk.com> writes:


    You or he would have to go into more detail, such as an actual example, to
    demonstrate whatever it is that you think is wrong about passing a pointer
    argument by-reference.
    No one has said any such thing, so I can't see how any more detail could >>>>> help. I suspect you've lost track of the point being made.

    Probably, and perhaps not just me! But I'd still quite like to know exactly
    what it is that is marked as 'disallowed'.
    I don't know how to explain it any better. If you really want to know,
    maybe you could say what /you/ think was incorrectly marked as
    disallowed so I can see how you were interpreting the table.

    I think I already said that several times!

    But I need to use concrete code, and there is no existing mainstream
    language with pass-by-reference that is simple enough to use for such
    examples. Except for mine and that is not acceptable here.

    So I'll use an extended C where a '&' in front of a parameter's base-type
    marks it as pass-by-reference.

    First an example written in standard C:

    #include <stdio.h>

    void F(int* p) {
    printf("%d\n", *p);
    ++p;
    }

    int main(void) {
    int A[] = {10,20,30};
    int* p = &A[0];

    F(p);
    printf("%d\n", *p);
    }

    This passes a normal pointer. The output is 10 10 from both printfs,
    because the ++p within F does not affect the original pointer in 'main'.

    Now the same program, but using pass-by-reference for 'p'; I won't show the >> whole thing, as the program looks exactly the same except for this line:

    void F(&int* p) {

    The output should now be 10 20 (as verified using my language; if
    interested, that is shown below, and below that is the standard C that
    might be generated by a transpiler from this fantasy C).

    So this is passing a pointer, by reference, and it is allowed!

    I can't imagine why you though this was what either Tim or I were
    talking about, but if that is genuinely what you thought he was saying
    was "disallowed" so be it. We could spend ages arguing about how you
    could possibly have thought this (especially as you wrote the original example), but nothing will be gained by doing that.

    Or maybe, just maybe, Tim said it for good reason.

    And yet, he continues to be cagey about it, and you're defending him. Just >> give me a freakin' example!

    WHAT EXACTLY IS IT THAT IS DISALLOWED?

    Good grief! You gave the example yourself! I'm not being cagey -- it
    never occurred to me that you could possibly be thinking about anything
    other the code you yourself posted and talked about!

    You: "I'm asking people to compare the first and third functions and
    their calls, and to see if there's any appreciable difference
    between them."

    Your first and third functions:

    int sum_byvalue(T* A, int n) {
    int i, sum=0;
    for (i=0; i<n; ++i) sum += A[i];
    return sum;
    }

    int sum_bytrueref(T (&A)[], int n) {
    int i, sum=0;
    for (i=0; i<n; ++i) sum += A[i];
    return sum;
    }

    and the caller uses:

    enum {N = 10};
    T A[N] = {10,20,30,40,50,60,70,80,90,100};
    ...
    total += sum_byvalue (A, N);
    ...
    total += sum_bytrueref(A, N)

    A post later and you used shorter names to show the similarities you
    wanted everyone to focus on -- the accesses in the functions. You used
    F for the function with a T* parameter and H for the function with a
    T(&)[10] parameter -- the true call-by-reference parameter that "sum_bytrueref" has a post earlier.

    Call Array access in callee
    (1) C call-by-value F(A) A[i]
    ...
    (3) true call-by-reference H(A) A[i]

    Tim just extended this by showing more call differences and more
    differences in access in the callee. The bit you so totally
    misunderstood was:

    C call-by-value call-by-reference
    =============== =================
    at call:
    (array argument) F(A) H(A)
    (pointer argument) F(p) (disallowed)
    (null argument) F(0) (disallowed)

    F (your name) can be called with an array argument (A) but not with a
    pointer argument (including a null pointer constant). Do you agree now
    that H(p) and H(0) are disallowed?

    How you thought that those last two rows of (this part of) the table
    could refer to a /different/ function (with a T*& parameter) -- a
    function that no one had posted up to that point -- is beyond me, but apparently you did. What's more you decided to put no effort in to try
    to work out what was being said. Instead you just said Tim was wrong
    and implied that I was defending the indefensible out of some sort of
    blind obedience.


    OK, thanks for trawling through the posts. I've just done the same
    exercise. So to summarise I posted 3 C functions (2 standard and one
    using a hypothetical feature), with names like 'sum_byvalue'.

    I later summarised the results in a table using names F, G, H (which I'd
    also used in a much earlier post but using my language).

    With the table however, I mentioned that it was about passing arrays.

    So I misunderstood Tim's table to be about passing those kinds of
    parameters: an Array, a Pointer, and Null pointer value, to functions
    that would take an Array or a Pointer respectively.

    So my mistake but it doesn't excuse not clarifying the point earlier
    with an example, such as:

    void H(&int X[]) {} // or int (&X)[], C-style

    int A[10];
    int i;

    H(A); // OK
    H(&i); // Not allowed
    H(NULL); // Not allowed

    (Verified using my language.)

    Those last two are not surprising, they're obviously the wrong type. But they're only relevant because using normal C, then &i is allowed (if you
    use T* rather than T(*)[]), and so is NULL, even using T(*)[].

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)