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.
*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.
If using pointers, one almost invariably needs to fall back
to doing "arr[y*N+x]"
Note that multidimensional indexing via multiple levels of pointer indirection would not be effected by this.
Similarly, structs may not be declared at the point of use, but only as types.
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).
If pulled of well, such a module system could be both faster and require
less memory use in the compiler if compared with headers
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.
Granted, even if parsing is fast, this still leaves the
challenge of fast/efficient machine-code generation.
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. ...
... So that causes awkwardness on
architectures like the PDP-10, for example.
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.
So that causes awkwardness on
architectures like the PDP-10, for example.
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.
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...
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.
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.
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.
It is a mystery why anyone uses LLVM, I would have considered it a dead
end due to all this...
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 ofI don’t think there is anything innate in the design of C to ensure
processor ...
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.
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:
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.
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:
That's what you want to force me to write, but I can use and array ofWhile eliminating structs could also simplify things; structsIndeed. And I'd have to use them for this!
also tend to
be a lot more useful.
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];
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, ...
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).
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).
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.
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...
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.
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).
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.
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.
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.
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);
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.
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 *.
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.
The main advantage however is not performance, but reducing the
severity of damage caused by buffer overrun bugs.
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.
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, ...
bart <bc@freeuk.com> writes:
On 09/07/2024 16:58, Ben Bacarisse wrote:
bart <bc@freeuk.com> writes:
Arrays are passed by reference:This is the sort of thing that bad tutors say to students so that they
void F(int a[20]) {}
int main(void) {
int x[20];
F(x);
}
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.
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.
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.
Therefore ABIs do not say anything about array parameters,
(or if they do, it's not in relation to C).
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.
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".
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.
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
'Subscripting' I think we can agree is the same thing as 'indexing':
what those funny square brackets do.
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?
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.
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:On the language level, that's passing a pointer to an array object.
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);
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,
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.
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.
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:An address value is passed by value. C has only one parameter passing
bart <bc@freeuk.com> writes:
Arrays are passed by reference: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.
void F(int a[20]) {}
int main(void) {
int x[20];
F(x);
}
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.
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.
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!
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:Parameter passing is relatively simple though since there is only one
bart <bc@freeuk.com> writes:
On 09/07/2024 16:58, Ben Bacarisse wrote: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
bart <bc@freeuk.com> writes:
Arrays are passed by reference: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
void F(int a[20]) {}
int main(void) {
int x[20];
F(x);
}
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.
passing is simple to understand, provided learner tune out voices like >>>>> yours.
Little about C's type system is simple.
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
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
"So if arrays aren't passed by value in C, and they aren't passed by reference, then how the hell ARE they passed?!"
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,
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.
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.
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.
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.
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!
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").
Part of the compiler writing fun is
trying to get good performance while also not breaking C semantics.
But, a language more aggressively tuned for performance would have even
more wonky semantic edge cases than it does already.
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".
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.
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.
Which is more unsafe out these: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.
... For functions, it happens in
all contexts except function call. Or, may be, including function call,
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.
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.
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.
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.
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.
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.
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.
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.)
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.
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.
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.
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.
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.
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.
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!
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,
and the rules are not hard to understand.
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).
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.
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.
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?
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.
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".
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.
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?
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.
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?
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.
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.
and the rules are not hard to understand.
Ha ha ha! Of course you would say that.
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[]);
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".
This has been explained (also with references to original
sources) to you many times.
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.
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.
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.
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.
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.
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.
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.
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.
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.
But, as I see it, no real point in arguing this stuff (personally, I
have better stuff to be doing...).
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).
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.
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,
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.
The proper comparison would be vs language that has arrays as 1st class citizen.
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.
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.
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,
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.
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)
(null argument) F(0) (disallowed)
inside function:
(access) A[i] A[i]
(update) A[i] = ... A[i] = ...
sizeof A (pointer size) (array size)
A++ (changes A variable) (disallowed)
A = (new value) (changes A variable) (disallowed)
The more complete comparion illustrate why C semantics should not
be thought of as call-by-reference.
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.
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.
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.
(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.
since it effectively applies & to the argument.
No. Passing a reference is /not/ like using & to pass a pointer.
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.
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
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.
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.
. In C, there is no "pass by reference" or "return by reference". Itis 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].
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.
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.
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.
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.
I'm going to show a demo in my language because
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
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
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:int(*)[20].
; 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
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.
4. Adding "pass by reference" and "arrays as first class objects" would
both be very significant changes to C
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:
Which is why, presumably, you didn't show the differences. YourC 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.
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.
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.
C call-by-value call-by-reference
=============== =================
(pointer argument) F(p) (disallowed)
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++.
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);
}
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.
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?
If you don't look for
alternative interpretations you won't ever find any.
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.
That's a lot of the reason people dismiss your comments.
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.
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.
It would be wrong (and is wrong in
C++) to pass a pointer where an array reference is expected.
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.
Or maybe, just maybe, Tim said it for good reason.
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:
My guess is he didn't understand the question. The code shownIn 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...
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'?".
(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.)
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.
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:I don't know how to explain it any better. If you really want to know,
Bart <bc@freeuk.com> writes:
You or he would have to go into more detail, such as an actual example, toNo 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.
demonstrate whatever it is that you think is wrong about passing a pointer
argument by-reference.
Probably, and perhaps not just me! But I'd still quite like to know exactly
what it is that is marked as 'disallowed'.
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.
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 546 |
Nodes: | 16 (2 / 14) |
Uptime: | 154:11:09 |
Calls: | 10,383 |
Files: | 14,054 |
Messages: | 6,417,843 |